Skip to content

Commit

Permalink
Add zfs_iput_async() interface
Browse files Browse the repository at this point in the history
Handle all final iput()'s in the unlink call path asynchronously to
avoid deadlocking.  One example of an observed deadlock is shown
below.

Call Trace:
 [<ffffffffa050adab>] rrw_enter_read+0x7b/0x230 [zfs]
 [<ffffffffa059519e>] zpl_writepages+0x4e/0x1a0 [zfs]
 [<ffffffff81135191>] do_writepages+0x21/0x40
 [<ffffffff811b67dd>] writeback_single_inode+0xdd/0x290
 [<ffffffff811b6ac8>] write_inode_now+0xa8/0x130
 [<ffffffff811a6a88>] generic_detach_inode+0x88/0x1f0
 [<ffffffff811a7b6d>] generic_drop_inode+0x1d/0x80
 [<ffffffff811a69f2>] iput+0x62/0x70
 [<ffffffffa0555dc7>] zfs_purgedir+0x1e7/0x280 [zfs]
 [<ffffffffa05560c5>] zfs_rmnode+0x265/0x410 [zfs]
 [<ffffffffa057d10e>] zfs_zinactive+0xfe/0x200 [zfs]
 [<ffffffffa0577d7f>] zfs_inactive+0x7f/0x370 [zfs]
 [<ffffffffa0596a7e>] zpl_clear_inode+0xe/0x10 [zfs]
 [<ffffffff811a7372>] clear_inode+0xd2/0x160
 [<ffffffff811a7b83>] generic_drop_inode+0x33/0x80
 [<ffffffff811a69f2>] iput+0x62/0x70
 [<ffffffffa055609c>] zfs_rmnode+0x23c/0x410 [zfs]
 [<ffffffffa057d10e>] zfs_zinactive+0xfe/0x200 [zfs]
 [<ffffffffa0577d7f>] zfs_inactive+0x7f/0x370 [zfs]
 [<ffffffffa0596a7e>] zpl_clear_inode+0xe/0x10 [zfs]
 [<ffffffff811a7372>] clear_inode+0xd2/0x160
 [<ffffffffa0596c10>] zpl_inode_delete+0x20/0x30 [zfs]
 [<ffffffff811a7a5e>] generic_delete_inode+0xde/0x1d0
 [<ffffffff811a7bb5>] generic_drop_inode+0x65/0x80
 [<ffffffff811a69f2>] iput+0x62/0x70
 [<ffffffffa03b7519>] taskq_thread+0x269/0x5c0 [spl]
 [<ffffffffa03b72b0>] ? taskq_thread+0x0/0x5c0 [spl]
 [<ffffffff8109b4d6>] kthread+0x96/0xa0

Signed-off-by: Brian Behlendorf <[email protected]>
  • Loading branch information
behlendorf committed Aug 5, 2014
1 parent 26cb948 commit 43cc1b5
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 11 deletions.
1 change: 1 addition & 0 deletions include/sys/zfs_vnops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
7 changes: 4 additions & 3 deletions module/zfs/zfs_dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
18 changes: 11 additions & 7 deletions module/zfs/zfs_vnops.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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));
}

Expand Down
2 changes: 1 addition & 1 deletion module/zfs/zfs_znode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down

0 comments on commit 43cc1b5

Please sign in to comment.