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