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