diff --git a/module/os/linux/zfs/zfs_znode_os.c b/module/os/linux/zfs/zfs_znode_os.c index bc1e17f086d..733c7c7af41 100644 --- a/module/os/linux/zfs/zfs_znode_os.c +++ b/module/os/linux/zfs/zfs_znode_os.c @@ -1053,7 +1053,6 @@ zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp) *zpp = NULL; -again: zh = zfs_znode_hold_enter(zfsvfs, obj_num); err = sa_buf_hold(zfsvfs->z_os, obj_num, NULL, &db); @@ -1076,55 +1075,24 @@ zfs_zget(zfsvfs_t *zfsvfs, uint64_t obj_num, znode_t **zpp) if (hdl != NULL) { zp = sa_get_userdata(hdl); - /* * Since "SA" does immediate eviction we * should never find a sa handle that doesn't * know about the znode. */ - ASSERT3P(zp, !=, NULL); - mutex_enter(&zp->z_lock); - ASSERT3U(zp->z_id, ==, obj_num); - /* - * If zp->z_unlinked is set, the znode is already marked - * for deletion and should not be discovered. Check this - * after checking igrab() due to fsetxattr() & O_TMPFILE. - * - * If igrab() returns NULL the VFS has independently - * determined the inode should be evicted and has - * called iput_final() to start the eviction process. - * The SA handle is still valid but because the VFS - * requires that the eviction succeed we must drop - * our locks and references to allow the eviction to - * complete. The zfs_zget() may then be retried. - * - * This unlikely case could be optimized by registering - * a sops->drop_inode() callback. The callback would - * need to detect the active SA hold thereby informing - * the VFS that this inode should not be evicted. - */ - if (igrab(ZTOI(zp)) == NULL) { - if (zp->z_unlinked) - err = SET_ERROR(ENOENT); - else - err = SET_ERROR(EAGAIN); - } else { - *zpp = zp; - err = 0; + if (zp->z_unlinked) { + sa_buf_rele(db, NULL); + zfs_znode_hold_exit(zfsvfs, zh); + return (SET_ERROR(ENOENT)); } + VERIFY3P(igrab(ZTOI(zp)), !=, NULL); + *zpp = zp; - mutex_exit(&zp->z_lock); sa_buf_rele(db, NULL); zfs_znode_hold_exit(zfsvfs, zh); - - if (err == EAGAIN) { - /* inode might need this to finish evict */ - cond_resched(); - goto again; - } - return (err); + return (0); } /* diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c index 287f5f36f9d..7cf8439a728 100644 --- a/module/os/linux/zfs/zpl_super.c +++ b/module/os/linux/zfs/zpl_super.c @@ -92,6 +92,26 @@ zpl_evict_inode(struct inode *ip) spl_fstrans_unmark(cookie); } +static int +zpl_drop_inode(struct inode *ip) +{ + znode_t *zp = ITOZ(ip); + dmu_buf_t *db; + int error; + + /* + * Don't allow the kernel to proceed to zpl_evict_inode if there's an + * active SA hold so that igrab() in zfs_zget doesn't race against + * eviction + */ + if ((zp->z_sa_hdl && (db = sa_get_db(zp->z_sa_hdl)) && + dmu_buf_refcount(db))) + return (0); + + error = generic_drop_inode(ip); + return (error); +} + static void zpl_put_super(struct super_block *sb) { @@ -384,6 +404,7 @@ const struct super_operations zpl_super_operations = { .dirty_inode = zpl_dirty_inode, .write_inode = NULL, .evict_inode = zpl_evict_inode, + .drop_inode = zpl_drop_inode, .put_super = zpl_put_super, .sync_fs = zpl_sync_fs, .statfs = zpl_statfs, diff --git a/module/zfs/spa_history.c b/module/zfs/spa_history.c index de036d6c371..09421c6557f 100644 --- a/module/zfs/spa_history.c +++ b/module/zfs/spa_history.c @@ -560,6 +560,12 @@ spa_history_log_internal(spa_t *spa, const char *operation, } } + if (htx->tx_txg > spa_final_dirty_txg(spa)) { + if (tx == NULL) + dmu_tx_abort(htx); + return; + } + va_start(adx, fmt); log_internal(fnvlist_alloc(), operation, spa, htx, fmt, adx); va_end(adx);