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