1235537Sgber/*-
2235537Sgber * Copyright (c) 2010-2012 Semihalf.
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber *
14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235537Sgber * SUCH DAMAGE.
25235537Sgber */
26235537Sgber
27235537Sgber#include <sys/cdefs.h>
28235537Sgber__FBSDID("$FreeBSD$");
29235537Sgber
30235537Sgber#include <sys/param.h>
31235537Sgber#include <sys/systm.h>
32235537Sgber#include <sys/conf.h>
33235537Sgber#include <sys/kernel.h>
34235537Sgber#include <sys/lock.h>
35235537Sgber#include <sys/malloc.h>
36235537Sgber#include <sys/mount.h>
37235537Sgber#include <sys/mutex.h>
38235537Sgber#include <sys/namei.h>
39235537Sgber#include <sys/sysctl.h>
40235537Sgber#include <sys/vnode.h>
41235537Sgber#include <sys/buf.h>
42235537Sgber#include <sys/bio.h>
43235537Sgber
44235537Sgber#include <vm/vm.h>
45235537Sgber#include <vm/vm_param.h>
46235537Sgber#include <vm/vm_kern.h>
47235537Sgber#include <vm/vm_page.h>
48235537Sgber
49235537Sgber#include <geom/geom.h>
50235537Sgber#include <geom/geom_vfs.h>
51235537Sgber
52235537Sgber#include <fs/nandfs/nandfs_mount.h>
53235537Sgber#include <fs/nandfs/nandfs.h>
54235537Sgber#include <fs/nandfs/nandfs_subr.h>
55235537Sgber
56235537Sgber#define	SU_USAGE_OFF(bp, offset) \
57235537Sgber	((struct nandfs_segment_usage *)((bp)->b_data + offset))
58235537Sgber
59235537Sgberstatic int
60235537Sgbernandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
61235537Sgber    uint64_t *blk, uint64_t *offset)
62235537Sgber{
63235537Sgber	uint64_t off;
64235537Sgber	uint16_t seg_size;
65235537Sgber
66235537Sgber	seg_size = fsdev->nd_fsdata.f_segment_usage_size;
67235537Sgber
68235537Sgber	off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
69235537Sgber	off += (seg * seg_size);
70235537Sgber
71235537Sgber	*blk = off / fsdev->nd_blocksize;
72235537Sgber	*offset = off % fsdev->nd_blocksize;
73235537Sgber	return (0);
74235537Sgber}
75235537Sgber
76235537Sgber/* Alloc new segment */
77235537Sgberint
78235537Sgbernandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
79235537Sgber{
80235537Sgber	struct nandfs_node *su_node;
81235537Sgber	struct nandfs_sufile_header *su_header;
82235537Sgber	struct nandfs_segment_usage *su_usage;
83235537Sgber	struct buf *bp_header, *bp;
84235537Sgber	uint64_t blk, vblk, offset, i, rest, nsegments;
85235537Sgber	uint16_t seg_size;
86235537Sgber	int error, found;
87235537Sgber
88235537Sgber	seg_size = fsdev->nd_fsdata.f_segment_usage_size;
89235537Sgber	nsegments = fsdev->nd_fsdata.f_nsegments;
90235537Sgber
91235537Sgber	su_node = fsdev->nd_su_node;
92235537Sgber	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
93235537Sgber
94235537Sgber	/* Read header buffer */
95235537Sgber	error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
96235537Sgber	if (error) {
97235537Sgber		brelse(bp_header);
98235537Sgber		return (error);
99235537Sgber	}
100235537Sgber
101235537Sgber	su_header = (struct nandfs_sufile_header *)bp_header->b_data;
102235537Sgber
103235537Sgber	/* Get last allocated segment */
104235537Sgber	i = su_header->sh_last_alloc + 1;
105235537Sgber
106235537Sgber	found = 0;
107235537Sgber	bp = NULL;
108235537Sgber	while (!found) {
109235537Sgber		nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
110235537Sgber		if(blk != 0) {
111235537Sgber			error = nandfs_bmap_lookup(su_node, blk, &vblk);
112235537Sgber			if (error) {
113235537Sgber				nandfs_error("%s: cannot find vblk for blk "
114235537Sgber				    "blk:%jx\n", __func__, blk);
115235537Sgber				return (error);
116235537Sgber			}
117235537Sgber			if (vblk)
118235537Sgber				error = nandfs_bread(su_node, blk, NOCRED, 0,
119235537Sgber				    &bp);
120235537Sgber			else
121235537Sgber				error = nandfs_bcreate(su_node, blk, NOCRED, 0,
122235537Sgber				    &bp);
123235537Sgber			if (error) {
124235537Sgber				nandfs_error("%s: cannot create/read "
125235537Sgber				    "vblk:%jx\n", __func__, vblk);
126235537Sgber				if (bp)
127235537Sgber					brelse(bp);
128235537Sgber				return (error);
129235537Sgber			}
130235537Sgber
131235537Sgber			su_usage = SU_USAGE_OFF(bp, offset);
132235537Sgber		} else {
133235537Sgber			su_usage = SU_USAGE_OFF(bp_header, offset);
134235537Sgber			bp = bp_header;
135235537Sgber		}
136235537Sgber
137235537Sgber		rest = (fsdev->nd_blocksize - offset) / seg_size;
138235537Sgber		/* Go through all su usage in block */
139235537Sgber		while (rest) {
140298806Spfg			/* When last check start from beginning */
141235537Sgber			if (i == nsegments)
142235537Sgber				break;
143235537Sgber
144235537Sgber			if (!su_usage->su_flags) {
145235537Sgber				su_usage->su_flags = 1;
146235537Sgber				found = 1;
147235537Sgber				break;
148235537Sgber			}
149235537Sgber			su_usage++;
150235537Sgber			i++;
151235537Sgber
152235537Sgber			/* If all checked return error */
153235537Sgber			if (i == su_header->sh_last_alloc) {
154235537Sgber				DPRINTF(SEG, ("%s: cannot allocate segment \n",
155235537Sgber				    __func__));
156235537Sgber				brelse(bp_header);
157235537Sgber				if (blk != 0)
158235537Sgber					brelse(bp);
159235537Sgber				return (1);
160235537Sgber			}
161235537Sgber			rest--;
162235537Sgber		}
163235537Sgber		if (!found) {
164235537Sgber			/* Otherwise read another block */
165235537Sgber			if (blk != 0)
166235537Sgber				brelse(bp);
167235537Sgber			if (i == nsegments) {
168235537Sgber				blk = 0;
169235537Sgber				i = 0;
170235537Sgber			} else
171235537Sgber				blk++;
172235537Sgber			offset = 0;
173235537Sgber		}
174235537Sgber	}
175235537Sgber
176235537Sgber	if (found) {
177235537Sgber		*seg = i;
178235537Sgber		su_header->sh_last_alloc = i;
179235537Sgber		su_header->sh_ncleansegs--;
180235537Sgber		su_header->sh_ndirtysegs++;
181235537Sgber
182235537Sgber		fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
183235537Sgber		    fsdev->nd_fsdata.f_blocks_per_segment;
184235537Sgber		fsdev->nd_clean_segs--;
185235537Sgber
186235537Sgber		/*
187235537Sgber		 * It is mostly called from syncer() so we want to force
188235537Sgber		 * making buf dirty.
189235537Sgber		 */
190235537Sgber		error = nandfs_dirty_buf(bp_header, 1);
191235537Sgber		if (error) {
192235537Sgber			if (bp && bp != bp_header)
193235537Sgber				brelse(bp);
194235537Sgber			return (error);
195235537Sgber		}
196235537Sgber		if (bp && bp != bp_header)
197235537Sgber			nandfs_dirty_buf(bp, 1);
198235537Sgber
199235537Sgber		DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
200235537Sgber
201235537Sgber		return (0);
202235537Sgber	}
203235537Sgber
204235537Sgber	DPRINTF(SEG, ("%s: failed\n", __func__));
205235537Sgber
206235537Sgber	return (1);
207235537Sgber}
208235537Sgber
209235537Sgber/*
210235537Sgber * Make buffer dirty, it will be updated soon but first it need to be
211235537Sgber * gathered by syncer.
212235537Sgber */
213235537Sgberint
214235537Sgbernandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
215235537Sgber{
216235537Sgber	struct nandfs_node *su_node;
217235537Sgber	struct buf *bp;
218235537Sgber	uint64_t blk, offset;
219235537Sgber	int error;
220235537Sgber
221235537Sgber	su_node = fsdev->nd_su_node;
222235537Sgber	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
223235537Sgber
224235537Sgber	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
225235537Sgber
226235537Sgber	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
227235537Sgber	if (error) {
228235537Sgber		brelse(bp);
229235537Sgber		nandfs_error("%s: cannot preallocate new segment\n", __func__);
230235537Sgber		return (error);
231235537Sgber	} else
232235537Sgber		nandfs_dirty_buf(bp, 1);
233235537Sgber
234235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
235235537Sgber	return (error);
236235537Sgber}
237235537Sgber
238235537Sgber/* Update block count of segment */
239235537Sgberint
240235537Sgbernandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
241235537Sgber{
242235537Sgber	struct nandfs_node *su_node;
243235537Sgber	struct nandfs_segment_usage *su_usage;
244235537Sgber	struct buf *bp;
245235537Sgber	uint64_t blk, offset;
246235537Sgber	int error;
247235537Sgber
248235537Sgber	su_node = fsdev->nd_su_node;
249235537Sgber	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
250235537Sgber
251235537Sgber	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
252235537Sgber
253235537Sgber	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
254235537Sgber	if (error) {
255235537Sgber		nandfs_error("%s: read block:%jx to update\n",
256235537Sgber		    __func__, blk);
257235537Sgber		brelse(bp);
258235537Sgber		return (error);
259235537Sgber	}
260235537Sgber
261235537Sgber	su_usage = SU_USAGE_OFF(bp, offset);
262235537Sgber	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
263235537Sgber	su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
264235537Sgber	su_usage->su_nblocks += nblks;
265235537Sgber
266235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n",  __func__,
267235537Sgber	    (uintmax_t)seg, nblks, su_usage->su_nblocks));
268235537Sgber
269235537Sgber	nandfs_dirty_buf(bp, 1);
270235537Sgber
271235537Sgber	return (0);
272235537Sgber}
273235537Sgber
274235537Sgber/* Make segment free */
275235537Sgberint
276235537Sgbernandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
277235537Sgber{
278235537Sgber	struct nandfs_node *su_node;
279235537Sgber	struct nandfs_sufile_header *su_header;
280235537Sgber	struct nandfs_segment_usage *su_usage;
281235537Sgber	struct buf *bp_header, *bp;
282235537Sgber	uint64_t blk, offset;
283235537Sgber	int error;
284235537Sgber
285235537Sgber	su_node = fsdev->nd_su_node;
286235537Sgber	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
287235537Sgber
288235537Sgber	/* Read su header */
289235537Sgber	error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
290235537Sgber	if (error) {
291235537Sgber		brelse(bp_header);
292235537Sgber		return (error);
293235537Sgber	}
294235537Sgber
295235537Sgber	su_header = (struct nandfs_sufile_header *)bp_header->b_data;
296235537Sgber	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
297235537Sgber
298235537Sgber	/* Read su usage block if other than su header block */
299235537Sgber	if (blk != 0) {
300235537Sgber		error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
301235537Sgber		if (error) {
302235537Sgber			brelse(bp);
303235537Sgber			brelse(bp_header);
304235537Sgber			return (error);
305235537Sgber		}
306235537Sgber	} else
307235537Sgber		bp = bp_header;
308235537Sgber
309235537Sgber	/* Reset su usage data */
310235537Sgber	su_usage = SU_USAGE_OFF(bp, offset);
311235537Sgber	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
312235537Sgber	su_usage->su_nblocks = 0;
313235537Sgber	su_usage->su_flags = 0;
314235537Sgber
315235537Sgber	/* Update clean/dirty counter in header */
316235537Sgber	su_header->sh_ncleansegs++;
317235537Sgber	su_header->sh_ndirtysegs--;
318235537Sgber
319235537Sgber	/*
320235537Sgber	 *  Make buffers dirty, called by cleaner
321235537Sgber	 *  so force dirty even if no much space left
322235537Sgber	 *  on device
323235537Sgber	 */
324235537Sgber	nandfs_dirty_buf(bp_header, 1);
325235537Sgber	if (bp != bp_header)
326235537Sgber		nandfs_dirty_buf(bp, 1);
327235537Sgber
328235537Sgber	/* Update free block count */
329235537Sgber	fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
330235537Sgber	    fsdev->nd_fsdata.f_blocks_per_segment;
331235537Sgber	fsdev->nd_clean_segs++;
332235537Sgber
333235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
334235537Sgber
335235537Sgber	return (0);
336235537Sgber}
337235537Sgber
338235537Sgberstatic int
339235537Sgbernandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
340235537Sgber{
341235537Sgber	struct nandfs_node *su_node;
342235537Sgber	struct nandfs_segment_usage *su_usage;
343235537Sgber	struct buf *bp;
344235537Sgber	uint64_t blk, offset;
345235537Sgber	int error;
346235537Sgber
347235537Sgber	su_node = fsdev->nd_su_node;
348235537Sgber	ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
349235537Sgber
350235537Sgber	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
351235537Sgber
352235537Sgber	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
353235537Sgber	if (error) {
354235537Sgber		brelse(bp);
355235537Sgber		return (error);
356235537Sgber	}
357235537Sgber
358235537Sgber	su_usage = SU_USAGE_OFF(bp, offset);
359235537Sgber	su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
360235537Sgber	su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR;
361235537Sgber
362235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
363235537Sgber
364235537Sgber	nandfs_dirty_buf(bp, 1);
365235537Sgber
366235537Sgber	return (0);
367235537Sgber}
368235537Sgber
369235537Sgberint
370235537Sgbernandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
371235537Sgber{
372235537Sgber	struct nandfs_node *su_node;
373235537Sgber	struct nandfs_segment_usage *su_usage;
374235537Sgber	struct buf *bp;
375235537Sgber	uint64_t blk, offset;
376235537Sgber	int error;
377235537Sgber
378235537Sgber	su_node = fsdev->nd_su_node;
379235537Sgber
380235537Sgber	VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
381235537Sgber
382235537Sgber	nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
383235537Sgber
384235537Sgber	error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
385235537Sgber	if (error) {
386235537Sgber		brelse(bp);
387235537Sgber		VOP_UNLOCK(NTOV(su_node), 0);
388235537Sgber		return (error);
389235537Sgber	}
390235537Sgber
391235537Sgber	su_usage = SU_USAGE_OFF(bp, offset);
392235537Sgber	MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0);
393235537Sgber	su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC;
394235537Sgber
395235537Sgber	brelse(bp);
396235537Sgber	VOP_UNLOCK(NTOV(su_node), 0);
397235537Sgber
398235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
399235537Sgber
400235537Sgber	return (0);
401235537Sgber}
402235537Sgber
403235537Sgberint
404235537Sgbernandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
405235537Sgber{
406235537Sgber	uint64_t offset, segsize;
407235537Sgber	uint32_t bps, bsize;
408235537Sgber	int error = 0;
409235537Sgber
410235537Sgber	bps = fsdev->nd_fsdata.f_blocks_per_segment;
411235537Sgber	bsize = fsdev->nd_blocksize;
412235537Sgber	segsize = bsize * bps;
413235537Sgber	nandfs_get_segment_range(fsdev, seg, &offset, NULL);
414235537Sgber	offset *= bsize;
415235537Sgber
416235537Sgber	DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
417235537Sgber
418235537Sgber	/* Erase it and mark it bad when fail */
419235537Sgber	if (nandfs_erase(fsdev, offset, segsize))
420235537Sgber		error = nandfs_bad_segment(fsdev, seg);
421235537Sgber
422235537Sgber	if (error)
423235537Sgber		return (error);
424235537Sgber
425235537Sgber	/* Mark it free */
426235537Sgber	error = nandfs_free_segment(fsdev, seg);
427235537Sgber
428235537Sgber	return (error);
429235537Sgber}
430235537Sgber
431235537Sgberint
432235537Sgbernandfs_get_seg_stat(struct nandfs_device *nandfsdev,
433235537Sgber    struct nandfs_seg_stat *nss)
434235537Sgber{
435235537Sgber	struct nandfs_sufile_header *suhdr;
436235537Sgber	struct nandfs_node *su_node;
437235537Sgber	struct buf *bp;
438235537Sgber	int err;
439235537Sgber
440235537Sgber	su_node = nandfsdev->nd_su_node;
441235537Sgber
442235537Sgber	NANDFS_WRITELOCK(nandfsdev);
443235537Sgber	VOP_LOCK(NTOV(su_node), LK_SHARED);
444235537Sgber	err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
445235537Sgber	if (err) {
446235537Sgber		brelse(bp);
447235537Sgber		VOP_UNLOCK(NTOV(su_node), 0);
448235537Sgber		NANDFS_WRITEUNLOCK(nandfsdev);
449235537Sgber		return (-1);
450235537Sgber	}
451235537Sgber
452235537Sgber	suhdr = (struct nandfs_sufile_header *)bp->b_data;
453235537Sgber	nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments;
454235537Sgber	nss->nss_ncleansegs = suhdr->sh_ncleansegs;
455235537Sgber	nss->nss_ndirtysegs = suhdr->sh_ndirtysegs;
456235537Sgber	nss->nss_ctime = 0;
457235537Sgber	nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
458235537Sgber	nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
459235537Sgber
460235537Sgber	brelse(bp);
461235537Sgber	VOP_UNLOCK(NTOV(su_node), 0);
462235537Sgber
463235537Sgber	NANDFS_WRITEUNLOCK(nandfsdev);
464235537Sgber
465235537Sgber	return (0);
466235537Sgber}
467235537Sgber
468235537Sgberint
469235537Sgbernandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
470235537Sgber    struct nandfs_argv *nargv)
471235537Sgber{
472235537Sgber	struct nandfs_suinfo *nsi;
473235537Sgber	int error;
474235537Sgber
475235537Sgber	if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
476235537Sgber		return (EINVAL);
477235537Sgber
478235537Sgber	nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
479235537Sgber	    M_NANDFSTEMP, M_WAITOK | M_ZERO);
480235537Sgber
481235537Sgber	error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
482235537Sgber	    nargv->nv_index);
483235537Sgber
484235537Sgber	if (error == 0)
485235537Sgber		error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
486235537Sgber		    sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
487235537Sgber
488235537Sgber	free(nsi, M_NANDFSTEMP);
489235537Sgber	return (error);
490235537Sgber}
491235537Sgber
492235537Sgberint
493235537Sgbernandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
494235537Sgber    uint32_t nmembs, uint64_t segment)
495235537Sgber{
496235537Sgber
497235537Sgber	return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
498235537Sgber	    NULL, 0, 0));
499235537Sgber}
500235537Sgber
501235537Sgberint
502235537Sgbernandfs_get_segment_info_filter(struct nandfs_device *fsdev,
503235537Sgber    struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment,
504235537Sgber    uint64_t *nsegs, uint32_t filter, uint32_t nfilter)
505235537Sgber{
506235537Sgber	struct nandfs_segment_usage *su;
507235537Sgber	struct nandfs_node *su_node;
508235537Sgber	struct buf *bp;
509235537Sgber	uint64_t curr, blocknr, blockoff, i;
510235537Sgber	uint32_t flags;
511235537Sgber	int err = 0;
512235537Sgber
513235537Sgber	curr = ~(0);
514235537Sgber
515235537Sgber	lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
516235537Sgber	su_node = fsdev->nd_su_node;
517235537Sgber
518235537Sgber	VOP_LOCK(NTOV(su_node), LK_SHARED);
519235537Sgber
520235537Sgber	bp = NULL;
521235537Sgber	if (nsegs !=  NULL)
522235537Sgber		*nsegs = 0;
523235537Sgber	for (i = 0; i < nmembs; segment++) {
524235537Sgber		if (segment == fsdev->nd_fsdata.f_nsegments)
525235537Sgber			break;
526235537Sgber
527235537Sgber		nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
528235537Sgber		    &blockoff);
529235537Sgber
530235537Sgber		if (i == 0 || curr != blocknr) {
531235537Sgber			if (bp != NULL)
532235537Sgber				brelse(bp);
533235537Sgber			err = nandfs_bread(su_node, blocknr, NOCRED,
534235537Sgber			    0, &bp);
535235537Sgber			if (err) {
536235537Sgber				goto out;
537235537Sgber			}
538235537Sgber			curr = blocknr;
539235537Sgber		}
540235537Sgber
541235537Sgber		su = SU_USAGE_OFF(bp, blockoff);
542235537Sgber		flags = su->su_flags;
543235537Sgber		if (segment == fsdev->nd_seg_num ||
544235537Sgber		    segment == fsdev->nd_next_seg_num)
545235537Sgber			flags |= NANDFS_SEGMENT_USAGE_ACTIVE;
546235537Sgber
547235537Sgber		if (nfilter != 0 && (flags & nfilter) != 0)
548235537Sgber			continue;
549235537Sgber		if (filter != 0 && (flags & filter) == 0)
550235537Sgber			continue;
551235537Sgber
552235537Sgber		nsi->nsi_num = segment;
553235537Sgber		nsi->nsi_lastmod = su->su_lastmod;
554235537Sgber		nsi->nsi_blocks = su->su_nblocks;
555235537Sgber		nsi->nsi_flags = flags;
556235537Sgber		nsi++;
557235537Sgber		i++;
558235537Sgber		if (nsegs != NULL)
559235537Sgber			(*nsegs)++;
560235537Sgber	}
561235537Sgber
562235537Sgberout:
563235537Sgber	if (bp != NULL)
564235537Sgber		brelse(bp);
565235537Sgber	VOP_UNLOCK(NTOV(su_node), 0);
566235537Sgber	lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
567235537Sgber
568235537Sgber	return (err);
569235537Sgber}
570