Clarify and restrict dmu_tx_assign() errors

There are three possible cases where dmu_tx_assign() may
encounter a fatal error.  When there is a true lack of free
space (ENOSPC), when there is a lack of quota space (EDQUOT),
or when data required to perform the transaction cannot be
read from disk (EIO).  See the dmu_tx_check_ioerr() function
for additional details of on the motivation for check for
I/O error early.

Prior to this change dmu_tx_assign() would return the
contents of tx->tx_err which covered a wide range of possible
error codes (EIO, ECKSUM, ESRCH, etc).  In practice, none
of the callers could do anything useful with this level of
detail and simply returned the error.

Therefore, this change converts all tx->tx_err errors to EIO,
adds ASSERTs to dmu_tx_assign() to cover the only possible
errors, and clarifies the function comment to include EIO as
a possible fatal error.

Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Brian D Behlendorf <behlendo@slag12.llnl.gov>
Closes #17463
This commit is contained in:
Brian Behlendorf 2025-06-23 12:48:30 -07:00 committed by GitHub
parent 8170eb6ebc
commit 48ce292ea0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 13 additions and 6 deletions

View File

@ -1816,7 +1816,7 @@ ztest_zd_fini(ztest_ds_t *zd)
(ztest_random(10) == 0 ? DMU_TX_NOWAIT : DMU_TX_WAIT) (ztest_random(10) == 0 ? DMU_TX_NOWAIT : DMU_TX_WAIT)
static uint64_t static uint64_t
ztest_tx_assign(dmu_tx_t *tx, uint64_t txg_how, const char *tag) ztest_tx_assign(dmu_tx_t *tx, dmu_tx_flag_t txg_how, const char *tag)
{ {
uint64_t txg; uint64_t txg;
int error; int error;
@ -1829,9 +1829,10 @@ ztest_tx_assign(dmu_tx_t *tx, uint64_t txg_how, const char *tag)
if (error == ERESTART) { if (error == ERESTART) {
ASSERT3U(txg_how, ==, DMU_TX_NOWAIT); ASSERT3U(txg_how, ==, DMU_TX_NOWAIT);
dmu_tx_wait(tx); dmu_tx_wait(tx);
} else { } else if (error == ENOSPC) {
ASSERT3U(error, ==, ENOSPC);
ztest_record_enospc(tag); ztest_record_enospc(tag);
} else {
ASSERT(error == EDQUOT || error == EIO);
} }
dmu_tx_abort(tx); dmu_tx_abort(tx);
return (0); return (0);

View File

@ -1054,7 +1054,7 @@ dmu_tx_try_assign(dmu_tx_t *tx)
if (tx->tx_err) { if (tx->tx_err) {
DMU_TX_STAT_BUMP(dmu_tx_error); DMU_TX_STAT_BUMP(dmu_tx_error);
return (tx->tx_err); return (SET_ERROR(EIO));
} }
if (spa_suspended(spa)) { if (spa_suspended(spa)) {
@ -1190,7 +1190,8 @@ dmu_tx_unassign(dmu_tx_t *tx)
* If DMU_TX_WAIT is set and the currently open txg is full, this function * If DMU_TX_WAIT is set and the currently open txg is full, this function
* will wait until there's a new txg. This should be used when no locks * will wait until there's a new txg. This should be used when no locks
* are being held. With this bit set, this function will only fail if * are being held. With this bit set, this function will only fail if
* we're truly out of space (or over quota). * we're truly out of space (ENOSPC), over quota (EDQUOT), or required
* data for the transaction could not be read from disk (EIO).
* *
* If DMU_TX_WAIT is *not* set and we can't assign into the currently open * If DMU_TX_WAIT is *not* set and we can't assign into the currently open
* txg without blocking, this function will return immediately with * txg without blocking, this function will return immediately with
@ -1279,8 +1280,11 @@ dmu_tx_assign(dmu_tx_t *tx, dmu_tx_flag_t flags)
* Return unless we decided to retry, or the caller does not * Return unless we decided to retry, or the caller does not
* want to block. * want to block.
*/ */
if (err != ERESTART || !(flags & DMU_TX_WAIT)) if (err != ERESTART || !(flags & DMU_TX_WAIT)) {
ASSERT(err == EDQUOT || err == ENOSPC ||
err == ERESTART || err == EIO);
return (err); return (err);
}
/* /*
* Wait until there's room in this txg, or until it's been * Wait until there's room in this txg, or until it's been

View File

@ -1450,6 +1450,8 @@ dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize, uint64_t asize,
MSEC2NSEC(10), MSEC2NSEC(10)); MSEC2NSEC(10), MSEC2NSEC(10));
err = SET_ERROR(ERESTART); err = SET_ERROR(ERESTART);
} }
ASSERT3U(err, ==, ERESTART);
} }
if (err == 0) { if (err == 0) {