making sure the last quiesced txg is synced
Fixed a potential bug as described in #8233:
Consider this scenario (see [txg.c](06f3fc2a4b/module/zfs/txg.c
) ):
There is heavy write load when the pool exports.
After `txg_sync_stop`'s call of `txg_wait_synced` returns, many more txgs get processed, but right before` txg_sync_stop` gets `tx_sync_lock`, the following happens:
- `txg_sync_thread` begins waiting on `tx_sync_more_cv`.
- `txg_quiesce_thread` gets done with `txg_quiesce(dp, txg)`.
- `txg_sync_stop` gets `tx_sync_lock` first, calls `cv_broadcast`s with `tx_exiting` == 1, and waits for exits.
- `txg_sync_thread` wakes up first and exits.
- Finally, `txg_quiesce_thread` gets `tx_sync_lock`, and calls `cv_broadcast(&tx->tx_sync_more_cv)`,
but `txg_sync_thread` is already gone, and the txg in `txg_quiesce(dp, txg)` above never gets synced.
Signed-off-by: Leap Second <leapsecond@protonmail.com>
This commit is contained in:
parent
0b8e4418b6
commit
1e05119d5b
|
@ -508,6 +508,7 @@ txg_sync_thread(void *arg)
|
|||
tx_state_t *tx = &dp->dp_tx;
|
||||
callb_cpr_t cpr;
|
||||
clock_t start, delta;
|
||||
boolean_t checked_quiescing = B_FALSE;
|
||||
|
||||
(void) spl_fstrans_mark();
|
||||
txg_thread_enter(tx, &cpr);
|
||||
|
@ -549,8 +550,18 @@ txg_sync_thread(void *arg)
|
|||
txg_thread_wait(tx, &cpr, &tx->tx_quiesce_done_cv, 0);
|
||||
}
|
||||
|
||||
if (tx->tx_exiting)
|
||||
if (tx->tx_exiting) {
|
||||
if (checked_quiescing)
|
||||
txg_thread_exit(tx, &cpr, &tx->tx_sync_thread);
|
||||
else {
|
||||
while (tx->tx_threads != 1)
|
||||
txg_thread_wait(tx, &cpr, &tx->tx_exit_cv, 0);
|
||||
if (tx->tx_quiesced_txg)
|
||||
checked_quiescing = B_TRUE;
|
||||
else
|
||||
txg_thread_exit(tx, &cpr, &tx->tx_sync_thread);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Consume the quiesced txg which has been handed off to
|
||||
|
|
Loading…
Reference in New Issue