]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/fs/nandfs/nandfs_sufile.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / fs / nandfs / nandfs_sufile.c
1 /*-
2  * Copyright (c) 2010-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/systm.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/mount.h>
37 #include <sys/mutex.h>
38 #include <sys/namei.h>
39 #include <sys/sysctl.h>
40 #include <sys/vnode.h>
41 #include <sys/buf.h>
42 #include <sys/bio.h>
43
44 #include <vm/vm.h>
45 #include <vm/vm_param.h>
46 #include <vm/vm_kern.h>
47 #include <vm/vm_page.h>
48
49 #include <geom/geom.h>
50 #include <geom/geom_vfs.h>
51
52 #include <fs/nandfs/nandfs_mount.h>
53 #include <fs/nandfs/nandfs.h>
54 #include <fs/nandfs/nandfs_subr.h>
55
56 #define SU_USAGE_OFF(bp, offset) \
57         ((struct nandfs_segment_usage *)((bp)->b_data + offset))
58
59 static int
60 nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
61     uint64_t *blk, uint64_t *offset)
62 {
63         uint64_t off;
64         uint16_t seg_size;
65
66         seg_size = fsdev->nd_fsdata.f_segment_usage_size;
67
68         off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
69         off += (seg * seg_size);
70
71         *blk = off / fsdev->nd_blocksize;
72         *offset = off % fsdev->nd_blocksize;
73         return (0);
74 }
75
76 /* Alloc new segment */
77 int
78 nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
79 {
80         struct nandfs_node *su_node;
81         struct nandfs_sufile_header *su_header;
82         struct nandfs_segment_usage *su_usage;
83         struct buf *bp_header, *bp;
84         uint64_t blk, vblk, offset, i, rest, nsegments;
85         uint16_t seg_size;
86         int error, found;
87
88         seg_size = fsdev->nd_fsdata.f_segment_usage_size;
89         nsegments = fsdev->nd_fsdata.f_nsegments;
90
91         su_node = fsdev->nd_su_node;
92         ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
93
94         /* Read header buffer */
95         error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
96         if (error) {
97                 brelse(bp_header);
98                 return (error);
99         }
100
101         su_header = (struct nandfs_sufile_header *)bp_header->b_data;
102
103         /* Get last allocated segment */
104         i = su_header->sh_last_alloc + 1;
105
106         found = 0;
107         bp = NULL;
108         while (!found) {
109                 nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
110                 if(blk != 0) {
111                         error = nandfs_bmap_lookup(su_node, blk, &vblk);
112                         if (error) {
113                                 nandfs_error("%s: cannot find vblk for blk "
114                                     "blk:%jx\n", __func__, blk);
115                                 return (error);
116                         }
117                         if (vblk)
118                                 error = nandfs_bread(su_node, blk, NOCRED, 0,
119                                     &bp);
120                         else
121                                 error = nandfs_bcreate(su_node, blk, NOCRED, 0,
122                                     &bp);
123                         if (error) {
124                                 nandfs_error("%s: cannot create/read "
125                                     "vblk:%jx\n", __func__, vblk);
126                                 if (bp)
127                                         brelse(bp);
128                                 return (error);
129                         }
130
131                         su_usage = SU_USAGE_OFF(bp, offset);
132                 } else {
133                         su_usage = SU_USAGE_OFF(bp_header, offset);
134                         bp = bp_header;
135                 }
136
137                 rest = (fsdev->nd_blocksize - offset) / seg_size;
138                 /* Go through all su usage in block */
139                 while (rest) {
140                         /* When last check start from beggining */
141                         if (i == nsegments)
142                                 break;
143
144                         if (!su_usage->su_flags) {
145                                 su_usage->su_flags = 1;
146                                 found = 1;
147                                 break;
148                         }
149                         su_usage++;
150                         i++;
151
152                         /* If all checked return error */
153                         if (i == su_header->sh_last_alloc) {
154                                 DPRINTF(SEG, ("%s: cannot allocate segment \n",
155                                     __func__));
156                                 brelse(bp_header);
157                                 if (blk != 0)
158                                         brelse(bp);
159                                 return (1);
160                         }
161                         rest--;
162                 }
163                 if (!found) {
164                         /* Otherwise read another block */
165                         if (blk != 0)
166                                 brelse(bp);
167                         if (i == nsegments) {
168                                 blk = 0;
169                                 i = 0;
170                         } else
171                                 blk++;
172                         offset = 0;
173                 }
174         }
175
176         if (found) {
177                 *seg = i;
178                 su_header->sh_last_alloc = i;
179                 su_header->sh_ncleansegs--;
180                 su_header->sh_ndirtysegs++;
181
182                 fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
183                     fsdev->nd_fsdata.f_blocks_per_segment;
184                 fsdev->nd_clean_segs--;
185
186                 /*
187                  * It is mostly called from syncer() so we want to force
188                  * making buf dirty.
189                  */
190                 error = nandfs_dirty_buf(bp_header, 1);
191                 if (error) {
192                         if (bp && bp != bp_header)
193                                 brelse(bp);
194                         return (error);
195                 }
196                 if (bp && bp != bp_header)
197                         nandfs_dirty_buf(bp, 1);
198
199                 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
200
201                 return (0);
202         }
203
204         DPRINTF(SEG, ("%s: failed\n", __func__));
205
206         return (1);
207 }
208
209 /*
210  * Make buffer dirty, it will be updated soon but first it need to be
211  * gathered by syncer.
212  */
213 int
214 nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
215 {
216         struct nandfs_node *su_node;
217         struct buf *bp;
218         uint64_t blk, offset;
219         int error;
220
221         su_node = fsdev->nd_su_node;
222         ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
223
224         nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
225
226         error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
227         if (error) {
228                 brelse(bp);
229                 nandfs_error("%s: cannot preallocate new segment\n", __func__);
230                 return (error);
231         } else
232                 nandfs_dirty_buf(bp, 1);
233
234         DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
235         return (error);
236 }
237
238 /* Update block count of segment */
239 int
240 nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
241 {
242         struct nandfs_node *su_node;
243         struct nandfs_segment_usage *su_usage;
244         struct buf *bp;
245         uint64_t blk, offset;
246         int error;
247
248         su_node = fsdev->nd_su_node;
249         ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
250
251         nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
252
253         error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
254         if (error) {
255                 nandfs_error("%s: read block:%jx to update\n",
256                     __func__, blk);
257                 brelse(bp);
258                 return (error);
259         }
260
261         su_usage = SU_USAGE_OFF(bp, offset);
262         su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
263         su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
264         su_usage->su_nblocks += nblks;
265
266         DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n",  __func__,
267             (uintmax_t)seg, nblks, su_usage->su_nblocks));
268
269         nandfs_dirty_buf(bp, 1);
270
271         return (0);
272 }
273
274 /* Make segment free */
275 int
276 nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
277 {
278         struct nandfs_node *su_node;
279         struct nandfs_sufile_header *su_header;
280         struct nandfs_segment_usage *su_usage;
281         struct buf *bp_header, *bp;
282         uint64_t blk, offset;
283         int error;
284
285         su_node = fsdev->nd_su_node;
286         ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
287
288         /* Read su header */
289         error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
290         if (error) {
291                 brelse(bp_header);
292                 return (error);
293         }
294
295         su_header = (struct nandfs_sufile_header *)bp_header->b_data;
296         nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
297
298         /* Read su usage block if other than su header block */
299         if (blk != 0) {
300                 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
301                 if (error) {
302                         brelse(bp);
303                         brelse(bp_header);
304                         return (error);
305                 }
306         } else
307                 bp = bp_header;
308
309         /* Reset su usage data */
310         su_usage = SU_USAGE_OFF(bp, offset);
311         su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
312         su_usage->su_nblocks = 0;
313         su_usage->su_flags = 0;
314
315         /* Update clean/dirty counter in header */
316         su_header->sh_ncleansegs++;
317         su_header->sh_ndirtysegs--;
318
319         /*
320          *  Make buffers dirty, called by cleaner
321          *  so force dirty even if no much space left
322          *  on device
323          */
324         nandfs_dirty_buf(bp_header, 1);
325         if (bp != bp_header)
326                 nandfs_dirty_buf(bp, 1);
327
328         /* Update free block count */
329         fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
330             fsdev->nd_fsdata.f_blocks_per_segment;
331         fsdev->nd_clean_segs++;
332
333         DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
334
335         return (0);
336 }
337
338 static int
339 nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
340 {
341         struct nandfs_node *su_node;
342         struct nandfs_segment_usage *su_usage;
343         struct buf *bp;
344         uint64_t blk, offset;
345         int error;
346
347         su_node = fsdev->nd_su_node;
348         ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
349
350         nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
351
352         error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
353         if (error) {
354                 brelse(bp);
355                 return (error);
356         }
357
358         su_usage = SU_USAGE_OFF(bp, offset);
359         su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
360         su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR;
361
362         DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
363
364         nandfs_dirty_buf(bp, 1);
365
366         return (0);
367 }
368
369 int
370 nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
371 {
372         struct nandfs_node *su_node;
373         struct nandfs_segment_usage *su_usage;
374         struct buf *bp;
375         uint64_t blk, offset;
376         int error;
377
378         su_node = fsdev->nd_su_node;
379
380         VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
381
382         nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
383
384         error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
385         if (error) {
386                 brelse(bp);
387                 VOP_UNLOCK(NTOV(su_node), 0);
388                 return (error);
389         }
390
391         su_usage = SU_USAGE_OFF(bp, offset);
392         MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0);
393         su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC;
394
395         brelse(bp);
396         VOP_UNLOCK(NTOV(su_node), 0);
397
398         DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
399
400         return (0);
401 }
402
403 int
404 nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
405 {
406         uint64_t offset, segsize;
407         uint32_t bps, bsize;
408         int error = 0;
409
410         bps = fsdev->nd_fsdata.f_blocks_per_segment;
411         bsize = fsdev->nd_blocksize;
412         segsize = bsize * bps;
413         nandfs_get_segment_range(fsdev, seg, &offset, NULL);
414         offset *= bsize;
415
416         DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
417
418         /* Erase it and mark it bad when fail */
419         if (nandfs_erase(fsdev, offset, segsize))
420                 error = nandfs_bad_segment(fsdev, seg);
421
422         if (error)
423                 return (error);
424
425         /* Mark it free */
426         error = nandfs_free_segment(fsdev, seg);
427
428         return (error);
429 }
430
431 int
432 nandfs_get_seg_stat(struct nandfs_device *nandfsdev,
433     struct nandfs_seg_stat *nss)
434 {
435         struct nandfs_sufile_header *suhdr;
436         struct nandfs_node *su_node;
437         struct buf *bp;
438         int err;
439
440         su_node = nandfsdev->nd_su_node;
441
442         NANDFS_WRITELOCK(nandfsdev);
443         VOP_LOCK(NTOV(su_node), LK_SHARED);
444         err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
445         if (err) {
446                 brelse(bp);
447                 VOP_UNLOCK(NTOV(su_node), 0);
448                 NANDFS_WRITEUNLOCK(nandfsdev);
449                 return (-1);
450         }
451
452         suhdr = (struct nandfs_sufile_header *)bp->b_data;
453         nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments;
454         nss->nss_ncleansegs = suhdr->sh_ncleansegs;
455         nss->nss_ndirtysegs = suhdr->sh_ndirtysegs;
456         nss->nss_ctime = 0;
457         nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
458         nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
459
460         brelse(bp);
461         VOP_UNLOCK(NTOV(su_node), 0);
462
463         NANDFS_WRITEUNLOCK(nandfsdev);
464
465         return (0);
466 }
467
468 int
469 nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
470     struct nandfs_argv *nargv)
471 {
472         struct nandfs_suinfo *nsi;
473         int error;
474
475         if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
476                 return (EINVAL);
477
478         nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
479             M_NANDFSTEMP, M_WAITOK | M_ZERO);
480
481         error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
482             nargv->nv_index);
483
484         if (error == 0)
485                 error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
486                     sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
487
488         free(nsi, M_NANDFSTEMP);
489         return (error);
490 }
491
492 int
493 nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
494     uint32_t nmembs, uint64_t segment)
495 {
496
497         return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
498             NULL, 0, 0));
499 }
500
501 int
502 nandfs_get_segment_info_filter(struct nandfs_device *fsdev,
503     struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment,
504     uint64_t *nsegs, uint32_t filter, uint32_t nfilter)
505 {
506         struct nandfs_segment_usage *su;
507         struct nandfs_node *su_node;
508         struct buf *bp;
509         uint64_t curr, blocknr, blockoff, i;
510         uint32_t flags;
511         int err = 0;
512
513         curr = ~(0);
514
515         lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
516         su_node = fsdev->nd_su_node;
517
518         VOP_LOCK(NTOV(su_node), LK_SHARED);
519
520         bp = NULL;
521         if (nsegs !=  NULL)
522                 *nsegs = 0;
523         for (i = 0; i < nmembs; segment++) {
524                 if (segment == fsdev->nd_fsdata.f_nsegments)
525                         break;
526
527                 nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
528                     &blockoff);
529
530                 if (i == 0 || curr != blocknr) {
531                         if (bp != NULL)
532                                 brelse(bp);
533                         err = nandfs_bread(su_node, blocknr, NOCRED,
534                             0, &bp);
535                         if (err) {
536                                 goto out;
537                         }
538                         curr = blocknr;
539                 }
540
541                 su = SU_USAGE_OFF(bp, blockoff);
542                 flags = su->su_flags;
543                 if (segment == fsdev->nd_seg_num ||
544                     segment == fsdev->nd_next_seg_num)
545                         flags |= NANDFS_SEGMENT_USAGE_ACTIVE;
546
547                 if (nfilter != 0 && (flags & nfilter) != 0)
548                         continue;
549                 if (filter != 0 && (flags & filter) == 0)
550                         continue;
551
552                 nsi->nsi_num = segment;
553                 nsi->nsi_lastmod = su->su_lastmod;
554                 nsi->nsi_blocks = su->su_nblocks;
555                 nsi->nsi_flags = flags;
556                 nsi++;
557                 i++;
558                 if (nsegs != NULL)
559                         (*nsegs)++;
560         }
561
562 out:
563         if (bp != NULL)
564                 brelse(bp);
565         VOP_UNLOCK(NTOV(su_node), 0);
566         lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
567
568         return (err);
569 }