Freeing an ifq is a two step process: in io_ring_exit_work(), the ifqs are first cleaned up via io_shutdown_zcrx_ifqs() while there are still outstanding ctx->refs. Then once ctx->refs falls to 0, the ifqs are freed in io_unregister_zcrx_ifqs(). The main thing to note is that io_shutdown_zcrx_ifqs() may be called multiple times. To ensure each ifq is only cleaned up once, set ifq->if_rxq to -1 once cleanup is done. Proxy ifqs hold two refs: one on the src ifq and one on the src ring. In io_shutdown_zcrx_ifqs(), dec both refs. While these refs are held, the src ring is looping in io_ring_exit_work(). The active refs on ifq->refs prevents the src ifqs from being cleaned up, and the active refs on ctx->refs prevents the objects from being freed. Once all refs are gone, the src ring proceeds with io_ring_ctx_free(). Signed-off-by: David Wei --- io_uring/zcrx.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 6b9066333fcf..2d0d1ca016c5 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -519,6 +519,8 @@ static void io_close_queue(struct io_zcrx_ifq *ifq) static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq) { + if (ifq->proxy) + goto free; io_close_queue(ifq); if (ifq->area) @@ -528,6 +530,7 @@ static void io_zcrx_ifq_free(struct io_zcrx_ifq *ifq) io_free_rbuf_ring(ifq); mutex_destroy(&ifq->pp_lock); +free: kfree(ifq); } @@ -801,10 +804,19 @@ void io_shutdown_zcrx_ifqs(struct io_ring_ctx *ctx) lockdep_assert_held(&ctx->uring_lock); xa_for_each(&ctx->zcrx_ctxs, index, ifq) { - if (refcount_read(&ifq->refs) > 1) + if (ifq->if_rxq == -1) continue; - io_zcrx_scrub(ifq); - io_close_queue(ifq); + + if (!ifq->proxy) { + if (refcount_read(&ifq->refs) > 1) + continue; + io_zcrx_scrub(ifq); + io_close_queue(ifq); + } else { + refcount_dec(&ifq->proxy->refs); + percpu_ref_put(&ifq->proxy->ctx->refs); + ifq->if_rxq = -1; + } } } -- 2.47.3