Skip to content

Commit c8184d7

Browse files
authored
Block cloning conditionally destroy ARC buffer
dmu_buf_will_clone() calls arc_buf_destroy() if there is an associated ARC buffer with the dbuf. However, this can only be done conditionally. If the previous dirty record's dr_data is pointed at db_dbf then destroying it can lead to NULL pointer deference when syncing out the previous dirty record. This updates dmu_buf_fill_clone() to only call arc_buf_destroy() if the previous dirty records dr_data is not pointing to db_buf. The block clone wil still set the dbuf's db_buf and db_data to NULL, but this will not cause any issues as any previous dirty record dr_data will still be pointing at the ARC buffer. Reviewed-by: Allan Jude <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Alexander Motin <[email protected]> Signed-off-by: Brian Atkinson <[email protected]> Closes #16337
1 parent c092bdd commit c8184d7

File tree

1 file changed

+15
-1
lines changed

1 file changed

+15
-1
lines changed

module/zfs/dbuf.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2705,6 +2705,9 @@ void
27052705
dmu_buf_will_clone(dmu_buf_t *db_fake, dmu_tx_t *tx)
27062706
{
27072707
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
2708+
ASSERT0(db->db_level);
2709+
ASSERT(db->db_blkid != DMU_BONUS_BLKID);
2710+
ASSERT(db->db.db_object != DMU_META_DNODE_OBJECT);
27082711

27092712
/*
27102713
* Block cloning: We are going to clone into this block, so undirty
@@ -2716,11 +2719,22 @@ dmu_buf_will_clone(dmu_buf_t *db_fake, dmu_tx_t *tx)
27162719
VERIFY(!dbuf_undirty(db, tx));
27172720
ASSERT0P(dbuf_find_dirty_eq(db, tx->tx_txg));
27182721
if (db->db_buf != NULL) {
2719-
arc_buf_destroy(db->db_buf, db);
2722+
/*
2723+
* If there is an associated ARC buffer with this dbuf we can
2724+
* only destroy it if the previous dirty record does not
2725+
* reference it.
2726+
*/
2727+
dbuf_dirty_record_t *dr = list_head(&db->db_dirty_records);
2728+
if (dr == NULL || dr->dt.dl.dr_data != db->db_buf)
2729+
arc_buf_destroy(db->db_buf, db);
2730+
27202731
db->db_buf = NULL;
27212732
dbuf_clear_data(db);
27222733
}
27232734

2735+
ASSERT3P(db->db_buf, ==, NULL);
2736+
ASSERT3P(db->db.db_data, ==, NULL);
2737+
27242738
db->db_state = DB_NOFILL;
27252739
DTRACE_SET_STATE(db, "allocating NOFILL buffer for clone");
27262740

0 commit comments

Comments
 (0)