1 /* $NetBSD: vfs_trans.c,v 1.26 2013/01/21 09:14:01 hannken Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Juergen Hannken-Illjes. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.26 2013/01/21 09:14:01 hannken Exp $"); 34 35 /* 36 * File system transaction operations. 37 */ 38 39 #include "opt_ddb.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/atomic.h> 44 #include <sys/buf.h> 45 #include <sys/kmem.h> 46 #include <sys/mount.h> 47 #include <sys/pserialize.h> 48 #include <sys/vnode.h> 49 #define _FSTRANS_API_PRIVATE 50 #include <sys/fstrans.h> 51 #include <sys/proc.h> 52 53 #include <miscfs/specfs/specdev.h> 54 #include <miscfs/syncfs/syncfs.h> 55 56 struct fscow_handler { 57 LIST_ENTRY(fscow_handler) ch_list; 58 int (*ch_func)(void *, struct buf *, bool); 59 void *ch_arg; 60 }; 61 struct fstrans_lwp_info { 62 struct fstrans_lwp_info *fli_succ; 63 struct lwp *fli_self; 64 struct mount *fli_mount; 65 int fli_trans_cnt; 66 int fli_cow_cnt; 67 enum fstrans_lock_type fli_lock_type; 68 LIST_ENTRY(fstrans_lwp_info) fli_list; 69 }; 70 struct fstrans_mount_info { 71 enum fstrans_state fmi_state; 72 unsigned int fmi_ref_cnt; 73 bool fmi_cow_change; 74 LIST_HEAD(, fscow_handler) fmi_cow_handler; 75 }; 76 77 static specificdata_key_t lwp_data_key; /* Our specific data key. */ 78 static kmutex_t vfs_suspend_lock; /* Serialize suspensions. */ 79 static kmutex_t fstrans_lock; /* Fstrans big lock. */ 80 static kcondvar_t fstrans_state_cv; /* Fstrans or cow state changed. */ 81 static kcondvar_t fstrans_count_cv; /* Fstrans or cow count changed. */ 82 static pserialize_t fstrans_psz; /* Pserialize state. */ 83 static LIST_HEAD(fstrans_lwp_head, fstrans_lwp_info) fstrans_fli_head; 84 /* List of all fstrans_lwp_info. */ 85 static pool_cache_t fstrans_cache; /* Pool of struct fstrans_lwp_info. */ 86 87 static void fstrans_lwp_dtor(void *); 88 static void fstrans_mount_dtor(struct mount *); 89 static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *, bool); 90 static bool grant_lock(const enum fstrans_state, const enum fstrans_lock_type); 91 static bool state_change_done(const struct mount *); 92 static bool cow_state_change_done(const struct mount *); 93 static void cow_change_enter(const struct mount *); 94 static void cow_change_done(const struct mount *); 95 96 /* 97 * Initialize. 98 */ 99 void 100 fstrans_init(void) 101 { 102 int error; 103 104 error = lwp_specific_key_create(&lwp_data_key, fstrans_lwp_dtor); 105 KASSERT(error == 0); 106 107 mutex_init(&vfs_suspend_lock, MUTEX_DEFAULT, IPL_NONE); 108 mutex_init(&fstrans_lock, MUTEX_DEFAULT, IPL_NONE); 109 cv_init(&fstrans_state_cv, "fstchg"); 110 cv_init(&fstrans_count_cv, "fstcnt"); 111 fstrans_psz = pserialize_create(); 112 LIST_INIT(&fstrans_fli_head); 113 fstrans_cache = pool_cache_init(sizeof(struct fstrans_lwp_info), 0, 0, 114 0, "fstrans", NULL, IPL_NONE, NULL, NULL, NULL); 115 } 116 117 /* 118 * Deallocate lwp state. 119 */ 120 static void 121 fstrans_lwp_dtor(void *arg) 122 { 123 struct fstrans_lwp_info *fli, *fli_next; 124 125 mutex_enter(&fstrans_lock); 126 for (fli = arg; fli; fli = fli_next) { 127 KASSERT(fli->fli_trans_cnt == 0); 128 KASSERT(fli->fli_cow_cnt == 0); 129 if (fli->fli_mount != NULL) 130 fstrans_mount_dtor(fli->fli_mount); 131 fli_next = fli->fli_succ; 132 LIST_REMOVE(fli, fli_list); 133 pool_cache_put(fstrans_cache, fli); 134 } 135 mutex_exit(&fstrans_lock); 136 } 137 138 /* 139 * Dereference mount state. 140 */ 141 static void 142 fstrans_mount_dtor(struct mount *mp) 143 { 144 struct fstrans_mount_info *fmi; 145 146 fmi = mp->mnt_transinfo; 147 if (atomic_dec_uint_nv(&fmi->fmi_ref_cnt) > 0) 148 return; 149 150 KASSERT(fmi->fmi_state == FSTRANS_NORMAL); 151 KASSERT(LIST_FIRST(&fmi->fmi_cow_handler) == NULL); 152 153 kmem_free(fmi, sizeof(*fmi)); 154 mp->mnt_iflag &= ~IMNT_HAS_TRANS; 155 mp->mnt_transinfo = NULL; 156 157 vfs_destroy(mp); 158 } 159 160 /* 161 * Allocate mount state. 162 */ 163 int 164 fstrans_mount(struct mount *mp) 165 { 166 int error; 167 struct fstrans_mount_info *new; 168 169 error = vfs_busy(mp, NULL); 170 if (error) 171 return error; 172 if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL) 173 return ENOMEM; 174 new->fmi_state = FSTRANS_NORMAL; 175 new->fmi_ref_cnt = 1; 176 LIST_INIT(&new->fmi_cow_handler); 177 new->fmi_cow_change = false; 178 179 mp->mnt_transinfo = new; 180 mp->mnt_iflag |= IMNT_HAS_TRANS; 181 182 vfs_unbusy(mp, true, NULL); 183 184 return 0; 185 } 186 187 /* 188 * Deallocate mount state. 189 */ 190 void 191 fstrans_unmount(struct mount *mp) 192 { 193 194 KASSERT(mp->mnt_transinfo != NULL); 195 196 fstrans_mount_dtor(mp); 197 } 198 199 /* 200 * Retrieve the per lwp info for this mount allocating if necessary. 201 */ 202 static struct fstrans_lwp_info * 203 fstrans_get_lwp_info(struct mount *mp, bool do_alloc) 204 { 205 struct fstrans_lwp_info *fli, *res; 206 struct fstrans_mount_info *fmi; 207 208 /* 209 * Scan our list for a match clearing entries whose mount is gone. 210 */ 211 res = NULL; 212 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { 213 if (fli->fli_mount == mp) { 214 KASSERT(res == NULL); 215 res = fli; 216 } else if (fli->fli_mount != NULL && 217 (fli->fli_mount->mnt_iflag & IMNT_GONE) != 0 && 218 fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0) { 219 fstrans_mount_dtor(fli->fli_mount); 220 fli->fli_mount = NULL; 221 } 222 } 223 if (__predict_true(res != NULL)) 224 return res; 225 226 if (! do_alloc) 227 return NULL; 228 229 /* 230 * Try to reuse a cleared entry or allocate a new one. 231 */ 232 for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { 233 if (fli->fli_mount == NULL) { 234 KASSERT(fli->fli_trans_cnt == 0); 235 KASSERT(fli->fli_cow_cnt == 0); 236 break; 237 } 238 } 239 if (fli == NULL) { 240 fli = pool_cache_get(fstrans_cache, PR_WAITOK); 241 mutex_enter(&fstrans_lock); 242 memset(fli, 0, sizeof(*fli)); 243 fli->fli_self = curlwp; 244 LIST_INSERT_HEAD(&fstrans_fli_head, fli, fli_list); 245 mutex_exit(&fstrans_lock); 246 fli->fli_succ = lwp_getspecific(lwp_data_key); 247 lwp_setspecific(lwp_data_key, fli); 248 } 249 250 /* 251 * Attach the entry to the mount. 252 */ 253 fmi = mp->mnt_transinfo; 254 fli->fli_mount = mp; 255 atomic_inc_uint(&fmi->fmi_ref_cnt); 256 257 return fli; 258 } 259 260 /* 261 * Check if this lock type is granted at this state. 262 */ 263 static bool 264 grant_lock(const enum fstrans_state state, const enum fstrans_lock_type type) 265 { 266 267 if (__predict_true(state == FSTRANS_NORMAL)) 268 return true; 269 if (type == FSTRANS_EXCL) 270 return true; 271 if (state == FSTRANS_SUSPENDING && type == FSTRANS_LAZY) 272 return true; 273 274 return false; 275 } 276 277 /* 278 * Start a transaction. If this thread already has a transaction on this 279 * file system increment the reference counter. 280 */ 281 int 282 _fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait) 283 { 284 int s; 285 struct fstrans_lwp_info *fli; 286 struct fstrans_mount_info *fmi; 287 288 ASSERT_SLEEPABLE(); 289 290 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 291 return 0; 292 293 fli = fstrans_get_lwp_info(mp, true); 294 295 if (fli->fli_trans_cnt > 0) { 296 KASSERT(lock_type != FSTRANS_EXCL); 297 fli->fli_trans_cnt += 1; 298 299 return 0; 300 } 301 302 s = pserialize_read_enter(); 303 fmi = mp->mnt_transinfo; 304 if (__predict_true(grant_lock(fmi->fmi_state, lock_type))) { 305 fli->fli_trans_cnt = 1; 306 fli->fli_lock_type = lock_type; 307 pserialize_read_exit(s); 308 309 return 0; 310 } 311 pserialize_read_exit(s); 312 313 if (! wait) 314 return EBUSY; 315 316 mutex_enter(&fstrans_lock); 317 while (! grant_lock(fmi->fmi_state, lock_type)) 318 cv_wait(&fstrans_state_cv, &fstrans_lock); 319 fli->fli_trans_cnt = 1; 320 fli->fli_lock_type = lock_type; 321 mutex_exit(&fstrans_lock); 322 323 return 0; 324 } 325 326 /* 327 * Finish a transaction. 328 */ 329 void 330 fstrans_done(struct mount *mp) 331 { 332 int s; 333 struct fstrans_lwp_info *fli; 334 struct fstrans_mount_info *fmi; 335 336 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 337 return; 338 339 fli = fstrans_get_lwp_info(mp, false); 340 KASSERT(fli != NULL); 341 KASSERT(fli->fli_trans_cnt > 0); 342 343 if (fli->fli_trans_cnt > 1) { 344 fli->fli_trans_cnt -= 1; 345 346 return; 347 } 348 349 s = pserialize_read_enter(); 350 fmi = mp->mnt_transinfo; 351 if (__predict_true(fmi->fmi_state == FSTRANS_NORMAL)) { 352 fli->fli_trans_cnt = 0; 353 pserialize_read_exit(s); 354 355 return; 356 } 357 pserialize_read_exit(s); 358 359 mutex_enter(&fstrans_lock); 360 fli->fli_trans_cnt = 0; 361 cv_signal(&fstrans_count_cv); 362 mutex_exit(&fstrans_lock); 363 } 364 365 /* 366 * Check if this thread has an exclusive lock. 367 */ 368 int 369 fstrans_is_owner(struct mount *mp) 370 { 371 struct fstrans_lwp_info *fli; 372 373 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 374 return 0; 375 376 fli = fstrans_get_lwp_info(mp, false); 377 if (fli == NULL || fli->fli_trans_cnt == 0) 378 return 0; 379 380 KASSERT(fli->fli_mount == mp); 381 KASSERT(fli->fli_trans_cnt > 0); 382 383 return (fli->fli_lock_type == FSTRANS_EXCL); 384 } 385 386 /* 387 * True, if no thread is in a transaction not granted at the current state. 388 */ 389 static bool 390 state_change_done(const struct mount *mp) 391 { 392 struct fstrans_lwp_info *fli; 393 struct fstrans_mount_info *fmi; 394 395 KASSERT(mutex_owned(&fstrans_lock)); 396 397 fmi = mp->mnt_transinfo; 398 LIST_FOREACH(fli, &fstrans_fli_head, fli_list) { 399 if (fli->fli_mount != mp) 400 continue; 401 if (fli->fli_trans_cnt == 0) 402 continue; 403 if (grant_lock(fmi->fmi_state, fli->fli_lock_type)) 404 continue; 405 406 return false; 407 } 408 409 return true; 410 } 411 412 /* 413 * Set new file system state. 414 */ 415 int 416 fstrans_setstate(struct mount *mp, enum fstrans_state new_state) 417 { 418 int error; 419 enum fstrans_state old_state; 420 struct fstrans_mount_info *fmi; 421 422 fmi = mp->mnt_transinfo; 423 old_state = fmi->fmi_state; 424 if (old_state == new_state) 425 return 0; 426 427 mutex_enter(&fstrans_lock); 428 fmi->fmi_state = new_state; 429 pserialize_perform(fstrans_psz); 430 431 /* 432 * All threads see the new state now. 433 * Wait for transactions invalid at this state to leave. 434 */ 435 error = 0; 436 while (! state_change_done(mp)) { 437 error = cv_wait_sig(&fstrans_count_cv, &fstrans_lock); 438 if (error) { 439 new_state = fmi->fmi_state = FSTRANS_NORMAL; 440 break; 441 } 442 } 443 cv_broadcast(&fstrans_state_cv); 444 mutex_exit(&fstrans_lock); 445 446 if (old_state != new_state) { 447 if (old_state == FSTRANS_NORMAL) 448 fstrans_start(mp, FSTRANS_EXCL); 449 if (new_state == FSTRANS_NORMAL) 450 fstrans_done(mp); 451 } 452 453 return error; 454 } 455 456 /* 457 * Get current file system state. 458 */ 459 enum fstrans_state 460 fstrans_getstate(struct mount *mp) 461 { 462 struct fstrans_mount_info *fmi; 463 464 fmi = mp->mnt_transinfo; 465 KASSERT(fmi != NULL); 466 467 return fmi->fmi_state; 468 } 469 470 /* 471 * Request a filesystem to suspend all operations. 472 */ 473 int 474 vfs_suspend(struct mount *mp, int nowait) 475 { 476 int error; 477 478 if (nowait) { 479 if (!mutex_tryenter(&vfs_suspend_lock)) 480 return EWOULDBLOCK; 481 } else 482 mutex_enter(&vfs_suspend_lock); 483 484 mutex_enter(&syncer_mutex); 485 if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) { 486 mutex_exit(&syncer_mutex); 487 mutex_exit(&vfs_suspend_lock); 488 } 489 490 return error; 491 } 492 493 /* 494 * Request a filesystem to resume all operations. 495 */ 496 void 497 vfs_resume(struct mount *mp) 498 { 499 500 VFS_SUSPENDCTL(mp, SUSPEND_RESUME); 501 mutex_exit(&syncer_mutex); 502 mutex_exit(&vfs_suspend_lock); 503 } 504 505 506 /* 507 * True, if no thread is running a cow handler. 508 */ 509 static bool 510 cow_state_change_done(const struct mount *mp) 511 { 512 struct fstrans_lwp_info *fli; 513 struct fstrans_mount_info *fmi; 514 515 fmi = mp->mnt_transinfo; 516 517 KASSERT(mutex_owned(&fstrans_lock)); 518 KASSERT(fmi->fmi_cow_change); 519 520 LIST_FOREACH(fli, &fstrans_fli_head, fli_list) { 521 if (fli->fli_mount != mp) 522 continue; 523 if (fli->fli_cow_cnt == 0) 524 continue; 525 526 return false; 527 } 528 529 return true; 530 } 531 532 /* 533 * Prepare for changing this mounts cow list. 534 * Returns with fstrans_lock locked. 535 */ 536 static void 537 cow_change_enter(const struct mount *mp) 538 { 539 struct fstrans_mount_info *fmi; 540 541 fmi = mp->mnt_transinfo; 542 543 mutex_enter(&fstrans_lock); 544 545 /* 546 * Wait for other threads changing the list. 547 */ 548 while (fmi->fmi_cow_change) 549 cv_wait(&fstrans_state_cv, &fstrans_lock); 550 551 /* 552 * Wait until all threads are aware of a state change. 553 */ 554 fmi->fmi_cow_change = true; 555 pserialize_perform(fstrans_psz); 556 557 while (! cow_state_change_done(mp)) 558 cv_wait(&fstrans_count_cv, &fstrans_lock); 559 } 560 561 /* 562 * Done changing this mounts cow list. 563 */ 564 static void 565 cow_change_done(const struct mount *mp) 566 { 567 struct fstrans_mount_info *fmi; 568 569 KASSERT(mutex_owned(&fstrans_lock)); 570 571 fmi = mp->mnt_transinfo; 572 573 fmi->fmi_cow_change = false; 574 pserialize_perform(fstrans_psz); 575 576 cv_broadcast(&fstrans_state_cv); 577 578 mutex_exit(&fstrans_lock); 579 } 580 581 /* 582 * Add a handler to this mount. 583 */ 584 int 585 fscow_establish(struct mount *mp, int (*func)(void *, struct buf *, bool), 586 void *arg) 587 { 588 struct fstrans_mount_info *fmi; 589 struct fscow_handler *new; 590 591 if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 592 return EINVAL; 593 594 fmi = mp->mnt_transinfo; 595 KASSERT(fmi != NULL); 596 597 if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL) 598 return ENOMEM; 599 new->ch_func = func; 600 new->ch_arg = arg; 601 602 cow_change_enter(mp); 603 LIST_INSERT_HEAD(&fmi->fmi_cow_handler, new, ch_list); 604 cow_change_done(mp); 605 606 return 0; 607 } 608 609 /* 610 * Remove a handler from this mount. 611 */ 612 int 613 fscow_disestablish(struct mount *mp, int (*func)(void *, struct buf *, bool), 614 void *arg) 615 { 616 struct fstrans_mount_info *fmi; 617 struct fscow_handler *hp = NULL; 618 619 if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0) 620 return EINVAL; 621 622 fmi = mp->mnt_transinfo; 623 KASSERT(fmi != NULL); 624 625 cow_change_enter(mp); 626 LIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list) 627 if (hp->ch_func == func && hp->ch_arg == arg) 628 break; 629 if (hp != NULL) { 630 LIST_REMOVE(hp, ch_list); 631 kmem_free(hp, sizeof(*hp)); 632 } 633 cow_change_done(mp); 634 635 return hp ? 0 : EINVAL; 636 } 637 638 /* 639 * Check for need to copy block that is about to be written. 640 */ 641 int 642 fscow_run(struct buf *bp, bool data_valid) 643 { 644 int error, s; 645 struct mount *mp; 646 struct fstrans_lwp_info *fli; 647 struct fstrans_mount_info *fmi; 648 struct fscow_handler *hp; 649 650 /* 651 * First check if we need run the copy-on-write handler. 652 */ 653 if ((bp->b_flags & B_COWDONE)) 654 return 0; 655 if (bp->b_vp == NULL) { 656 bp->b_flags |= B_COWDONE; 657 return 0; 658 } 659 if (bp->b_vp->v_type == VBLK) 660 mp = bp->b_vp->v_specmountpoint; 661 else 662 mp = bp->b_vp->v_mount; 663 if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) { 664 bp->b_flags |= B_COWDONE; 665 return 0; 666 } 667 668 fli = fstrans_get_lwp_info(mp, true); 669 fmi = mp->mnt_transinfo; 670 671 /* 672 * On non-recursed run check if other threads 673 * want to change the list. 674 */ 675 if (fli->fli_cow_cnt == 0) { 676 s = pserialize_read_enter(); 677 if (__predict_false(fmi->fmi_cow_change)) { 678 pserialize_read_exit(s); 679 mutex_enter(&fstrans_lock); 680 while (fmi->fmi_cow_change) 681 cv_wait(&fstrans_state_cv, &fstrans_lock); 682 fli->fli_cow_cnt = 1; 683 mutex_exit(&fstrans_lock); 684 } else { 685 fli->fli_cow_cnt = 1; 686 pserialize_read_exit(s); 687 } 688 } else 689 fli->fli_cow_cnt += 1; 690 691 /* 692 * Run all copy-on-write handlers, stop on error. 693 */ 694 error = 0; 695 LIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list) 696 if ((error = (*hp->ch_func)(hp->ch_arg, bp, data_valid)) != 0) 697 break; 698 if (error == 0) 699 bp->b_flags |= B_COWDONE; 700 701 /* 702 * Check if other threads want to change the list. 703 */ 704 if (fli->fli_cow_cnt > 1) { 705 fli->fli_cow_cnt -= 1; 706 } else { 707 s = pserialize_read_enter(); 708 if (__predict_false(fmi->fmi_cow_change)) { 709 pserialize_read_exit(s); 710 mutex_enter(&fstrans_lock); 711 fli->fli_cow_cnt = 0; 712 cv_signal(&fstrans_count_cv); 713 mutex_exit(&fstrans_lock); 714 } else { 715 fli->fli_cow_cnt = 0; 716 pserialize_read_exit(s); 717 } 718 } 719 720 return error; 721 } 722 723 #if defined(DDB) 724 void fstrans_dump(int); 725 726 static void 727 fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose) 728 { 729 char prefix[9]; 730 struct fstrans_lwp_info *fli; 731 732 snprintf(prefix, sizeof(prefix), "%d.%d", p->p_pid, l->l_lid); 733 LIST_FOREACH(fli, &fstrans_fli_head, fli_list) { 734 if (fli->fli_self != l) 735 continue; 736 if (fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0) { 737 if (! verbose) 738 continue; 739 } 740 printf("%-8s", prefix); 741 if (verbose) 742 printf(" @%p", fli); 743 if (fli->fli_mount != NULL) 744 printf(" (%s)", fli->fli_mount->mnt_stat.f_mntonname); 745 else 746 printf(" NULL"); 747 if (fli->fli_trans_cnt == 0) { 748 printf(" -"); 749 } else { 750 switch (fli->fli_lock_type) { 751 case FSTRANS_LAZY: 752 printf(" lazy"); 753 break; 754 case FSTRANS_SHARED: 755 printf(" shared"); 756 break; 757 case FSTRANS_EXCL: 758 printf(" excl"); 759 break; 760 default: 761 printf(" %#x", fli->fli_lock_type); 762 break; 763 } 764 } 765 printf(" %d cow %d\n", fli->fli_trans_cnt, fli->fli_cow_cnt); 766 prefix[0] = '\0'; 767 } 768 } 769 770 static void 771 fstrans_print_mount(struct mount *mp, int verbose) 772 { 773 struct fstrans_mount_info *fmi; 774 775 fmi = mp->mnt_transinfo; 776 if (!verbose && (fmi == NULL || fmi->fmi_state == FSTRANS_NORMAL)) 777 return; 778 779 printf("%-16s ", mp->mnt_stat.f_mntonname); 780 if (fmi == NULL) { 781 printf("(null)\n"); 782 return; 783 } 784 switch (fmi->fmi_state) { 785 case FSTRANS_NORMAL: 786 printf("state normal\n"); 787 break; 788 case FSTRANS_SUSPENDING: 789 printf("state suspending\n"); 790 break; 791 case FSTRANS_SUSPENDED: 792 printf("state suspended\n"); 793 break; 794 default: 795 printf("state %#x\n", fmi->fmi_state); 796 break; 797 } 798 } 799 800 void 801 fstrans_dump(int full) 802 { 803 const struct proclist_desc *pd; 804 struct proc *p; 805 struct lwp *l; 806 struct mount *mp; 807 808 printf("Fstrans locks by lwp:\n"); 809 for (pd = proclists; pd->pd_list != NULL; pd++) 810 PROCLIST_FOREACH(p, pd->pd_list) 811 LIST_FOREACH(l, &p->p_lwps, l_sibling) 812 fstrans_print_lwp(p, l, full == 1); 813 814 printf("Fstrans state by mount:\n"); 815 CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) 816 fstrans_print_mount(mp, full == 1); 817 } 818 #endif /* defined(DDB) */ 819