summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/fs/vnode.c36
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_pool.c10
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_pool.h5
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vnops.c21
-rw-r--r--usr/src/uts/common/fs/zfs/zil.c2
-rw-r--r--usr/src/uts/common/sys/vnode.h8
6 files changed, 73 insertions, 9 deletions
diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c
index 5c1f8a96b6..06f9177101 100644
--- a/usr/src/uts/common/fs/vnode.c
+++ b/usr/src/uts/common/fs/vnode.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -37,8 +37,6 @@
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/t_lock.h>
@@ -66,6 +64,7 @@
#include <sys/nbmlock.h>
#include <sys/fcntl.h>
#include <fs/fs_subr.h>
+#include <sys/taskq.h>
/* Determine if this vnode is a file that is read-only */
#define ISROFILE(vp) \
@@ -869,6 +868,37 @@ vn_rele_stream(vnode_t *vp)
mutex_exit(&vp->v_lock);
}
+static void
+vn_rele_inactive(vnode_t *vp)
+{
+ VOP_INACTIVE(vp, CRED(), NULL);
+}
+
+/*
+ * Like vn_rele() except if we are going to call VOP_INACTIVE() then do it
+ * asynchronously using a taskq. This can avoid deadlocks caused by re-entering
+ * the file system as a result of releasing the vnode. Note, file systems
+ * already have to handle the race where the vnode is incremented before the
+ * inactive routine is called and does its locking.
+ *
+ * Warning: Excessive use of this routine can lead to performance problems.
+ * This is because taskqs throttle back allocation if too many are created.
+ */
+void
+vn_rele_async(vnode_t *vp, taskq_t *taskq)
+{
+ VERIFY(vp->v_count > 0);
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count == 1) {
+ mutex_exit(&vp->v_lock);
+ VERIFY(taskq_dispatch(taskq, (task_func_t *)vn_rele_inactive,
+ vp, TQ_SLEEP) != NULL);
+ return;
+ }
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+}
+
int
vn_open(
char *pnamep,
diff --git a/usr/src/uts/common/fs/zfs/dsl_pool.c b/usr/src/uts/common/fs/zfs/dsl_pool.c
index 35ad2e2213..025e0eb544 100644
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c
@@ -90,6 +90,9 @@ dsl_pool_open_impl(spa_t *spa, uint64_t txg)
mutex_init(&dp->dp_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&dp->dp_scrub_cancel_lock, NULL, MUTEX_DEFAULT, NULL);
+ dp->dp_vnrele_taskq = taskq_create("zfs_vn_rele_taskq", 1, minclsyspri,
+ 1, 4, 0);
+
return (dp);
}
@@ -227,6 +230,7 @@ dsl_pool_close(dsl_pool_t *dp)
rw_destroy(&dp->dp_config_rwlock);
mutex_destroy(&dp->dp_lock);
mutex_destroy(&dp->dp_scrub_cancel_lock);
+ taskq_destroy(dp->dp_vnrele_taskq);
if (dp->dp_blkstats)
kmem_free(dp->dp_blkstats, sizeof (zfs_all_blkstats_t));
kmem_free(dp, sizeof (dsl_pool_t));
@@ -612,3 +616,9 @@ dsl_pool_create_origin(dsl_pool_t *dp, dmu_tx_t *tx)
dsl_dataset_rele(ds, FTAG);
rw_exit(&dp->dp_config_rwlock);
}
+
+taskq_t *
+dsl_pool_vnrele_taskq(dsl_pool_t *dp)
+{
+ return (dp->dp_vnrele_taskq);
+}
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_pool.h b/usr/src/uts/common/fs/zfs/sys/dsl_pool.h
index 3bb4ad4efe..d8da295f33 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_pool.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_pool.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -77,6 +77,7 @@ typedef struct dsl_pool {
struct dsl_dir *dp_mos_dir;
struct dsl_dataset *dp_origin_snap;
uint64_t dp_root_dir_obj;
+ struct taskq *dp_vnrele_taskq;
/* No lock needed - sync context only */
blkptr_t dp_meta_rootbp;
@@ -143,6 +144,8 @@ int dsl_pool_scrub_clean(dsl_pool_t *dp);
void dsl_pool_scrub_sync(dsl_pool_t *dp, dmu_tx_t *tx);
void dsl_pool_scrub_restart(dsl_pool_t *dp);
+taskq_t *dsl_pool_vnrele_taskq(dsl_pool_t *dp);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c
index 831504b44d..20b8a6d658 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c
@@ -101,6 +101,7 @@
* pushing cached pages (which acquires range locks) and syncing out
* cached atime changes. Third, zfs_zinactive() may require a new tx,
* which could deadlock the system if you were already holding one.
+ * If you must call VN_RELE() within a tx then use VN_RELE_ASYNC().
*
* (3) All range locks must be grabbed before calling dmu_tx_assign(),
* as they can span dmu_tx_assign() calls.
@@ -791,10 +792,15 @@ zfs_get_done(dmu_buf_t *db, void *vzgd)
zgd_t *zgd = (zgd_t *)vzgd;
rl_t *rl = zgd->zgd_rl;
vnode_t *vp = ZTOV(rl->r_zp);
+ objset_t *os = rl->r_zp->z_zfsvfs->z_os;
dmu_buf_rele(db, vzgd);
zfs_range_unlock(rl);
- VN_RELE(vp);
+ /*
+ * Release the vnode asynchronously as we currently have the
+ * txg stopped from syncing.
+ */
+ VN_RELE_ASYNC(vp, dsl_pool_vnrele_taskq(dmu_objset_pool(os)));
zil_add_block(zgd->zgd_zilog, zgd->zgd_bp);
kmem_free(zgd, sizeof (zgd_t));
}
@@ -824,7 +830,12 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
if (zfs_zget(zfsvfs, lr->lr_foid, &zp) != 0)
return (ENOENT);
if (zp->z_unlinked) {
- VN_RELE(ZTOV(zp));
+ /*
+ * Release the vnode asynchronously as we currently have the
+ * txg stopped from syncing.
+ */
+ VN_RELE_ASYNC(ZTOV(zp),
+ dsl_pool_vnrele_taskq(dmu_objset_pool(os)));
return (ENOENT);
}
@@ -896,7 +907,11 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
}
out:
zfs_range_unlock(rl);
- VN_RELE(ZTOV(zp));
+ /*
+ * Release the vnode asynchronously as we currently have the
+ * txg stopped from syncing.
+ */
+ VN_RELE_ASYNC(ZTOV(zp), dsl_pool_vnrele_taskq(dmu_objset_pool(os)));
return (error);
}
diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c
index 2a1f82a1be..7cccf6c7d7 100644
--- a/usr/src/uts/common/fs/zfs/zil.c
+++ b/usr/src/uts/common/fs/zfs/zil.c
@@ -1043,7 +1043,7 @@ zil_clean(zilog_t *zilog)
if ((itx != NULL) &&
(itx->itx_lr.lrc_txg <= spa_last_synced_txg(zilog->zl_spa))) {
(void) taskq_dispatch(zilog->zl_clean_taskq,
- (void (*)(void *))zil_itx_clean, zilog, TQ_NOSLEEP);
+ (task_func_t *)zil_itx_clean, zilog, TQ_SLEEP);
}
mutex_exit(&zilog->zl_lock);
}
diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h
index 5ec7dad756..947144b841 100644
--- a/usr/src/uts/common/sys/vnode.h
+++ b/usr/src/uts/common/sys/vnode.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -778,6 +778,7 @@ struct page;
struct seg;
struct as;
struct pollhead;
+struct taskq;
#ifdef _KERNEL
@@ -1206,6 +1207,7 @@ int vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, ssize_t len,
offset_t offset, enum uio_seg seg, int ioflag, rlim64_t ulimit,
cred_t *cr, ssize_t *residp);
void vn_rele(struct vnode *vp);
+void vn_rele_async(struct vnode *vp, struct taskq *taskq);
void vn_rele_dnlc(struct vnode *vp);
void vn_rele_stream(struct vnode *vp);
int vn_link(char *from, char *to, enum uio_seg seg);
@@ -1284,6 +1286,10 @@ extern uint_t pvn_vmodsort_supported;
vn_rele(vp); \
}
+#define VN_RELE_ASYNC(vp, taskq) { \
+ vn_rele_async(vp, taskq); \
+}
+
#define VN_SET_VFS_TYPE_DEV(vp, vfsp, type, dev) { \
(vp)->v_vfsp = (vfsp); \
(vp)->v_type = (type); \