1 /* $NetBSD: vfs_trans.c,v 1.24 2008/11/16 19:34:29 pooka Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Juergen Hannken-Illjes.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.24 2008/11/16 19:34:29 pooka Exp $");
36 * File system transaction operations.
42 #define _LWP_API_PRIVATE /* Need _lwp_getspecific_by_lwp() */
45 #include <sys/param.h>
46 #include <sys/systm.h>
49 #include <sys/mount.h>
50 #include <sys/rwlock.h>
51 #include <sys/vnode.h>
52 #define _FSTRANS_API_PRIVATE
53 #include <sys/fstrans.h>
56 #include <miscfs/specfs/specdev.h>
57 #include <miscfs/syncfs/syncfs.h>
59 struct fscow_handler
{
60 SLIST_ENTRY(fscow_handler
) ch_list
;
61 int (*ch_func
)(void *, struct buf
*, bool);
64 struct fstrans_lwp_info
{
65 struct fstrans_lwp_info
*fli_succ
;
66 struct mount
*fli_mount
;
69 enum fstrans_lock_type fli_lock_type
;
71 struct fstrans_mount_info
{
72 enum fstrans_state fmi_state
;
73 krwlock_t fmi_shared_lock
;
74 krwlock_t fmi_lazy_lock
;
75 krwlock_t fmi_cow_lock
;
76 SLIST_HEAD(, fscow_handler
) fmi_cow_handler
;
79 static specificdata_key_t lwp_data_key
;
80 static kmutex_t vfs_suspend_lock
; /* Serialize suspensions. */
81 static pool_cache_t fstrans_cache
;
83 static void fstrans_lwp_dtor(void *);
84 static struct fstrans_lwp_info
*fstrans_get_lwp_info(struct mount
*);
94 error
= lwp_specific_key_create(&lwp_data_key
, fstrans_lwp_dtor
);
97 mutex_init(&vfs_suspend_lock
, MUTEX_DEFAULT
, IPL_NONE
);
98 fstrans_cache
= pool_cache_init(sizeof(struct fstrans_lwp_info
), 0, 0,
99 0, "fstrans", NULL
, IPL_NONE
, NULL
, NULL
, NULL
);
103 * Deallocate lwp state
106 fstrans_lwp_dtor(void *arg
)
108 struct fstrans_lwp_info
*fli
, *fli_next
;
110 for (fli
= arg
; fli
; fli
= fli_next
) {
111 KASSERT(fli
->fli_trans_cnt
== 0);
112 KASSERT(fli
->fli_cow_cnt
== 0);
113 fli_next
= fli
->fli_succ
;
114 pool_cache_put(fstrans_cache
, fli
);
119 * Allocate mount state
122 fstrans_mount(struct mount
*mp
)
124 struct fstrans_mount_info
*new;
126 if ((new = kmem_alloc(sizeof(*new), KM_SLEEP
)) == NULL
)
128 new->fmi_state
= FSTRANS_NORMAL
;
129 rw_init(&new->fmi_lazy_lock
);
130 rw_init(&new->fmi_shared_lock
);
131 SLIST_INIT(&new->fmi_cow_handler
);
132 rw_init(&new->fmi_cow_lock
);
134 mp
->mnt_transinfo
= new;
135 mp
->mnt_iflag
|= IMNT_HAS_TRANS
;
141 * Deallocate mount state
144 fstrans_unmount(struct mount
*mp
)
146 struct fstrans_mount_info
*fmi
;
147 struct fscow_handler
*hp
;
149 if ((fmi
= mp
->mnt_transinfo
) == NULL
)
152 KASSERT(fmi
->fmi_state
== FSTRANS_NORMAL
);
153 rw_destroy(&fmi
->fmi_lazy_lock
);
154 rw_destroy(&fmi
->fmi_shared_lock
);
155 rw_enter(&fmi
->fmi_cow_lock
, RW_WRITER
);
156 while ((hp
= SLIST_FIRST(&fmi
->fmi_cow_handler
)) != NULL
) {
157 SLIST_REMOVE(&fmi
->fmi_cow_handler
, hp
, fscow_handler
, ch_list
);
158 kmem_free(hp
, sizeof(*hp
));
160 rw_exit(&fmi
->fmi_cow_lock
);
161 rw_destroy(&fmi
->fmi_cow_lock
);
162 kmem_free(fmi
, sizeof(*fmi
));
163 mp
->mnt_iflag
&= ~IMNT_HAS_TRANS
;
164 mp
->mnt_transinfo
= NULL
;
168 * Retrieve the per lwp info for this mount
170 static struct fstrans_lwp_info
*
171 fstrans_get_lwp_info(struct mount
*mp
)
173 struct fstrans_lwp_info
*fli
, *new_fli
;
176 for (fli
= lwp_getspecific(lwp_data_key
); fli
; fli
= fli
->fli_succ
) {
177 if (fli
->fli_mount
== mp
)
179 else if (fli
->fli_trans_cnt
== 0 && fli
->fli_cow_cnt
== 0 &&
184 if (new_fli
== NULL
) {
185 new_fli
= pool_cache_get(fstrans_cache
, PR_WAITOK
);
186 new_fli
->fli_trans_cnt
= 0;
187 new_fli
->fli_cow_cnt
= 0;
188 new_fli
->fli_succ
= lwp_getspecific(lwp_data_key
);
189 lwp_setspecific(lwp_data_key
, new_fli
);
192 KASSERT(new_fli
->fli_trans_cnt
== 0);
193 KASSERT(new_fli
->fli_cow_cnt
== 0);
195 new_fli
->fli_mount
= mp
;
201 * Start a transaction. If this thread already has a transaction on this
202 * file system increment the reference counter.
203 * A thread with an exclusive transaction lock may get a shared or lazy one.
204 * A thread with a shared or lazy transaction lock cannot upgrade to an
208 _fstrans_start(struct mount
*mp
, enum fstrans_lock_type lock_type
, int wait
)
212 struct fstrans_lwp_info
*fli
;
213 struct fstrans_mount_info
*fmi
;
217 if (mp
== NULL
|| (mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
220 fli
= fstrans_get_lwp_info(mp
);
222 if (fli
->fli_trans_cnt
> 0) {
223 if (fli
->fli_lock_type
!= FSTRANS_EXCL
&&
224 lock_type
== FSTRANS_EXCL
)
225 panic("fstrans_start: cannot upgrade lock");
226 fli
->fli_trans_cnt
+= 1;
230 fmi
= mp
->mnt_transinfo
;
232 if (lock_type
== FSTRANS_LAZY
)
233 lock_p
= &fmi
->fmi_lazy_lock
;
235 lock_p
= &fmi
->fmi_shared_lock
;
236 lock_op
= (lock_type
== FSTRANS_EXCL
? RW_WRITER
: RW_READER
);
239 rw_enter(lock_p
, lock_op
);
240 else if (rw_tryenter(lock_p
, lock_op
) == 0)
243 fli
->fli_trans_cnt
= 1;
244 fli
->fli_lock_type
= lock_type
;
250 * Finish a transaction.
253 fstrans_done(struct mount
*mp
)
255 struct fstrans_lwp_info
*fli
;
256 struct fstrans_mount_info
*fmi
;
258 if (mp
== NULL
|| (mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
261 for (fli
= lwp_getspecific(lwp_data_key
); fli
; fli
= fli
->fli_succ
) {
262 if (fli
->fli_mount
== mp
) {
263 fli
->fli_trans_cnt
-= 1;
264 if (fli
->fli_trans_cnt
> 0)
270 KASSERT(fli
!= NULL
);
271 KASSERT(fli
->fli_mount
== mp
);
272 KASSERT(fli
->fli_trans_cnt
== 0);
274 fmi
= mp
->mnt_transinfo
;
275 KASSERT(fmi
!= NULL
);
276 if (fli
->fli_lock_type
== FSTRANS_LAZY
)
277 rw_exit(&fmi
->fmi_lazy_lock
);
279 rw_exit(&fmi
->fmi_shared_lock
);
283 * Check if this thread has an exclusive lock.
286 fstrans_is_owner(struct mount
*mp
)
288 struct fstrans_lwp_info
*fli
;
292 if ((mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
295 for (fli
= lwp_getspecific(lwp_data_key
); fli
; fli
= fli
->fli_succ
)
296 if (fli
->fli_mount
== mp
)
299 if (fli
== NULL
|| fli
->fli_trans_cnt
== 0)
302 KASSERT(fli
->fli_mount
== mp
);
303 KASSERT(fli
->fli_trans_cnt
> 0);
304 return (fli
->fli_lock_type
== FSTRANS_EXCL
);
308 * Set new file system state.
311 fstrans_setstate(struct mount
*mp
, enum fstrans_state new_state
)
313 struct fstrans_mount_info
*fmi
;
315 fmi
= mp
->mnt_transinfo
;
318 case FSTRANS_SUSPENDING
:
319 KASSERT(fmi
->fmi_state
== FSTRANS_NORMAL
);
320 fstrans_start(mp
, FSTRANS_EXCL
);
321 fmi
->fmi_state
= FSTRANS_SUSPENDING
;
324 case FSTRANS_SUSPENDED
:
325 KASSERT(fmi
->fmi_state
== FSTRANS_NORMAL
||
326 fmi
->fmi_state
== FSTRANS_SUSPENDING
);
327 KASSERT(fmi
->fmi_state
== FSTRANS_NORMAL
||
328 fstrans_is_owner(mp
));
329 if (fmi
->fmi_state
== FSTRANS_NORMAL
)
330 fstrans_start(mp
, FSTRANS_EXCL
);
331 rw_enter(&fmi
->fmi_lazy_lock
, RW_WRITER
);
332 fmi
->fmi_state
= FSTRANS_SUSPENDED
;
336 KASSERT(fmi
->fmi_state
== FSTRANS_NORMAL
||
337 fstrans_is_owner(mp
));
338 if (fmi
->fmi_state
== FSTRANS_SUSPENDED
)
339 rw_exit(&fmi
->fmi_lazy_lock
);
340 if (fmi
->fmi_state
== FSTRANS_SUSPENDING
||
341 fmi
->fmi_state
== FSTRANS_SUSPENDED
) {
342 fmi
->fmi_state
= FSTRANS_NORMAL
;
348 panic("%s: illegal state %d", __func__
, new_state
);
355 * Get current file system state
358 fstrans_getstate(struct mount
*mp
)
360 struct fstrans_mount_info
*fmi
;
362 fmi
= mp
->mnt_transinfo
;
364 return fmi
->fmi_state
;
368 * Request a filesystem to suspend all operations.
371 vfs_suspend(struct mount
*mp
, int nowait
)
376 if (!mutex_tryenter(&vfs_suspend_lock
))
379 mutex_enter(&vfs_suspend_lock
);
381 mutex_enter(&syncer_mutex
);
383 if ((error
= VFS_SUSPENDCTL(mp
, SUSPEND_SUSPEND
)) != 0) {
384 mutex_exit(&syncer_mutex
);
385 mutex_exit(&vfs_suspend_lock
);
392 * Request a filesystem to resume all operations.
395 vfs_resume(struct mount
*mp
)
398 VFS_SUSPENDCTL(mp
, SUSPEND_RESUME
);
399 mutex_exit(&syncer_mutex
);
400 mutex_exit(&vfs_suspend_lock
);
404 void fstrans_dump(int);
407 fstrans_print_lwp(struct proc
*p
, struct lwp
*l
, int verbose
)
410 struct fstrans_lwp_info
*fli
;
412 snprintf(prefix
, sizeof(prefix
), "%d.%d", p
->p_pid
, l
->l_lid
);
413 for (fli
= _lwp_getspecific_by_lwp(l
, lwp_data_key
);
415 fli
= fli
->fli_succ
) {
416 if (!verbose
&& fli
->fli_trans_cnt
== 0)
418 printf("%-8s", prefix
);
421 if (fli
->fli_mount
!= NULL
)
422 printf(" (%s)", fli
->fli_mount
->mnt_stat
.f_mntonname
);
425 switch (fli
->fli_lock_type
) {
436 printf(" %#x", fli
->fli_lock_type
);
439 printf(" %d\n", fli
->fli_trans_cnt
);
445 fstrans_print_mount(struct mount
*mp
, int verbose
)
447 struct fstrans_mount_info
*fmi
;
449 fmi
= mp
->mnt_transinfo
;
450 if (!verbose
&& (fmi
== NULL
|| fmi
->fmi_state
== FSTRANS_NORMAL
))
453 printf("%-16s ", mp
->mnt_stat
.f_mntonname
);
458 switch (fmi
->fmi_state
) {
460 printf("state normal\n");
462 case FSTRANS_SUSPENDING
:
463 printf("state suspending\n");
465 case FSTRANS_SUSPENDED
:
466 printf("state suspended\n");
469 printf("state %#x\n", fmi
->fmi_state
);
472 printf("%16s r=%d w=%d\n", "lock_lazy:",
473 rw_read_held(&fmi
->fmi_lazy_lock
),
474 rw_write_held(&fmi
->fmi_lazy_lock
));
475 printf("%16s r=%d w=%d\n", "lock_shared:",
476 rw_read_held(&fmi
->fmi_shared_lock
),
477 rw_write_held(&fmi
->fmi_shared_lock
));
481 fstrans_dump(int full
)
483 const struct proclist_desc
*pd
;
488 printf("Fstrans locks by lwp:\n");
489 for (pd
= proclists
; pd
->pd_list
!= NULL
; pd
++)
490 PROCLIST_FOREACH(p
, pd
->pd_list
)
491 LIST_FOREACH(l
, &p
->p_lwps
, l_sibling
)
492 fstrans_print_lwp(p
, l
, full
== 1);
494 printf("Fstrans state by mount:\n");
495 CIRCLEQ_FOREACH(mp
, &mountlist
, mnt_list
)
496 fstrans_print_mount(mp
, full
== 1);
498 #endif /* defined(DDB) */
501 fscow_establish(struct mount
*mp
, int (*func
)(void *, struct buf
*, bool),
504 struct fstrans_mount_info
*fmi
;
505 struct fscow_handler
*new;
507 if ((mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
510 fmi
= mp
->mnt_transinfo
;
512 if ((new = kmem_alloc(sizeof(*new), KM_SLEEP
)) == NULL
)
516 rw_enter(&fmi
->fmi_cow_lock
, RW_WRITER
);
517 SLIST_INSERT_HEAD(&fmi
->fmi_cow_handler
, new, ch_list
);
518 rw_exit(&fmi
->fmi_cow_lock
);
524 fscow_disestablish(struct mount
*mp
, int (*func
)(void *, struct buf
*, bool),
527 struct fstrans_mount_info
*fmi
;
528 struct fscow_handler
*hp
= NULL
;
530 if ((mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
533 fmi
= mp
->mnt_transinfo
;
535 rw_enter(&fmi
->fmi_cow_lock
, RW_WRITER
);
536 SLIST_FOREACH(hp
, &fmi
->fmi_cow_handler
, ch_list
)
537 if (hp
->ch_func
== func
&& hp
->ch_arg
== arg
)
540 SLIST_REMOVE(&fmi
->fmi_cow_handler
, hp
, fscow_handler
, ch_list
);
541 kmem_free(hp
, sizeof(*hp
));
543 rw_exit(&fmi
->fmi_cow_lock
);
545 return hp
? 0 : EINVAL
;
549 fscow_run(struct buf
*bp
, bool data_valid
)
553 struct fstrans_lwp_info
*fli
;
554 struct fstrans_mount_info
*fmi
;
555 struct fscow_handler
*hp
;
557 if ((bp
->b_flags
& B_COWDONE
))
559 if (bp
->b_vp
== NULL
)
561 if (bp
->b_vp
->v_type
== VBLK
)
562 mp
= bp
->b_vp
->v_specmountpoint
;
564 mp
= bp
->b_vp
->v_mount
;
565 if (mp
== NULL
|| (mp
->mnt_iflag
& IMNT_HAS_TRANS
) == 0)
568 fli
= fstrans_get_lwp_info(mp
);
569 fmi
= mp
->mnt_transinfo
;
571 if (fli
->fli_cow_cnt
++ == 0)
572 rw_enter(&fmi
->fmi_cow_lock
, RW_READER
);
574 SLIST_FOREACH(hp
, &fmi
->fmi_cow_handler
, ch_list
)
575 if ((error
= (*hp
->ch_func
)(hp
->ch_arg
, bp
, data_valid
)) != 0)
578 if (--fli
->fli_cow_cnt
== 0)
579 rw_exit(&fmi
->fmi_cow_lock
);
583 bp
->b_flags
|= B_COWDONE
;