aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Snajdr <snajpa@snajpa.net>2024-04-04 01:09:19 +0000
committerGitHub <noreply@github.com>2024-04-04 01:09:19 +0000
commit30c4eba4eaaef92cbc1b8d2eed6689600a342386 (patch)
tree1e5cddacf3bf4e005ee69274c24b4d003675ac18
parent66929f6829985b9d90dcc0828291e5353e6b3fc9 (diff)
downloadsrc-30c4eba4eaaef92cbc1b8d2eed6689600a342386.tar.gz
src-30c4eba4eaaef92cbc1b8d2eed6689600a342386.zip
Fix panics when truncating/deleting files
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
-rw-r--r--module/zfs/dbuf.c18
1 files changed, 8 insertions, 10 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c
index 0ab143bd089f..d43f84e84725 100644
--- a/module/zfs/dbuf.c
+++ b/module/zfs/dbuf.c
@@ -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.
*/