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$"); 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 59static int 60nandfs_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 */ 77int 78nandfs_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 beginning */ 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 */ 213int 214nandfs_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 */ 239int 240nandfs_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 */ 275int 276nandfs_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 338static int 339nandfs_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 369int 370nandfs_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 403int 404nandfs_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 431int 432nandfs_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 468int 469nandfs_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 492int 493nandfs_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 501int 502nandfs_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 562out: 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