dbuf: account dirty data for embedded writes

dmu_buf_will_not_fill() leaves the dbuf DB_NOFILL and dbuf_dirty()
skips the dirty accounting for those, so the dirty record from an
embedded write never raises dp_dirty_total.  A receive of a stream
of embedded records runs unthrottled ahead of the syncing TXG,
pinning dbufs the ARC cannot evict.  Same gap as dmu_brt_clone().

An embedded write fills a whole block, so charge one.

No reproducer yet.  A receive of 300,002 embedded records against
a 64 MiB ARC peaks at 116%, and the same receive carrying 2
embedded records peaks at 118%, so that overshoot is dnode and
bonus buffers from creating 300,000 files rather than the embedded
writes.  The accounting gap is real either way.

Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Patrick Domack <[email protected]>
Closes #19071
This commit is contained in:
Patrick Domack authored and GitHub committed 2026-09-10 10:43:57 -07:00
1 parent 8fe28f4ca4
commit db07a5e0a8
1 file changed
+10
+10
View File
@@ -3166,6 +3166,16 @@ dmu_buf_write_embedded(dmu_buf_t *dbuf, void *data,
dl->dr_override_state = DR_OVERRIDDEN;
BP_SET_LOGICAL_BIRTH(&dl->dr_overridden_by, dr->dr_txg);
/*
* dmu_buf_will_not_fill() leaves the dbuf DB_NOFILL, which
* dbuf_dirty() does not account for, so charge the dirty
* record here. This keeps a receive of embedded records
* throttled against the syncing TXG. An embedded write fills
* a whole block, so charge one.
*/
dr->dr_accounted = db->db.db_size;
dmu_objset_willuse_space(db->db_objset, dr->dr_accounted, tx);
}
void