io-uring.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jens Axboe <axboe@kernel.dk>
To: io-uring <io-uring@vger.kernel.org>, 李通洲 <carter.li@eoitek.com>
Subject: Re: [PATCH] io_uring: prune request from overflow list on flush
Date: Thu, 13 Feb 2020 17:51:53 -0700	[thread overview]
Message-ID: <03adb172-b58d-147e-4955-b79422bc1e93@kernel.dk> (raw)
In-Reply-To: <01ae4ae7-0cbb-389b-8ee9-f561b3df1d6a@kernel.dk>

On 2/13/20 5:17 PM, Jens Axboe wrote:
> Carter reported an issue where he could produce a stall on ring exit,
> when we're cleaning up requests that match the given file table. For
> this particular test case, a combination of a few things caused the
> issue:
> 
> - The cq ring was overflown
> - The request being canceled was in the overflow list
> 
> The combination of the above means that the cq overflow list holds a
> reference to the request. The request is canceled correctly, but since
> the overflow list holds a reference to it, the final put won't happen.
> Since the final put doesn't happen, the request remains in the inflight.
> Hence we never finish the cancelation flush.
> 
> Fix this by removing requests from the overflow list if we're canceling
> them.

What I queued up was a v2, only difference being that we increment the
overflow counter if we prune it. Below for reference:


commit 2ca10259b4189a433c309054496dd6af1415f992
Author: Jens Axboe <axboe@kernel.dk>
Date:   Thu Feb 13 17:17:35 2020 -0700

    io_uring: prune request from overflow list on flush
    
    Carter reported an issue where he could produce a stall on ring exit,
    when we're cleaning up requests that match the given file table. For
    this particular test case, a combination of a few things caused the
    issue:
    
    - The cq ring was overflown
    - The request being canceled was in the overflow list
    
    The combination of the above means that the cq overflow list holds a
    reference to the request. The request is canceled correctly, but since
    the overflow list holds a reference to it, the final put won't happen.
    Since the final put doesn't happen, the request remains in the inflight.
    Hence we never finish the cancelation flush.
    
    Fix this by removing requests from the overflow list if we're canceling
    them.
    
    Cc: stable@vger.kernel.org # 5.5
    Reported-by: Carter Li 李通洲 <carter.li@eoitek.com>
    Signed-off-by: Jens Axboe <axboe@kernel.dk>

diff --git a/fs/io_uring.c b/fs/io_uring.c
index 6d4e20d59729..5a826017ebb8 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -481,6 +481,7 @@ enum {
 	REQ_F_TIMEOUT_NOSEQ_BIT,
 	REQ_F_COMP_LOCKED_BIT,
 	REQ_F_NEED_CLEANUP_BIT,
+	REQ_F_OVERFLOW_BIT,
 };
 
 enum {
@@ -521,6 +522,8 @@ enum {
 	REQ_F_COMP_LOCKED	= BIT(REQ_F_COMP_LOCKED_BIT),
 	/* needs cleanup */
 	REQ_F_NEED_CLEANUP	= BIT(REQ_F_NEED_CLEANUP_BIT),
+	/* in overflow list */
+	REQ_F_OVERFLOW		= BIT(REQ_F_OVERFLOW_BIT),
 };
 
 /*
@@ -1103,6 +1106,7 @@ static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force)
 		req = list_first_entry(&ctx->cq_overflow_list, struct io_kiocb,
 						list);
 		list_move(&req->list, &list);
+		req->flags &= ~REQ_F_OVERFLOW;
 		if (cqe) {
 			WRITE_ONCE(cqe->user_data, req->user_data);
 			WRITE_ONCE(cqe->res, req->result);
@@ -1155,6 +1159,7 @@ static void io_cqring_fill_event(struct io_kiocb *req, long res)
 			set_bit(0, &ctx->sq_check_overflow);
 			set_bit(0, &ctx->cq_check_overflow);
 		}
+		req->flags |= REQ_F_OVERFLOW;
 		refcount_inc(&req->refs);
 		req->result = res;
 		list_add_tail(&req->list, &ctx->cq_overflow_list);
@@ -6463,6 +6468,29 @@ static void io_uring_cancel_files(struct io_ring_ctx *ctx,
 		if (!cancel_req)
 			break;
 
+		if (cancel_req->flags & REQ_F_OVERFLOW) {
+			spin_lock_irq(&ctx->completion_lock);
+			list_del(&cancel_req->list);
+			cancel_req->flags &= ~REQ_F_OVERFLOW;
+			if (list_empty(&ctx->cq_overflow_list)) {
+				clear_bit(0, &ctx->sq_check_overflow);
+				clear_bit(0, &ctx->cq_check_overflow);
+			}
+			spin_unlock_irq(&ctx->completion_lock);
+
+			WRITE_ONCE(ctx->rings->cq_overflow,
+				atomic_inc_return(&ctx->cached_cq_overflow));
+
+			/*
+			 * Put inflight ref and overflow ref. If that's
+			 * all we had, then we're done with this request.
+			 */
+			if (refcount_sub_and_test(2, &cancel_req->refs)) {
+				io_put_req(cancel_req);
+				continue;
+			}
+		}
+
 		io_wq_cancel_work(ctx->io_wq, &cancel_req->work);
 		io_put_req(cancel_req);
 		schedule();

-- 
Jens Axboe


      reply	other threads:[~2020-02-14  0:54 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-14  0:17 [PATCH] io_uring: prune request from overflow list on flush Jens Axboe
2020-02-14  0:51 ` Jens Axboe [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=03adb172-b58d-147e-4955-b79422bc1e93@kernel.dk \
    --to=axboe@kernel.dk \
    --cc=carter.li@eoitek.com \
    --cc=io-uring@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).