Fscow_run() may recurse into itself.

Take care by adding a per-lwp recursion counter.
This commit is contained in:
hannken 2008-05-16 09:01:56 +00:00
parent e369394198
commit 961a5d4bcb
1 changed files with 68 additions and 41 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_trans.c,v 1.19 2008/04/28 20:24:05 martin Exp $ */ /* $NetBSD: vfs_trans.c,v 1.20 2008/05/16 09:01:56 hannken Exp $ */
/*- /*-
* Copyright (c) 2007 The NetBSD Foundation, Inc. * Copyright (c) 2007 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.19 2008/04/28 20:24:05 martin Exp $"); __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.20 2008/05/16 09:01:56 hannken Exp $");
/* /*
* File system transaction operations. * File system transaction operations.
@ -63,7 +63,8 @@ struct fscow_handler {
struct fstrans_lwp_info { struct fstrans_lwp_info {
struct fstrans_lwp_info *fli_succ; struct fstrans_lwp_info *fli_succ;
struct mount *fli_mount; struct mount *fli_mount;
int fli_count; int fli_trans_cnt;
int fli_cow_cnt;
enum fstrans_lock_type fli_lock_type; enum fstrans_lock_type fli_lock_type;
}; };
struct fstrans_mount_info { struct fstrans_mount_info {
@ -81,6 +82,7 @@ POOL_INIT(fstrans_pl, sizeof(struct fstrans_lwp_info), 0, 0, 0,
"fstrans", NULL, IPL_NONE); "fstrans", NULL, IPL_NONE);
static void fstrans_lwp_dtor(void *); static void fstrans_lwp_dtor(void *);
static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *);
/* /*
* Initialize * Initialize
@ -105,8 +107,8 @@ fstrans_lwp_dtor(void *arg)
struct fstrans_lwp_info *fli, *fli_next; struct fstrans_lwp_info *fli, *fli_next;
for (fli = arg; fli; fli = fli_next) { for (fli = arg; fli; fli = fli_next) {
KASSERT(fli->fli_mount == NULL); KASSERT(fli->fli_trans_cnt == 0);
KASSERT(fli->fli_count == 0); KASSERT(fli->fli_cow_cnt == 0);
fli_next = fli->fli_succ; fli_next = fli->fli_succ;
pool_put(&fstrans_pl, fli); pool_put(&fstrans_pl, fli);
} }
@ -155,6 +157,39 @@ fstrans_unmount(struct mount *mp)
mp->mnt_transinfo = NULL; mp->mnt_transinfo = NULL;
} }
/*
* Retrieve the per lwp info for this mount
*/
static struct fstrans_lwp_info *
fstrans_get_lwp_info(struct mount *mp)
{
struct fstrans_lwp_info *fli, *new_fli;
new_fli = NULL;
for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
if (fli->fli_mount == mp)
return fli;
else if (fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0 &&
new_fli == NULL)
new_fli = fli;
}
if (new_fli == NULL) {
new_fli = pool_get(&fstrans_pl, PR_WAITOK);
new_fli->fli_trans_cnt = 0;
new_fli->fli_cow_cnt = 0;
new_fli->fli_succ = lwp_getspecific(lwp_data_key);
lwp_setspecific(lwp_data_key, new_fli);
}
KASSERT(new_fli->fli_trans_cnt == 0);
KASSERT(new_fli->fli_cow_cnt == 0);
new_fli->fli_mount = mp;
return new_fli;
}
/* /*
* Start a transaction. If this thread already has a transaction on this * Start a transaction. If this thread already has a transaction on this
* file system increment the reference counter. * file system increment the reference counter.
@ -167,7 +202,7 @@ _fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait)
{ {
krwlock_t *lock_p; krwlock_t *lock_p;
krw_t lock_op; krw_t lock_op;
struct fstrans_lwp_info *fli, *new_fli; struct fstrans_lwp_info *fli;
struct fstrans_mount_info *fmi; struct fstrans_mount_info *fmi;
ASSERT_SLEEPABLE(); ASSERT_SLEEPABLE();
@ -175,31 +210,16 @@ _fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait)
if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
return 0; return 0;
new_fli = NULL; fli = fstrans_get_lwp_info(mp);
for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
if (fli->fli_mount == NULL && new_fli == NULL)
new_fli = fli;
if (fli->fli_mount == mp) {
KASSERT(fli->fli_count > 0);
if (fli->fli_lock_type != FSTRANS_EXCL &&
lock_type == FSTRANS_EXCL)
panic("fstrans_start: cannot upgrade lock");
fli->fli_count += 1;
return 0;
}
}
if (new_fli == NULL) { if (fli->fli_trans_cnt > 0) {
new_fli = pool_get(&fstrans_pl, PR_WAITOK); if (fli->fli_lock_type != FSTRANS_EXCL &&
new_fli->fli_mount = NULL; lock_type == FSTRANS_EXCL)
new_fli->fli_count = 0; panic("fstrans_start: cannot upgrade lock");
new_fli->fli_succ = lwp_getspecific(lwp_data_key); fli->fli_trans_cnt += 1;
lwp_setspecific(lwp_data_key, new_fli); return 0;
} }
KASSERT(new_fli->fli_mount == NULL);
KASSERT(new_fli->fli_count == 0);
fmi = mp->mnt_transinfo; fmi = mp->mnt_transinfo;
if (lock_type == FSTRANS_LAZY) if (lock_type == FSTRANS_LAZY)
@ -213,9 +233,8 @@ _fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait)
else if (rw_tryenter(lock_p, lock_op) == 0) else if (rw_tryenter(lock_p, lock_op) == 0)
return EBUSY; return EBUSY;
new_fli->fli_mount = mp; fli->fli_trans_cnt = 1;
new_fli->fli_count = 1; fli->fli_lock_type = lock_type;
new_fli->fli_lock_type = lock_type;
return 0; return 0;
} }
@ -234,8 +253,8 @@ fstrans_done(struct mount *mp)
for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) { for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
if (fli->fli_mount == mp) { if (fli->fli_mount == mp) {
fli->fli_count -= 1; fli->fli_trans_cnt -= 1;
if (fli->fli_count > 0) if (fli->fli_trans_cnt > 0)
return; return;
break; break;
} }
@ -243,8 +262,8 @@ fstrans_done(struct mount *mp)
KASSERT(fli != NULL); KASSERT(fli != NULL);
KASSERT(fli->fli_mount == mp); KASSERT(fli->fli_mount == mp);
KASSERT(fli->fli_count == 0); KASSERT(fli->fli_trans_cnt == 0);
fli->fli_mount = NULL;
fmi = mp->mnt_transinfo; fmi = mp->mnt_transinfo;
KASSERT(fmi != NULL); KASSERT(fmi != NULL);
if (fli->fli_lock_type == FSTRANS_LAZY) if (fli->fli_lock_type == FSTRANS_LAZY)
@ -270,11 +289,11 @@ fstrans_is_owner(struct mount *mp)
if (fli->fli_mount == mp) if (fli->fli_mount == mp)
break; break;
if (fli == NULL) if (fli == NULL || fli->fli_trans_cnt == 0)
return 0; return 0;
KASSERT(fli->fli_mount == mp); KASSERT(fli->fli_mount == mp);
KASSERT(fli->fli_count > 0); KASSERT(fli->fli_trans_cnt > 0);
return (fli->fli_lock_type == FSTRANS_EXCL); return (fli->fli_lock_type == FSTRANS_EXCL);
} }
@ -387,7 +406,7 @@ fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose)
for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key); for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key);
fli; fli;
fli = fli->fli_succ) { fli = fli->fli_succ) {
if (!verbose && fli->fli_count == 0) if (!verbose && fli->fli_trans_cnt == 0)
continue; continue;
printf("%-8s", prefix); printf("%-8s", prefix);
if (verbose) if (verbose)
@ -410,7 +429,7 @@ fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose)
printf(" %#x", fli->fli_lock_type); printf(" %#x", fli->fli_lock_type);
break; break;
} }
printf(" %d\n", fli->fli_count); printf(" %d\n", fli->fli_trans_cnt);
prefix[0] = '\0'; prefix[0] = '\0';
} }
} }
@ -524,9 +543,12 @@ fscow_run(struct buf *bp, bool data_valid)
{ {
int error = 0; int error = 0;
struct mount *mp; struct mount *mp;
struct fstrans_lwp_info *fli;
struct fstrans_mount_info *fmi; struct fstrans_mount_info *fmi;
struct fscow_handler *hp; struct fscow_handler *hp;
KASSERT(ISSET(bp->b_cflags, BC_BUSY));
if ((bp->b_flags & B_COWDONE)) if ((bp->b_flags & B_COWDONE))
goto done; goto done;
if (bp->b_vp == NULL) if (bp->b_vp == NULL)
@ -538,13 +560,18 @@ fscow_run(struct buf *bp, bool data_valid)
if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0) if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
goto done; goto done;
fli = fstrans_get_lwp_info(mp);
fmi = mp->mnt_transinfo; fmi = mp->mnt_transinfo;
rw_enter(&fmi->fmi_cow_lock, RW_READER); if (fli->fli_cow_cnt++ == 0)
rw_enter(&fmi->fmi_cow_lock, RW_READER);
SLIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list) SLIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list)
if ((error = (*hp->ch_func)(hp->ch_arg, bp, data_valid)) != 0) if ((error = (*hp->ch_func)(hp->ch_arg, bp, data_valid)) != 0)
break; break;
rw_exit(&fmi->fmi_cow_lock);
if (--fli->fli_cow_cnt == 0)
rw_exit(&fmi->fmi_cow_lock);
done: done:
if (error == 0) if (error == 0)