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
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
86 static void fstrans_lwp_dtor(void *);
87 static void fstrans_mount_dtor(struct mount *);
88 static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *, bool);
89 static bool grant_lock(const enum fstrans_state, const enum fstrans_lock_type);
90 static bool state_change_done(const struct mount *);
91 static bool cow_state_change_done(const struct mount *);
92 static void cow_change_enter(const struct mount *);
93 static void cow_change_done(const struct mount *);
94
95 /*
96 * Initialize.
97 */
98 void
99 fstrans_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 */
117 static void
118 fstrans_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 */
137 static void
138 fstrans_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 */
159 int
160 fstrans_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 */
185 void
186 fstrans_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 */
197 static struct fstrans_lwp_info *
198 fstrans_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 */
272 static bool
273 grant_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 */
290 int
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 */
338 void
339 fstrans_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 */
377 int
378 fstrans_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 */
398 static bool
399 state_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 */
424 int
425 fstrans_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 */
468 enum fstrans_state
469 fstrans_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 */
482 int
483 vfs_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 */
505 void
506 vfs_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 */
518 static bool
519 cow_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 */
545 static void
546 cow_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 */
573 static void
574 cow_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 */
593 int
594 fscow_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 */
620 int
621 fscow_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 */
649 int
650 fscow_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)
732 void fstrans_dump(int);
733
734 static void
735 fstrans_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
778 static void
779 fstrans_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
808 void
809 fstrans_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