2 * Copyright (c) 2010-2012 Semihalf.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/systm.h>
33 #include <sys/kernel.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>
45 #include <vm/vm_param.h>
46 #include <vm/vm_kern.h>
47 #include <vm/vm_page.h>
49 #include <geom/geom.h>
50 #include <geom/geom_vfs.h>
52 #include <fs/nandfs/nandfs_mount.h>
53 #include <fs/nandfs/nandfs.h>
54 #include <fs/nandfs/nandfs_subr.h>
56 #define SU_USAGE_OFF(bp, offset) \
57 ((struct nandfs_segment_usage *)((bp)->b_data + offset))
60 nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
61 uint64_t *blk, uint64_t *offset)
66 seg_size = fsdev->nd_fsdata.f_segment_usage_size;
68 off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
69 off += (seg * seg_size);
71 *blk = off / fsdev->nd_blocksize;
72 *offset = off % fsdev->nd_blocksize;
76 /* Alloc new segment */
78 nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
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;
88 seg_size = fsdev->nd_fsdata.f_segment_usage_size;
89 nsegments = fsdev->nd_fsdata.f_nsegments;
91 su_node = fsdev->nd_su_node;
92 ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
94 /* Read header buffer */
95 error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
101 su_header = (struct nandfs_sufile_header *)bp_header->b_data;
103 /* Get last allocated segment */
104 i = su_header->sh_last_alloc + 1;
109 nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
111 error = nandfs_bmap_lookup(su_node, blk, &vblk);
113 nandfs_error("%s: cannot find vblk for blk "
114 "blk:%jx\n", __func__, blk);
118 error = nandfs_bread(su_node, blk, NOCRED, 0,
121 error = nandfs_bcreate(su_node, blk, NOCRED, 0,
124 nandfs_error("%s: cannot create/read "
125 "vblk:%jx\n", __func__, vblk);
131 su_usage = SU_USAGE_OFF(bp, offset);
133 su_usage = SU_USAGE_OFF(bp_header, offset);
137 rest = (fsdev->nd_blocksize - offset) / seg_size;
138 /* Go through all su usage in block */
140 /* When last check start from beggining */
144 if (!su_usage->su_flags) {
145 su_usage->su_flags = 1;
152 /* If all checked return error */
153 if (i == su_header->sh_last_alloc) {
154 DPRINTF(SEG, ("%s: cannot allocate segment \n",
164 /* Otherwise read another block */
167 if (i == nsegments) {
178 su_header->sh_last_alloc = i;
179 su_header->sh_ncleansegs--;
180 su_header->sh_ndirtysegs++;
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--;
187 * It is mostly called from syncer() so we want to force
190 error = nandfs_dirty_buf(bp_header, 1);
192 if (bp && bp != bp_header)
196 if (bp && bp != bp_header)
197 nandfs_dirty_buf(bp, 1);
199 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
204 DPRINTF(SEG, ("%s: failed\n", __func__));
210 * Make buffer dirty, it will be updated soon but first it need to be
211 * gathered by syncer.
214 nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
216 struct nandfs_node *su_node;
218 uint64_t blk, offset;
221 su_node = fsdev->nd_su_node;
222 ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
224 nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
226 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
229 nandfs_error("%s: cannot preallocate new segment\n", __func__);
232 nandfs_dirty_buf(bp, 1);
234 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
238 /* Update block count of segment */
240 nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
242 struct nandfs_node *su_node;
243 struct nandfs_segment_usage *su_usage;
245 uint64_t blk, offset;
248 su_node = fsdev->nd_su_node;
249 ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
251 nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
253 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
255 nandfs_error("%s: read block:%jx to update\n",
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;
266 DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n", __func__,
267 (uintmax_t)seg, nblks, su_usage->su_nblocks));
269 nandfs_dirty_buf(bp, 1);
274 /* Make segment free */
276 nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
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;
285 su_node = fsdev->nd_su_node;
286 ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
289 error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
295 su_header = (struct nandfs_sufile_header *)bp_header->b_data;
296 nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
298 /* Read su usage block if other than su header block */
300 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
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;
315 /* Update clean/dirty counter in header */
316 su_header->sh_ncleansegs++;
317 su_header->sh_ndirtysegs--;
320 * Make buffers dirty, called by cleaner
321 * so force dirty even if no much space left
324 nandfs_dirty_buf(bp_header, 1);
326 nandfs_dirty_buf(bp, 1);
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++;
333 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
339 nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
341 struct nandfs_node *su_node;
342 struct nandfs_segment_usage *su_usage;
344 uint64_t blk, offset;
347 su_node = fsdev->nd_su_node;
348 ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
350 nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
352 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
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;
362 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
364 nandfs_dirty_buf(bp, 1);
370 nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
372 struct nandfs_node *su_node;
373 struct nandfs_segment_usage *su_usage;
375 uint64_t blk, offset;
378 su_node = fsdev->nd_su_node;
380 VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
382 nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
384 error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
387 VOP_UNLOCK(NTOV(su_node), 0);
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;
396 VOP_UNLOCK(NTOV(su_node), 0);
398 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
404 nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
406 uint64_t offset, segsize;
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);
416 DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
418 /* Erase it and mark it bad when fail */
419 if (nandfs_erase(fsdev, offset, segsize))
420 error = nandfs_bad_segment(fsdev, seg);
426 error = nandfs_free_segment(fsdev, seg);
432 nandfs_get_seg_stat(struct nandfs_device *nandfsdev,
433 struct nandfs_seg_stat *nss)
435 struct nandfs_sufile_header *suhdr;
436 struct nandfs_node *su_node;
440 su_node = nandfsdev->nd_su_node;
442 NANDFS_WRITELOCK(nandfsdev);
443 VOP_LOCK(NTOV(su_node), LK_SHARED);
444 err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
447 VOP_UNLOCK(NTOV(su_node), 0);
448 NANDFS_WRITEUNLOCK(nandfsdev);
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;
457 nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
458 nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
461 VOP_UNLOCK(NTOV(su_node), 0);
463 NANDFS_WRITEUNLOCK(nandfsdev);
469 nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
470 struct nandfs_argv *nargv)
472 struct nandfs_suinfo *nsi;
475 if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
478 nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
479 M_NANDFSTEMP, M_WAITOK | M_ZERO);
481 error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
485 error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
486 sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
488 free(nsi, M_NANDFSTEMP);
493 nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
494 uint32_t nmembs, uint64_t segment)
497 return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
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)
506 struct nandfs_segment_usage *su;
507 struct nandfs_node *su_node;
509 uint64_t curr, blocknr, blockoff, i;
515 lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
516 su_node = fsdev->nd_su_node;
518 VOP_LOCK(NTOV(su_node), LK_SHARED);
523 for (i = 0; i < nmembs; segment++) {
524 if (segment == fsdev->nd_fsdata.f_nsegments)
527 nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
530 if (i == 0 || curr != blocknr) {
533 err = nandfs_bread(su_node, blocknr, NOCRED,
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;
547 if (nfilter != 0 && (flags & nfilter) != 0)
549 if (filter != 0 && (flags & filter) == 0)
552 nsi->nsi_num = segment;
553 nsi->nsi_lastmod = su->su_lastmod;
554 nsi->nsi_blocks = su->su_nblocks;
555 nsi->nsi_flags = flags;
565 VOP_UNLOCK(NTOV(su_node), 0);
566 lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);