On 10/27/2026 2:11 AM, Larysa Zaremba wrote:
On Mon, Oct 26, 2026 at 03:19:07PM -0700, Emil Tantilov wrote:
libie_ctlq_xn_shutdown() waits on xnm->can_destroy for the outstanding
transactions to be returned. libie_ctlq_xn_push_free() signals it with
complete() when the last entry goes back to the free bitmap. Once the
manager is shut down, libie_ctlq_xn_pop_free() hands out no more entries,
so that point is reached exactly once and only one waiter is woken.
Nothing restricts libie_ctlq_xn_shutdown() to a single caller, though.
If two threads call it while a transaction is still outstanding, both see
the entry in use and both wait, and the one that is not woken blocks
forever, as wait_for_completion() has no timeout.
A drained manager stays drained until libie_ctlq_xn_deinit() frees it, so
signal it with complete_all(). All callers waiting at that point are
released, and later ones find the bitmap full and return at once.
Yeah, I should have used complete_all().
Signal it before free_xns_bm_lock is dropped. A caller that finds the
bitmap full then knows the completion has already been signalled. That
leaves libie_ctlq_xn_push_free() holding the lock after it has woken the
waiters, so have a woken waiter take and release free_xns_bm_lock before
it returns, the same lock/unlock pair completion_done() uses to wait for
complete(), see commit dec13c42d21a ("completion: Replace
spin_unlock_wait() with lock/unlock pair"). Its unlock is then the last
access to the manager by the time any caller returns.
The way you phrase this, the locking change seems redundant, as complete()
provides the same end synchronization. I can't see a scenario, where just
complete()->complete_all() change wouldn't be enough by itself, so please
describe the problematic sequence.
Correct, it's a leftover from my earlier attempts to solve it via
locking... Will clean it up in v3.
Thanks for reviewing!
Emil