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.

> 
> The manager must still outlive every caller, since a woken waiter has to
> retake the completion and bitmap locks before it returns. Spell that out
> in the kernel-doc: libie_ctlq_xn_deinit() must not be called until every
> libie_ctlq_xn_shutdown() caller has returned.
> 
> The next patch makes idpf call libie_ctlq_xn_shutdown() from its service
> task, which can run concurrently with the reset and remove paths.
> 
> Fixes: b4ff4e626a7c ("libie: add bookkeeping support for control queue 
> messages")
> Assisted-by: LLM
> Signed-off-by: Emil Tantilov <[email protected]>
> ---
>  drivers/net/ethernet/intel/libie/controlq.c | 19 +++++++++++--------
>  1 file changed, 11 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/libie/controlq.c 
> b/drivers/net/ethernet/intel/libie/controlq.c
> index 6214fc036ce5f..9bcb022ed9782 100644
> --- a/drivers/net/ethernet/intel/libie/controlq.c
> +++ b/drivers/net/ethernet/intel/libie/controlq.c
> @@ -735,13 +735,10 @@ static bool __libie_ctlq_xn_push_free(struct 
> libie_ctlq_xn_manager *xnm,
>  static void libie_ctlq_xn_push_free(struct libie_ctlq_xn_manager *xnm,
>                                   struct libie_ctlq_xn *xn)
>  {
> -     bool can_destroy;
> -
> -     scoped_guard(spinlock, &xnm->free_xns_bm_lock)
> -             can_destroy = __libie_ctlq_xn_push_free(xnm, xn);
> +     guard(spinlock)(&xnm->free_xns_bm_lock);
>  
> -     if (can_destroy)
> -             complete(&xnm->can_destroy);
> +     if (__libie_ctlq_xn_push_free(xnm, xn))
> +             complete_all(&xnm->can_destroy);
>  }
>  
>  /**
> @@ -1171,7 +1168,9 @@ EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_send_clean, 
> "LIBIE_CP");
>   * @xnm: pointer to the transaction manager
>   *
>   * Synchronously terminate existing transactions and stop accepting new ones.
> - * Async transactions are discarded without invoking resp_cb.
> + * Async transactions are discarded without invoking resp_cb. May be called
> + * concurrently, libie_ctlq_xn_deinit() must not be called until every caller
> + * has returned.
>   */
>  void libie_ctlq_xn_shutdown(struct libie_ctlq_xn_manager *xnm)
>  {
> @@ -1218,8 +1217,12 @@ void libie_ctlq_xn_shutdown(struct 
> libie_ctlq_xn_manager *xnm)
>  
>       spin_unlock(&xnm->free_xns_bm_lock);
>  
> -     if (must_wait)
> +     if (must_wait) {
>               wait_for_completion(&xnm->can_destroy);
> +             /* Synchronize with libie_ctlq_xn_push_free() unlocking */
> +             spin_lock(&xnm->free_xns_bm_lock);
> +             spin_unlock(&xnm->free_xns_bm_lock);
> +     }
>  }
>  EXPORT_SYMBOL_NS_GPL(libie_ctlq_xn_shutdown, "LIBIE_CP");
>  
> -- 
> 2.37.3
> 

Reply via email to