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: releng/10.4/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
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 }
Cache object: 4931449297796899fedab703ad034250
|