]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix panics when truncating/deleting files
authorPavel Snajdr <snajpa@snajpa.net>
Thu, 4 Apr 2024 01:09:19 +0000 (03:09 +0200)
committerGitHub <noreply@github.com>
Thu, 4 Apr 2024 01:09:19 +0000 (18:09 -0700)
There's an union in dbuf_dirty_record_t; dr_brtwrite could evaluate
to B_TRUE if the dirty record is of another type than dl. Adding
more explicit dr type check before trying to access dr_brtwrite.

Fixes two similar panics:

[ 1373.806119] VERIFY0(db->db_level) failed (0 == 1)
[ 1373.807232] PANIC at dbuf.c:2549:dbuf_undirty()
[ 1373.814979]  dump_stack_lvl+0x71/0x90
[ 1373.815799]  spl_panic+0xd3/0x100 [spl]
[ 1373.827709]  dbuf_undirty+0x62a/0x970 [zfs]
[ 1373.829204]  dmu_buf_will_dirty_impl+0x1e9/0x5b0 [zfs]
[ 1373.831010]  dnode_free_range+0x532/0x1220 [zfs]
[ 1373.833922]  dmu_free_long_range+0x4e0/0x930 [zfs]
[ 1373.835277]  zfs_trunc+0x75/0x1e0 [zfs]
[ 1373.837958]  zfs_freesp+0x9b/0x470 [zfs]
[ 1373.847236]  zfs_setattr+0x161a/0x3500 [zfs]
[ 1373.855267]  zpl_setattr+0x125/0x320 [zfs]
[ 1373.856725]  notify_change+0x1ee/0x4a0
[ 1373.859207]  do_truncate+0x7f/0xd0
[ 1373.859968]  do_sys_ftruncate+0x28e/0x2e0
[ 1373.860962]  do_syscall_64+0x38/0x90
[ 1373.861751]  entry_SYSCALL_64_after_hwframe+0x6e/0xd8

[ 1822.381337] VERIFY0(db->db_level) failed (0 == 1)
[ 1822.382376] PANIC at dbuf.c:2549:dbuf_undirty()
[ 1822.389232]  dump_stack_lvl+0x71/0x90
[ 1822.389920]  spl_panic+0xd3/0x100 [spl]
[ 1822.399567]  dbuf_undirty+0x62a/0x970 [zfs]
[ 1822.400583]  dmu_buf_will_dirty_impl+0x1e9/0x5b0 [zfs]
[ 1822.401752]  dnode_free_range+0x532/0x1220 [zfs]
[ 1822.402841]  dmu_object_free+0x74/0x120 [zfs]
[ 1822.403869]  zfs_znode_delete+0x75/0x120 [zfs]
[ 1822.404906]  zfs_rmnode+0x3f6/0x7f0 [zfs]
[ 1822.405870]  zfs_inactive+0xa3/0x610 [zfs]
[ 1822.407803]  zpl_evict_inode+0x3e/0x90 [zfs]
[ 1822.408831]  evict+0xc1/0x1c0
[ 1822.409387]  do_unlinkat+0x147/0x300
[ 1822.410060]  __x64_sys_unlinkat+0x33/0x60
[ 1822.410802]  do_syscall_64+0x38/0x90
[ 1822.411458]  entry_SYSCALL_64_after_hwframe+0x6e/0xd8

Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Brian Atkinson <batkinson@lanl.gov>
Signed-off-by: Pavel Snajdr <snajpa@snajpa.net>
Closes #15983

module/zfs/dbuf.c

index 0ab143bd089f7e3723af891c0c93bba3bd1bd703..d43f84e84725fac6d12f639962c8fca9bb7bf216 100644 (file)
@@ -2633,26 +2633,24 @@ dmu_buf_will_dirty_impl(dmu_buf_t *db_fake, int flags, dmu_tx_t *tx)
        ASSERT(!zfs_refcount_is_zero(&db->db_holds));
 
        /*
-        * Quick check for dirtiness.  For already dirty blocks, this
-        * reduces runtime of this function by >90%, and overall performance
-        * by 50% for some workloads (e.g. file deletion with indirect blocks
-        * cached).
+        * Quick check for dirtiness to improve performance for some workloads
+        * (e.g. file deletion with indirect blocks cached).
         */
        mutex_enter(&db->db_mtx);
-
        if (db->db_state == DB_CACHED || db->db_state == DB_NOFILL) {
-               dbuf_dirty_record_t *dr = dbuf_find_dirty_eq(db, tx->tx_txg);
                /*
-                * It's possible that it is already dirty but not cached,
+                * It's possible that the dbuf is already dirty but not cached,
                 * because there are some calls to dbuf_dirty() that don't
                 * go through dmu_buf_will_dirty().
                 */
+               dbuf_dirty_record_t *dr = dbuf_find_dirty_eq(db, tx->tx_txg);
                if (dr != NULL) {
-                       if (dr->dt.dl.dr_brtwrite) {
+                       if (db->db_level == 0 &&
+                           dr->dt.dl.dr_brtwrite) {
                                /*
                                 * Block cloning: If we are dirtying a cloned
-                                * block, we cannot simply redirty it, because
-                                * this dr has no data associated with it.
+                                * level 0 block, we cannot simply redirty it,
+                                * because this dr has no associated data.
                                 * We will go through a full undirtying below,
                                 * before dirtying it again.
                                 */