zfs: annotate nested dd_lock in reservation sync accounting
When reservation sync updates a child's reserved space, it rolls the delta into ancestor space accounting while still holding the child's dd_lock. That locking order is intentional, but Linux lockdep sees the ancestor acquisition as recursive because it lacks a nested lock subclass annotation. Teach the reservation-sync space-accounting path to acquire ancestor dd_lock instances with a nested subclass. Keep the existing public interfaces and accounting behavior unchanged by routing only the ancestor rollup through local helpers. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Signed-off-by: gality369 <gality369@example.com> Closes #18497
This commit is contained in:
+50
-14
@@ -1534,9 +1534,28 @@ dsl_dir_willuse_space(dsl_dir_t *dd, int64_t space, dmu_tx_t *tx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* call from syncing context when we actually write/free space for this dd */
|
/* call from syncing context when we actually write/free space for this dd */
|
||||||
void
|
static void dsl_dir_diduse_transfer_space_impl(dsl_dir_t *dd, int64_t used,
|
||||||
dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
|
int64_t compressed, int64_t uncompressed, int64_t tonew,
|
||||||
int64_t used, int64_t compressed, int64_t uncompressed, dmu_tx_t *tx)
|
dd_used_t oldtype, dd_used_t newtype, boolean_t nested, dmu_tx_t *tx);
|
||||||
|
|
||||||
|
static void
|
||||||
|
dsl_dir_lock_enter(dsl_dir_t *dd, boolean_t nested)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* lockdep needs an explicit subclass when a child dd_lock
|
||||||
|
* nests an ancestor.
|
||||||
|
*/
|
||||||
|
if (nested) {
|
||||||
|
mutex_enter_nested(&dd->dd_lock, NESTED_SINGLE);
|
||||||
|
} else {
|
||||||
|
mutex_enter(&dd->dd_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dsl_dir_diduse_space_impl(dsl_dir_t *dd, dd_used_t type,
|
||||||
|
int64_t used, int64_t compressed, int64_t uncompressed,
|
||||||
|
boolean_t nested, dmu_tx_t *tx)
|
||||||
{
|
{
|
||||||
int64_t accounted_delta;
|
int64_t accounted_delta;
|
||||||
|
|
||||||
@@ -1554,7 +1573,7 @@ dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
|
|||||||
*/
|
*/
|
||||||
boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
|
boolean_t needlock = !MUTEX_HELD(&dd->dd_lock);
|
||||||
if (needlock)
|
if (needlock)
|
||||||
mutex_enter(&dd->dd_lock);
|
dsl_dir_lock_enter(dd, nested);
|
||||||
dsl_dir_phys_t *ddp = dsl_dir_phys(dd);
|
dsl_dir_phys_t *ddp = dsl_dir_phys(dd);
|
||||||
accounted_delta = parent_delta(dd, ddp->dd_used_bytes, used);
|
accounted_delta = parent_delta(dd, ddp->dd_used_bytes, used);
|
||||||
ASSERT(used >= 0 || ddp->dd_used_bytes >= -used);
|
ASSERT(used >= 0 || ddp->dd_used_bytes >= -used);
|
||||||
@@ -1582,12 +1601,20 @@ dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type,
|
|||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
|
|
||||||
if (dd->dd_parent != NULL) {
|
if (dd->dd_parent != NULL) {
|
||||||
dsl_dir_diduse_transfer_space(dd->dd_parent,
|
dsl_dir_diduse_transfer_space_impl(dd->dd_parent,
|
||||||
accounted_delta, compressed, uncompressed,
|
accounted_delta, compressed, uncompressed,
|
||||||
used, DD_USED_CHILD_RSRV, DD_USED_CHILD, tx);
|
used, DD_USED_CHILD_RSRV, DD_USED_CHILD, nested, tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dsl_dir_diduse_space(dsl_dir_t *dd, dd_used_t type, int64_t used,
|
||||||
|
int64_t compressed, int64_t uncompressed, dmu_tx_t *tx)
|
||||||
|
{
|
||||||
|
dsl_dir_diduse_space_impl(dd, type, used, compressed, uncompressed,
|
||||||
|
B_FALSE, tx);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
|
dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
|
||||||
dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
|
dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
|
||||||
@@ -1612,10 +1639,10 @@ dsl_dir_transfer_space(dsl_dir_t *dd, int64_t delta,
|
|||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
dsl_dir_diduse_transfer_space(dsl_dir_t *dd, int64_t used,
|
dsl_dir_diduse_transfer_space_impl(dsl_dir_t *dd, int64_t used,
|
||||||
int64_t compressed, int64_t uncompressed, int64_t tonew,
|
int64_t compressed, int64_t uncompressed, int64_t tonew,
|
||||||
dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
|
dd_used_t oldtype, dd_used_t newtype, boolean_t nested, dmu_tx_t *tx)
|
||||||
{
|
{
|
||||||
int64_t accounted_delta;
|
int64_t accounted_delta;
|
||||||
|
|
||||||
@@ -1625,7 +1652,7 @@ dsl_dir_diduse_transfer_space(dsl_dir_t *dd, int64_t used,
|
|||||||
|
|
||||||
dmu_buf_will_dirty(dd->dd_dbuf, tx);
|
dmu_buf_will_dirty(dd->dd_dbuf, tx);
|
||||||
|
|
||||||
mutex_enter(&dd->dd_lock);
|
dsl_dir_lock_enter(dd, nested);
|
||||||
dsl_dir_phys_t *ddp = dsl_dir_phys(dd);
|
dsl_dir_phys_t *ddp = dsl_dir_phys(dd);
|
||||||
accounted_delta = parent_delta(dd, ddp->dd_used_bytes, used);
|
accounted_delta = parent_delta(dd, ddp->dd_used_bytes, used);
|
||||||
ASSERT(used >= 0 || ddp->dd_used_bytes >= -used);
|
ASSERT(used >= 0 || ddp->dd_used_bytes >= -used);
|
||||||
@@ -1656,12 +1683,21 @@ dsl_dir_diduse_transfer_space(dsl_dir_t *dd, int64_t used,
|
|||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
|
|
||||||
if (dd->dd_parent != NULL) {
|
if (dd->dd_parent != NULL) {
|
||||||
dsl_dir_diduse_transfer_space(dd->dd_parent,
|
dsl_dir_diduse_transfer_space_impl(dd->dd_parent,
|
||||||
accounted_delta, compressed, uncompressed,
|
accounted_delta, compressed, uncompressed,
|
||||||
used, DD_USED_CHILD_RSRV, DD_USED_CHILD, tx);
|
used, DD_USED_CHILD_RSRV, DD_USED_CHILD, nested, tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dsl_dir_diduse_transfer_space(dsl_dir_t *dd, int64_t used,
|
||||||
|
int64_t compressed, int64_t uncompressed, int64_t tonew,
|
||||||
|
dd_used_t oldtype, dd_used_t newtype, dmu_tx_t *tx)
|
||||||
|
{
|
||||||
|
dsl_dir_diduse_transfer_space_impl(dd, used, compressed,
|
||||||
|
uncompressed, tonew, oldtype, newtype, B_FALSE, tx);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct dsl_dir_set_qr_arg {
|
typedef struct dsl_dir_set_qr_arg {
|
||||||
const char *ddsqra_name;
|
const char *ddsqra_name;
|
||||||
zprop_source_t ddsqra_source;
|
zprop_source_t ddsqra_source;
|
||||||
@@ -1828,8 +1864,8 @@ dsl_dir_set_reservation_sync_impl(dsl_dir_t *dd, uint64_t value, dmu_tx_t *tx)
|
|||||||
|
|
||||||
if (dd->dd_parent != NULL) {
|
if (dd->dd_parent != NULL) {
|
||||||
/* Roll up this additional usage into our ancestors */
|
/* Roll up this additional usage into our ancestors */
|
||||||
dsl_dir_diduse_space(dd->dd_parent, DD_USED_CHILD_RSRV,
|
dsl_dir_diduse_space_impl(dd->dd_parent, DD_USED_CHILD_RSRV,
|
||||||
delta, 0, 0, tx);
|
delta, 0, 0, B_TRUE, tx);
|
||||||
}
|
}
|
||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user