]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Do not hash unlinked inodes
authorPaul Zuchowski <31706010+PaulZ-98@users.noreply.github.com>
Sat, 12 Jun 2021 00:00:33 +0000 (20:00 -0400)
committerGitHub <noreply@github.com>
Sat, 12 Jun 2021 00:00:33 +0000 (17:00 -0700)
In zfs_znode_alloc we always hash inodes.  If the
znode is unlinked, we do not need to hash it.  This
fixes the problem where zfs_suspend_fs is doing zrele
(iput) in an async fashion, and zfs_resume_fs unlinked
drain processing will try to hash an inode that could
still be hashed, resulting in a panic.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alan Somers <asomers@gmail.com>
Signed-off-by: Paul Zuchowski <pzuchowski@datto.com>
Closes #9741
Closes #11223
Closes #11648
Closes #12210

module/os/linux/zfs/zfs_znode.c

index 6015aea62dcaafad623883d1d91650c5b4a976ad..577927747aefbcd7ac689b23283624c29dc43e43 100644 (file)
@@ -606,17 +606,24 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz,
         * number is already hashed for this super block.  This can never
         * happen because the inode numbers map 1:1 with the object numbers.
         *
-        * The one exception is rolling back a mounted file system, but in
-        * this case all the active inode are unhashed during the rollback.
+        * Exceptions include rolling back a mounted file system, either
+        * from the zfs rollback or zfs recv command.
+        *
+        * Active inodes are unhashed during the rollback, but since zrele
+        * can happen asynchronously, we can't guarantee they've been
+        * unhashed.  This can cause hash collisions in unlinked drain
+        * processing so do not hash unlinked znodes.
         */
-       VERIFY3S(insert_inode_locked(ip), ==, 0);
+       if (links > 0)
+               VERIFY3S(insert_inode_locked(ip), ==, 0);
 
        mutex_enter(&zfsvfs->z_znodes_lock);
        list_insert_tail(&zfsvfs->z_all_znodes, zp);
        zfsvfs->z_nr_znodes++;
        mutex_exit(&zfsvfs->z_znodes_lock);
 
-       unlock_new_inode(ip);
+       if (links > 0)
+               unlock_new_inode(ip);
        return (zp);
 
 error: