summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2014-08-04 13:30:20 -0700
committerBrian Behlendorf <[email protected]>2014-08-11 16:11:43 -0700
commit0a50679ce9eb8ded7bf20685e9d32724ded9cb8d (patch)
tree0087b2570d24eb85e710051c4c3721bc22469069
parent4dd18932ba4cfdcf9b16609f6a80c2d6c239cb15 (diff)
Add zfs_iput_async() interface
Handle all iputs in zfs_purgedir() and zfs_inode_destroy() asynchronously to prevent deadlocks. When the iputs are allowed to run synchronously in the destroy call path deadlocks between xattr directory inodes and their parent file inodes are possible. Signed-off-by: Brian Behlendorf <[email protected]> Signed-off-by: Richard Yao <[email protected]> Closes #457
-rw-r--r--include/sys/zfs_vnops.h1
-rw-r--r--module/zfs/zfs_dir.c7
-rw-r--r--module/zfs/zfs_vnops.c18
-rw-r--r--module/zfs/zfs_znode.c2
4 files changed, 17 insertions, 11 deletions
diff --git a/include/sys/zfs_vnops.h b/include/sys/zfs_vnops.h
index c9fecf8ba..c331035c5 100644
--- a/include/sys/zfs_vnops.h
+++ b/include/sys/zfs_vnops.h
@@ -79,6 +79,7 @@ extern int zfs_putpage(struct inode *ip, struct page *pp,
extern int zfs_dirty_inode(struct inode *ip, int flags);
extern int zfs_map(struct inode *ip, offset_t off, caddr_t *addrp,
size_t len, unsigned long vm_flags);
+extern void zfs_iput_async(struct inode *ip);
#ifdef __cplusplus
}
diff --git a/module/zfs/zfs_dir.c b/module/zfs/zfs_dir.c
index 448a8727e..d2b5bc408 100644
--- a/module/zfs/zfs_dir.c
+++ b/module/zfs/zfs_dir.c
@@ -46,6 +46,7 @@
#include <sys/policy.h>
#include <sys/zfs_dir.h>
#include <sys/zfs_acl.h>
+#include <sys/zfs_vnops.h>
#include <sys/fs/zfs.h>
#include "fs/fs_subr.h"
#include <sys/zap.h>
@@ -528,7 +529,7 @@ zfs_purgedir(znode_t *dzp)
error = dmu_tx_assign(tx, TXG_WAIT);
if (error) {
dmu_tx_abort(tx);
- iput(ZTOI(xzp));
+ zfs_iput_async(ZTOI(xzp));
skipped += 1;
continue;
}
@@ -541,7 +542,7 @@ zfs_purgedir(znode_t *dzp)
skipped += 1;
dmu_tx_commit(tx);
- iput(ZTOI(xzp));
+ zfs_iput_async(ZTOI(xzp));
}
zap_cursor_fini(&zc);
if (error != ENOENT)
@@ -729,7 +730,7 @@ zfs_rmnode(znode_t *zp)
dmu_tx_commit(tx);
out:
if (xzp)
- iput(ZTOI(xzp));
+ zfs_iput_async(ZTOI(xzp));
}
static uint64_t
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 4f531733e..18b2564a2 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -101,7 +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 iput() within a tx then use iput_ASYNC().
+ * If you must call iput() within a tx then use zfs_iput_async().
*
* (3) All range locks must be grabbed before calling dmu_tx_assign(),
* as they can span dmu_tx_assign() calls.
@@ -927,12 +927,17 @@ zfs_write(struct inode *ip, uio_t *uio, int ioflag, cred_t *cr)
}
EXPORT_SYMBOL(zfs_write);
-static void
-iput_async(struct inode *ip, taskq_t *taskq)
+void
+zfs_iput_async(struct inode *ip)
{
+ objset_t *os = ITOZSB(ip)->z_os;
+
ASSERT(atomic_read(&ip->i_count) > 0);
+ ASSERT(os != NULL);
+
if (atomic_read(&ip->i_count) == 1)
- taskq_dispatch(taskq, (task_func_t *)iput, ip, TQ_PUSHPAGE);
+ taskq_dispatch(dsl_pool_iput_taskq(dmu_objset_pool(os)),
+ (task_func_t *)iput, ip, TQ_PUSHPAGE);
else
iput(ip);
}
@@ -941,7 +946,6 @@ void
zfs_get_done(zgd_t *zgd, int error)
{
znode_t *zp = zgd->zgd_private;
- objset_t *os = ZTOZSB(zp)->z_os;
if (zgd->zgd_db)
dmu_buf_rele(zgd->zgd_db, zgd);
@@ -952,7 +956,7 @@ zfs_get_done(zgd_t *zgd, int error)
* Release the vnode asynchronously as we currently have the
* txg stopped from syncing.
*/
- iput_async(ZTOI(zp), dsl_pool_iput_taskq(dmu_objset_pool(os)));
+ zfs_iput_async(ZTOI(zp));
if (error == 0 && zgd->zgd_bp)
zil_add_block(zgd->zgd_zilog, zgd->zgd_bp);
@@ -994,7 +998,7 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
* Release the vnode asynchronously as we currently have the
* txg stopped from syncing.
*/
- iput_async(ZTOI(zp), dsl_pool_iput_taskq(dmu_objset_pool(os)));
+ zfs_iput_async(ZTOI(zp));
return (SET_ERROR(ENOENT));
}
diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c
index 4403e3289..5fcb9e930 100644
--- a/module/zfs/zfs_znode.c
+++ b/module/zfs/zfs_znode.c
@@ -292,7 +292,7 @@ zfs_inode_destroy(struct inode *ip)
}
if (zp->z_xattr_parent) {
- iput(ZTOI(zp->z_xattr_parent));
+ zfs_iput_async(ZTOI(zp->z_xattr_parent));
zp->z_xattr_parent = NULL;
}