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.
@ -30,7 +30,7 @@
*/
#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.
@ -63,7 +63,8 @@ struct fscow_handler {
struct fstrans_lwp_info {
struct fstrans_lwp_info *fli_succ;
struct mount *fli_mount;
int fli_count;
int fli_trans_cnt;
int fli_cow_cnt;
enum fstrans_lock_type fli_lock_type;
};
struct fstrans_mount_info {
@ -81,6 +82,7 @@ POOL_INIT(fstrans_pl, sizeof(struct fstrans_lwp_info), 0, 0, 0,
"fstrans", NULL, IPL_NONE);
static void fstrans_lwp_dtor(void *);
static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *);
/*
* Initialize
@ -105,8 +107,8 @@ fstrans_lwp_dtor(void *arg)
struct fstrans_lwp_info *fli, *fli_next;
for (fli = arg; fli; fli = fli_next) {
KASSERT(fli->fli_mount == NULL);
KASSERT(fli->fli_count == 0);
KASSERT(fli->fli_trans_cnt == 0);
KASSERT(fli->fli_cow_cnt == 0);
fli_next = fli->fli_succ;
pool_put(&fstrans_pl, fli);
}
@ -155,6 +157,39 @@ fstrans_unmount(struct mount *mp)
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
* 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;
krw_t lock_op;
struct fstrans_lwp_info *fli, *new_fli;
struct fstrans_lwp_info *fli;
struct fstrans_mount_info *fmi;
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)
return 0;
new_fli = NULL;
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;
}
}
fli = fstrans_get_lwp_info(mp);
if (new_fli == NULL) {
new_fli = pool_get(&fstrans_pl, PR_WAITOK);
new_fli->fli_mount = NULL;
new_fli->fli_count = 0;
new_fli->fli_succ = lwp_getspecific(lwp_data_key);
lwp_setspecific(lwp_data_key, new_fli);
if (fli->fli_trans_cnt > 0) {
if (fli->fli_lock_type != FSTRANS_EXCL &&
lock_type == FSTRANS_EXCL)
panic("fstrans_start: cannot upgrade lock");
fli->fli_trans_cnt += 1;
return 0;
}
KASSERT(new_fli->fli_mount == NULL);
KASSERT(new_fli->fli_count == 0);
fmi = mp->mnt_transinfo;
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)
return EBUSY;
new_fli->fli_mount = mp;
new_fli->fli_count = 1;
new_fli->fli_lock_type = lock_type;
fli->fli_trans_cnt = 1;
fli->fli_lock_type = lock_type;
return 0;
}
@ -234,8 +253,8 @@ fstrans_done(struct mount *mp)
for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
if (fli->fli_mount == mp) {
fli->fli_count -= 1;
if (fli->fli_count > 0)
fli->fli_trans_cnt -= 1;
if (fli->fli_trans_cnt > 0)
return;
break;
}
@ -243,8 +262,8 @@ fstrans_done(struct mount *mp)
KASSERT(fli != NULL);
KASSERT(fli->fli_mount == mp);
KASSERT(fli->fli_count == 0);
fli->fli_mount = NULL;
KASSERT(fli->fli_trans_cnt == 0);
fmi = mp->mnt_transinfo;
KASSERT(fmi != NULL);
if (fli->fli_lock_type == FSTRANS_LAZY)
@ -270,11 +289,11 @@ fstrans_is_owner(struct mount *mp)
if (fli->fli_mount == mp)
break;
if (fli == NULL)
if (fli == NULL || fli->fli_trans_cnt == 0)
return 0;
KASSERT(fli->fli_mount == mp);
KASSERT(fli->fli_count > 0);
KASSERT(fli->fli_trans_cnt > 0);
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);
fli;
fli = fli->fli_succ) {
if (!verbose && fli->fli_count == 0)
if (!verbose && fli->fli_trans_cnt == 0)
continue;
printf("%-8s", prefix);
if (verbose)
@ -410,7 +429,7 @@ fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose)
printf(" %#x", fli->fli_lock_type);
break;
}
printf(" %d\n", fli->fli_count);
printf(" %d\n", fli->fli_trans_cnt);
prefix[0] = '\0';
}
}
@ -524,9 +543,12 @@ fscow_run(struct buf *bp, bool data_valid)
{
int error = 0;
struct mount *mp;
struct fstrans_lwp_info *fli;
struct fstrans_mount_info *fmi;
struct fscow_handler *hp;
KASSERT(ISSET(bp->b_cflags, BC_BUSY));
if ((bp->b_flags & B_COWDONE))
goto done;
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)
goto done;
fli = fstrans_get_lwp_info(mp);
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)
if ((error = (*hp->ch_func)(hp->ch_arg, bp, data_valid)) != 0)
break;
rw_exit(&fmi->fmi_cow_lock);
if (--fli->fli_cow_cnt == 0)
rw_exit(&fmi->fmi_cow_lock);
done:
if (error == 0)