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 <behlendorf1@llnl.gov> Signed-off-by: Richard Yao <ryao@gentoo.org> Closes #457
This commit is contained in:
parent
4dd18932ba
commit
0a50679ce9
|
@ -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_dirty_inode(struct inode *ip, int flags);
|
||||||
extern int zfs_map(struct inode *ip, offset_t off, caddr_t *addrp,
|
extern int zfs_map(struct inode *ip, offset_t off, caddr_t *addrp,
|
||||||
size_t len, unsigned long vm_flags);
|
size_t len, unsigned long vm_flags);
|
||||||
|
extern void zfs_iput_async(struct inode *ip);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include <sys/policy.h>
|
#include <sys/policy.h>
|
||||||
#include <sys/zfs_dir.h>
|
#include <sys/zfs_dir.h>
|
||||||
#include <sys/zfs_acl.h>
|
#include <sys/zfs_acl.h>
|
||||||
|
#include <sys/zfs_vnops.h>
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
#include "fs/fs_subr.h"
|
#include "fs/fs_subr.h"
|
||||||
#include <sys/zap.h>
|
#include <sys/zap.h>
|
||||||
|
@ -528,7 +529,7 @@ zfs_purgedir(znode_t *dzp)
|
||||||
error = dmu_tx_assign(tx, TXG_WAIT);
|
error = dmu_tx_assign(tx, TXG_WAIT);
|
||||||
if (error) {
|
if (error) {
|
||||||
dmu_tx_abort(tx);
|
dmu_tx_abort(tx);
|
||||||
iput(ZTOI(xzp));
|
zfs_iput_async(ZTOI(xzp));
|
||||||
skipped += 1;
|
skipped += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -541,7 +542,7 @@ zfs_purgedir(znode_t *dzp)
|
||||||
skipped += 1;
|
skipped += 1;
|
||||||
dmu_tx_commit(tx);
|
dmu_tx_commit(tx);
|
||||||
|
|
||||||
iput(ZTOI(xzp));
|
zfs_iput_async(ZTOI(xzp));
|
||||||
}
|
}
|
||||||
zap_cursor_fini(&zc);
|
zap_cursor_fini(&zc);
|
||||||
if (error != ENOENT)
|
if (error != ENOENT)
|
||||||
|
@ -729,7 +730,7 @@ zfs_rmnode(znode_t *zp)
|
||||||
dmu_tx_commit(tx);
|
dmu_tx_commit(tx);
|
||||||
out:
|
out:
|
||||||
if (xzp)
|
if (xzp)
|
||||||
iput(ZTOI(xzp));
|
zfs_iput_async(ZTOI(xzp));
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
* pushing cached pages (which acquires range locks) and syncing out
|
* pushing cached pages (which acquires range locks) and syncing out
|
||||||
* cached atime changes. Third, zfs_zinactive() may require a new tx,
|
* cached atime changes. Third, zfs_zinactive() may require a new tx,
|
||||||
* which could deadlock the system if you were already holding one.
|
* 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(),
|
* (3) All range locks must be grabbed before calling dmu_tx_assign(),
|
||||||
* as they can span dmu_tx_assign() calls.
|
* 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);
|
EXPORT_SYMBOL(zfs_write);
|
||||||
|
|
||||||
static void
|
void
|
||||||
iput_async(struct inode *ip, taskq_t *taskq)
|
zfs_iput_async(struct inode *ip)
|
||||||
{
|
{
|
||||||
|
objset_t *os = ITOZSB(ip)->z_os;
|
||||||
|
|
||||||
ASSERT(atomic_read(&ip->i_count) > 0);
|
ASSERT(atomic_read(&ip->i_count) > 0);
|
||||||
|
ASSERT(os != NULL);
|
||||||
|
|
||||||
if (atomic_read(&ip->i_count) == 1)
|
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
|
else
|
||||||
iput(ip);
|
iput(ip);
|
||||||
}
|
}
|
||||||
|
@ -941,7 +946,6 @@ void
|
||||||
zfs_get_done(zgd_t *zgd, int error)
|
zfs_get_done(zgd_t *zgd, int error)
|
||||||
{
|
{
|
||||||
znode_t *zp = zgd->zgd_private;
|
znode_t *zp = zgd->zgd_private;
|
||||||
objset_t *os = ZTOZSB(zp)->z_os;
|
|
||||||
|
|
||||||
if (zgd->zgd_db)
|
if (zgd->zgd_db)
|
||||||
dmu_buf_rele(zgd->zgd_db, zgd);
|
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
|
* Release the vnode asynchronously as we currently have the
|
||||||
* txg stopped from syncing.
|
* 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)
|
if (error == 0 && zgd->zgd_bp)
|
||||||
zil_add_block(zgd->zgd_zilog, 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
|
* Release the vnode asynchronously as we currently have the
|
||||||
* txg stopped from syncing.
|
* 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));
|
return (SET_ERROR(ENOENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ zfs_inode_destroy(struct inode *ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zp->z_xattr_parent) {
|
if (zp->z_xattr_parent) {
|
||||||
iput(ZTOI(zp->z_xattr_parent));
|
zfs_iput_async(ZTOI(zp->z_xattr_parent));
|
||||||
zp->z_xattr_parent = NULL;
|
zp->z_xattr_parent = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue