]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/nand/nandsim_swap.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / nand / nandsim_swap.c
1 /*-
2  * Copyright (C) 2009-2012 Semihalf
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/systm.h>
33 #include <sys/malloc.h>
34 #include <sys/queue.h>
35 #include <sys/fcntl.h>
36 #include <sys/proc.h>
37 #include <sys/namei.h>
38 #include <sys/lock.h>
39 #include <sys/vnode.h>
40 #include <sys/mount.h>
41
42 #include <dev/nand/nandsim_chip.h>
43 #include <dev/nand/nandsim_swap.h>
44
45 static int  init_block_state(struct chip_swap *);
46 static void destroy_block_state(struct chip_swap *);
47
48 static int  create_buffers(struct chip_swap *);
49 static void destroy_buffers(struct chip_swap *);
50
51 static int  swap_file_open(struct chip_swap *, const char *);
52 static void swap_file_close(struct chip_swap *);
53 static int  swap_file_write(struct chip_swap *, struct block_state *);
54 static int  swap_file_read(struct chip_swap *, struct block_state *);
55
56 #define CHIP_SWAP_CMODE         0600
57 #define CHIP_SWAP_BLOCKSPACES   2
58
59 static int
60 init_block_state(struct chip_swap *swap)
61 {
62         struct block_state *blk_state;
63         int i;
64
65         if (swap == NULL)
66                 return (-1);
67
68         blk_state = malloc(swap->nof_blks * sizeof(struct block_state),
69             M_NANDSIM, M_WAITOK | M_ZERO);
70
71         for (i = 0; i < swap->nof_blks; i++)
72                 blk_state[i].offset = 0xffffffff;
73
74         swap->blk_state = blk_state;
75
76         return (0);
77 }
78
79 static void
80 destroy_block_state(struct chip_swap *swap)
81 {
82
83         if (swap == NULL)
84                 return;
85
86         if (swap->blk_state != NULL)
87                 free(swap->blk_state, M_NANDSIM);
88 }
89
90 static int
91 create_buffers(struct chip_swap *swap)
92 {
93         struct block_space *block_space;
94         void *block;
95         int i;
96
97         for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) {
98                 block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK);
99                 block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK);
100                 block_space->blk_ptr = block;
101                 SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link);
102                 nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space,
103                     block);
104         }
105
106         if (i == 0)
107                 return (-1);
108
109         return (0);
110 }
111
112 static void
113 destroy_buffers(struct chip_swap *swap)
114 {
115         struct block_space *blk_space;
116
117         if (swap == NULL)
118                 return;
119
120         blk_space = SLIST_FIRST(&swap->free_bs);
121         while (blk_space) {
122                 SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
123                 nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
124                     blk_space, blk_space->blk_ptr);
125                 free(blk_space->blk_ptr, M_NANDSIM);
126                 free(blk_space, M_NANDSIM);
127                 blk_space = SLIST_FIRST(&swap->free_bs);
128         }
129
130         blk_space = STAILQ_FIRST(&swap->used_bs);
131         while (blk_space) {
132                 STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
133                 nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
134                     blk_space, blk_space->blk_ptr);
135                 free(blk_space->blk_ptr, M_NANDSIM);
136                 free(blk_space, M_NANDSIM);
137                 blk_space = STAILQ_FIRST(&swap->used_bs);
138         }
139 }
140
141 static int
142 swap_file_open(struct chip_swap *swap, const char *swap_file)
143 {
144         struct nameidata nd;
145         int flags, error;
146
147         NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, swap_file,
148             curthread);
149
150         flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC;
151
152         error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL);
153         if (error) {
154                 nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file);
155                 NDFREE(&nd, NDF_ONLY_PNBUF);
156                 return (error);
157         }
158
159         swap->swap_cred = crhold(curthread->td_ucred);
160         NDFREE(&nd, NDF_ONLY_PNBUF);
161
162         /* We just unlock so we hold a reference */
163         VOP_UNLOCK(nd.ni_vp, 0);
164
165         swap->swap_vp = nd.ni_vp;
166
167         return (0);
168 }
169
170 static void
171 swap_file_close(struct chip_swap *swap)
172 {
173
174         if (swap == NULL)
175                 return;
176
177         if (swap->swap_vp == NULL)
178                 return;
179
180         vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread);
181         crfree(swap->swap_cred);
182 }
183
184 static int
185 swap_file_write(struct chip_swap *swap, struct block_state *blk_state)
186 {
187         struct block_space *blk_space;
188         struct thread *td;
189         struct mount *mp;
190         struct vnode *vp;
191         struct uio auio;
192         struct iovec aiov;
193
194         if (swap == NULL || blk_state == NULL)
195                 return (-1);
196
197         blk_space = blk_state->blk_sp;
198         if (blk_state->offset == -1) {
199                 blk_state->offset = swap->swap_offset;
200                 swap->swap_offset += swap->blk_size;
201         }
202
203         nand_debug(NDBG_SIM,"saving %p[%p] at %x\n",
204             blk_space, blk_space->blk_ptr, blk_state->offset);
205
206         bzero(&aiov, sizeof(aiov));
207         bzero(&auio, sizeof(auio));
208
209         aiov.iov_base = blk_space->blk_ptr;
210         aiov.iov_len = swap->blk_size;
211         td = curthread;
212         vp = swap->swap_vp;
213
214         auio.uio_iov = &aiov;
215         auio.uio_offset = blk_state->offset;
216         auio.uio_segflg = UIO_SYSSPACE;
217         auio.uio_rw = UIO_WRITE;
218         auio.uio_iovcnt = 1;
219         auio.uio_resid = swap->blk_size;
220         auio.uio_td = td;
221
222         vn_start_write(vp, &mp, V_WAIT);
223         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
224         VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred);
225         VOP_UNLOCK(vp, 0);
226         vn_finished_write(mp);
227
228         return (0);
229 }
230
231 static int
232 swap_file_read(struct chip_swap *swap, struct block_state *blk_state)
233 {
234         struct block_space *blk_space;
235         struct thread *td;
236         struct vnode *vp;
237         struct uio auio;
238         struct iovec aiov;
239
240         if (swap == NULL || blk_state == NULL)
241                 return (-1);
242
243         blk_space = blk_state->blk_sp;
244
245         nand_debug(NDBG_SIM,"restore %p[%p] at %x\n",
246             blk_space, blk_space->blk_ptr, blk_state->offset);
247
248         bzero(&aiov, sizeof(aiov));
249         bzero(&auio, sizeof(auio));
250
251         aiov.iov_base = blk_space->blk_ptr;
252         aiov.iov_len = swap->blk_size;
253         td = curthread;
254         vp = swap->swap_vp;
255
256         auio.uio_iov = &aiov;
257         auio.uio_offset = blk_state->offset;
258         auio.uio_segflg = UIO_SYSSPACE;
259         auio.uio_rw = UIO_READ;
260         auio.uio_iovcnt = 1;
261         auio.uio_resid = swap->blk_size;
262         auio.uio_td = td;
263
264         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
265         VOP_READ(vp, &auio, 0, swap->swap_cred);
266         VOP_UNLOCK(vp, 0);
267
268         return (0);
269 }
270
271 struct chip_swap *
272 nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size)
273 {
274         struct chip_swap *swap;
275         int err = 0;
276
277         if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0))
278                 return (NULL);
279
280         swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO);
281
282         SLIST_INIT(&swap->free_bs);
283         STAILQ_INIT(&swap->used_bs);
284         swap->blk_size = blk_size;
285         swap->nof_blks = nof_blks;
286
287         err = init_block_state(swap);
288         if (err) {
289                 nandsim_swap_destroy(swap);
290                 return (NULL);
291         }
292
293         err = create_buffers(swap);
294         if (err) {
295                 nandsim_swap_destroy(swap);
296                 return (NULL);
297         }
298
299         err = swap_file_open(swap, swap_file);
300         if (err) {
301                 nandsim_swap_destroy(swap);
302                 return (NULL);
303         }
304
305         return (swap);
306 }
307
308 void
309 nandsim_swap_destroy(struct chip_swap *swap)
310 {
311
312         if (swap == NULL)
313                 return;
314
315         destroy_block_state(swap);
316         destroy_buffers(swap);
317         swap_file_close(swap);
318         free(swap, M_NANDSIM);
319 }
320
321 struct block_space *
322 get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing)
323 {
324         struct block_state *blk_state, *old_blk_state = NULL;
325         struct block_space *blk_space;
326
327         if (swap == NULL || (block >= swap->nof_blks))
328                 return (NULL);
329
330         blk_state = &swap->blk_state[block];
331         nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status);
332
333         if (blk_state->status & BLOCK_ALLOCATED) {
334                 blk_space = blk_state->blk_sp;
335         } else {
336                 blk_space = SLIST_FIRST(&swap->free_bs);
337                 if (blk_space) {
338                         SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
339                         STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
340                             used_link);
341                 } else {
342                         blk_space = STAILQ_FIRST(&swap->used_bs);
343                         old_blk_state = blk_space->blk_state;
344                         STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
345                         STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
346                             used_link);
347                         if (old_blk_state->status & BLOCK_DIRTY) {
348                                 swap_file_write(swap, old_blk_state);
349                                 old_blk_state->status &= ~BLOCK_DIRTY;
350                                 old_blk_state->status |= BLOCK_SWAPPED;
351                         }
352                 }
353         }
354
355         if (blk_space == NULL)
356                 return (NULL);
357
358         if (old_blk_state != NULL) {
359                 old_blk_state->status &= ~BLOCK_ALLOCATED;
360                 old_blk_state->blk_sp = NULL;
361         }
362
363         blk_state->blk_sp = blk_space;
364         blk_space->blk_state = blk_state;
365
366         if (!(blk_state->status & BLOCK_ALLOCATED)) {
367                 if (blk_state->status & BLOCK_SWAPPED)
368                         swap_file_read(swap, blk_state);
369                 else
370                         memset(blk_space->blk_ptr, 0xff, swap->blk_size);
371                 blk_state->status |= BLOCK_ALLOCATED;
372         }
373
374         if (writing)
375                 blk_state->status |= BLOCK_DIRTY;
376
377         nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space,
378             blk_space->blk_ptr, blk_state->status);
379
380         return (blk_space);
381 }