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: stable/10/sys/fs/nandfs/nandfs_sufile.c 235537 2012-05-17 10:11:18Z gber $");
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
nandfs_seg_usage_blk_offset(struct nandfs_device * fsdev,uint64_t seg,uint64_t * blk,uint64_t * offset)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
nandfs_alloc_segment(struct nandfs_device * fsdev,uint64_t * seg)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
nandfs_touch_segment(struct nandfs_device * fsdev,uint64_t seg)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
nandfs_update_segment(struct nandfs_device * fsdev,uint64_t seg,uint32_t nblks)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
nandfs_free_segment(struct nandfs_device * fsdev,uint64_t seg)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
nandfs_bad_segment(struct nandfs_device * fsdev,uint64_t seg)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
nandfs_markgc_segment(struct nandfs_device * fsdev,uint64_t seg)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
nandfs_clear_segment(struct nandfs_device * fsdev,uint64_t seg)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
nandfs_get_seg_stat(struct nandfs_device * nandfsdev,struct nandfs_seg_stat * nss)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
nandfs_get_segment_info_ioctl(struct nandfs_device * fsdev,struct nandfs_argv * nargv)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
nandfs_get_segment_info(struct nandfs_device * fsdev,struct nandfs_suinfo * nsi,uint32_t nmembs,uint64_t segment)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
nandfs_get_segment_info_filter(struct nandfs_device * fsdev,struct nandfs_suinfo * nsi,uint32_t nmembs,uint64_t segment,uint64_t * nsegs,uint32_t filter,uint32_t nfilter)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 }
570