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