mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-24 18:39:34 +03:00
Revert "Revert "Revert "Fix unlink/xattr deadlock"""
This reverts commit7973e46
which brings the basic flow of the code back in line with the other ZFS implementations. This was possible due to the following related changes.e89260a
Directory xattr znodes hold a reference on their parent6f9548c
Fix deadlock in zfs_zget()0a50679
Add zfs_iput_async() interface4dd1893
Avoid 128K kmem allocations in mzap_upgrade() Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Richard Yao <ryao@gentoo.org> Closes #457 Closes #2058 Closes #2128 Closes #2240
This commit is contained in:
parent
0a50679ce9
commit
0d5c500d6c
@ -484,72 +484,6 @@ zfs_unlinked_add(znode_t *zp, dmu_tx_t *tx)
|
||||
zap_add_int(zsb->z_os, zsb->z_unlinkedobj, zp->z_id, tx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the entire contents of a directory. Return a count
|
||||
* of the number of entries that could not be deleted. If we encounter
|
||||
* an error, return a count of at least one so that the directory stays
|
||||
* in the unlinked set.
|
||||
*
|
||||
* NOTE: this function assumes that the directory is inactive,
|
||||
* so there is no need to lock its entries before deletion.
|
||||
* Also, it assumes the directory contents is *only* regular
|
||||
* files.
|
||||
*/
|
||||
static int
|
||||
zfs_purgedir(znode_t *dzp)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t zap;
|
||||
znode_t *xzp;
|
||||
dmu_tx_t *tx;
|
||||
zfs_sb_t *zsb = ZTOZSB(dzp);
|
||||
zfs_dirlock_t dl;
|
||||
int skipped = 0;
|
||||
int error;
|
||||
|
||||
for (zap_cursor_init(&zc, zsb->z_os, dzp->z_id);
|
||||
(error = zap_cursor_retrieve(&zc, &zap)) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
error = zfs_zget(zsb,
|
||||
ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp);
|
||||
if (error) {
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT(S_ISREG(ZTOI(xzp)->i_mode)||S_ISLNK(ZTOI(xzp)->i_mode));
|
||||
|
||||
tx = dmu_tx_create(zsb->z_os);
|
||||
dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name);
|
||||
dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_zap(tx, zsb->z_unlinkedobj, FALSE, NULL);
|
||||
/* Is this really needed ? */
|
||||
zfs_sa_upgrade_txholds(tx, xzp);
|
||||
error = dmu_tx_assign(tx, TXG_WAIT);
|
||||
if (error) {
|
||||
dmu_tx_abort(tx);
|
||||
zfs_iput_async(ZTOI(xzp));
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
bzero(&dl, sizeof (dl));
|
||||
dl.dl_dzp = dzp;
|
||||
dl.dl_name = zap.za_name;
|
||||
|
||||
error = zfs_link_destroy(&dl, xzp, tx, 0, NULL);
|
||||
if (error)
|
||||
skipped += 1;
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
zfs_iput_async(ZTOI(xzp));
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
if (error != ENOENT)
|
||||
skipped += 1;
|
||||
return (skipped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up any znodes that had no links when we either crashed or
|
||||
* (force) umounted the file system.
|
||||
@ -596,25 +530,78 @@ zfs_unlinked_drain(zfs_sb_t *zsb)
|
||||
continue;
|
||||
|
||||
zp->z_unlinked = B_TRUE;
|
||||
|
||||
/*
|
||||
* If this is an attribute directory, purge its contents.
|
||||
*/
|
||||
if (S_ISDIR(ZTOI(zp)->i_mode) && (zp->z_pflags & ZFS_XATTR)) {
|
||||
/*
|
||||
* We don't need to check the return value of
|
||||
* zfs_purgedir here, because zfs_rmnode will just
|
||||
* return this xattr directory to the unlinked set
|
||||
* until all of its xattrs are gone.
|
||||
*/
|
||||
(void) zfs_purgedir(zp);
|
||||
}
|
||||
|
||||
iput(ZTOI(zp));
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the entire contents of a directory. Return a count
|
||||
* of the number of entries that could not be deleted. If we encounter
|
||||
* an error, return a count of at least one so that the directory stays
|
||||
* in the unlinked set.
|
||||
*
|
||||
* NOTE: this function assumes that the directory is inactive,
|
||||
* so there is no need to lock its entries before deletion.
|
||||
* Also, it assumes the directory contents is *only* regular
|
||||
* files.
|
||||
*/
|
||||
static int
|
||||
zfs_purgedir(znode_t *dzp)
|
||||
{
|
||||
zap_cursor_t zc;
|
||||
zap_attribute_t zap;
|
||||
znode_t *xzp;
|
||||
dmu_tx_t *tx;
|
||||
zfs_sb_t *zsb = ZTOZSB(dzp);
|
||||
zfs_dirlock_t dl;
|
||||
int skipped = 0;
|
||||
int error;
|
||||
|
||||
for (zap_cursor_init(&zc, zsb->z_os, dzp->z_id);
|
||||
(error = zap_cursor_retrieve(&zc, &zap)) == 0;
|
||||
zap_cursor_advance(&zc)) {
|
||||
error = zfs_zget(zsb,
|
||||
ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp);
|
||||
if (error) {
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT(S_ISREG(ZTOI(xzp)->i_mode) ||
|
||||
S_ISLNK(ZTOI(xzp)->i_mode));
|
||||
|
||||
tx = dmu_tx_create(zsb->z_os);
|
||||
dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name);
|
||||
dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_zap(tx, zsb->z_unlinkedobj, FALSE, NULL);
|
||||
/* Is this really needed ? */
|
||||
zfs_sa_upgrade_txholds(tx, xzp);
|
||||
error = dmu_tx_assign(tx, TXG_WAIT);
|
||||
if (error) {
|
||||
dmu_tx_abort(tx);
|
||||
zfs_iput_async(ZTOI(xzp));
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
bzero(&dl, sizeof (dl));
|
||||
dl.dl_dzp = dzp;
|
||||
dl.dl_name = zap.za_name;
|
||||
|
||||
error = zfs_link_destroy(&dl, xzp, tx, 0, NULL);
|
||||
if (error)
|
||||
skipped += 1;
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
zfs_iput_async(ZTOI(xzp));
|
||||
}
|
||||
zap_cursor_fini(&zc);
|
||||
if (error != ENOENT)
|
||||
skipped += 1;
|
||||
return (skipped);
|
||||
}
|
||||
|
||||
void
|
||||
zfs_rmnode(znode_t *zp)
|
||||
{
|
||||
@ -624,7 +611,6 @@ zfs_rmnode(znode_t *zp)
|
||||
dmu_tx_t *tx;
|
||||
uint64_t acl_obj;
|
||||
uint64_t xattr_obj;
|
||||
uint64_t count;
|
||||
int error;
|
||||
|
||||
ASSERT(zp->z_links == 0);
|
||||
@ -634,27 +620,13 @@ zfs_rmnode(znode_t *zp)
|
||||
* If this is an attribute directory, purge its contents.
|
||||
*/
|
||||
if (S_ISDIR(ZTOI(zp)->i_mode) && (zp->z_pflags & ZFS_XATTR)) {
|
||||
error = zap_count(os, zp->z_id, &count);
|
||||
if (error) {
|
||||
zfs_znode_dmu_fini(zp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
taskq_t *taskq;
|
||||
|
||||
if (zfs_purgedir(zp) != 0) {
|
||||
/*
|
||||
* There are still directory entries in this xattr
|
||||
* directory. Let zfs_unlinked_drain() deal with
|
||||
* them to avoid deadlocking this process in the
|
||||
* zfs_purgedir()->zfs_zget()->ilookup() callpath
|
||||
* on the xattr inode's I_FREEING bit.
|
||||
* Not enough space to delete some xattrs.
|
||||
* Leave it in the unlinked set.
|
||||
*/
|
||||
taskq = dsl_pool_iput_taskq(dmu_objset_pool(os));
|
||||
taskq_dispatch(taskq, (task_func_t *)
|
||||
zfs_unlinked_drain, zsb, TQ_SLEEP);
|
||||
|
||||
zfs_znode_dmu_fini(zp);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user