Fix a deadlock where one thread exits, enters fstrans_lwp_dtor()
and wants fstrans_lock. This thread holds the proc_lock. Another thread holds fstrans_lock and runs pserialize_perform(). As the first thread holds the proc_lock, timeouts are blocked and the second thread blocks forever in kpause(). Change fstrans_lwp_dtor() to invalidate, but not free its info structs. No need to take fstrans_lock. Change fstrans_get_lwp_info() to reuse invalidated info before trying to allocate a new one.
This commit is contained in:
parent
05b3bfa0ba
commit
2f1e07219a
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: vfs_trans.c,v 1.29 2013/11/23 13:35:36 christos Exp $ */
|
||||
/* $NetBSD: vfs_trans.c,v 1.30 2014/04/15 09:50:45 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.29 2013/11/23 13:35:36 christos Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.30 2014/04/15 09:50:45 hannken Exp $");
|
||||
|
||||
/*
|
||||
* File system transaction operations.
|
||||
|
@ -82,7 +82,6 @@ static kcondvar_t fstrans_count_cv; /* Fstrans or cow count changed. */
|
|||
static pserialize_t fstrans_psz; /* Pserialize state. */
|
||||
static LIST_HEAD(fstrans_lwp_head, fstrans_lwp_info) fstrans_fli_head;
|
||||
/* List of all fstrans_lwp_info. */
|
||||
static pool_cache_t fstrans_cache; /* Pool of struct fstrans_lwp_info. */
|
||||
|
||||
static void fstrans_lwp_dtor(void *);
|
||||
static void fstrans_mount_dtor(struct mount *);
|
||||
|
@ -110,8 +109,6 @@ fstrans_init(void)
|
|||
cv_init(&fstrans_count_cv, "fstcnt");
|
||||
fstrans_psz = pserialize_create();
|
||||
LIST_INIT(&fstrans_fli_head);
|
||||
fstrans_cache = pool_cache_init(sizeof(struct fstrans_lwp_info), 0, 0,
|
||||
0, "fstrans", NULL, IPL_NONE, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -122,17 +119,16 @@ fstrans_lwp_dtor(void *arg)
|
|||
{
|
||||
struct fstrans_lwp_info *fli, *fli_next;
|
||||
|
||||
mutex_enter(&fstrans_lock);
|
||||
for (fli = arg; fli; fli = fli_next) {
|
||||
KASSERT(fli->fli_trans_cnt == 0);
|
||||
KASSERT(fli->fli_cow_cnt == 0);
|
||||
if (fli->fli_mount != NULL)
|
||||
fstrans_mount_dtor(fli->fli_mount);
|
||||
fli_next = fli->fli_succ;
|
||||
LIST_REMOVE(fli, fli_list);
|
||||
pool_cache_put(fstrans_cache, fli);
|
||||
fli->fli_mount = NULL;
|
||||
membar_sync();
|
||||
fli->fli_self = NULL;
|
||||
}
|
||||
mutex_exit(&fstrans_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -237,7 +233,21 @@ fstrans_get_lwp_info(struct mount *mp, bool do_alloc)
|
|||
}
|
||||
}
|
||||
if (fli == NULL) {
|
||||
fli = pool_cache_get(fstrans_cache, PR_WAITOK);
|
||||
mutex_enter(&fstrans_lock);
|
||||
LIST_FOREACH(fli, &fstrans_fli_head, fli_list) {
|
||||
if (fli->fli_self == NULL) {
|
||||
KASSERT(fli->fli_trans_cnt == 0);
|
||||
KASSERT(fli->fli_cow_cnt == 0);
|
||||
fli->fli_self = curlwp;
|
||||
fli->fli_succ = lwp_getspecific(lwp_data_key);
|
||||
lwp_setspecific(lwp_data_key, fli);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_exit(&fstrans_lock);
|
||||
}
|
||||
if (fli == NULL) {
|
||||
fli = kmem_alloc(sizeof(*fli), KM_SLEEP);
|
||||
mutex_enter(&fstrans_lock);
|
||||
memset(fli, 0, sizeof(*fli));
|
||||
fli->fli_self = curlwp;
|
||||
|
|
Loading…
Reference in New Issue