io_uring: make timeout sequence == 0 mean no sequence
authorJens Axboe <axboe@kernel.dk>
Tue, 12 Nov 2019 06:34:31 +0000 (23:34 -0700)
committerJens Axboe <axboe@kernel.dk>
Tue, 12 Nov 2019 07:18:51 +0000 (00:18 -0700)
Currently we make sequence == 0 be the same as sequence == 1, but that's
not super useful if the intent is really to have a timeout that's just
a pure timeout.

If the user passes in sqe->off == 0, then don't apply any sequence logic
to the request, let it purely be driven by the timeout specified.

Reported-by: 李通洲 <carter.li@eoitek.com>
Reviewed-by: 李通洲 <carter.li@eoitek.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c

index f9a3899..87beca4 100644 (file)
@@ -326,6 +326,7 @@ struct io_kiocb {
 #define REQ_F_TIMEOUT          1024    /* timeout request */
 #define REQ_F_ISREG            2048    /* regular file */
 #define REQ_F_MUST_PUNT                4096    /* must be punted even for NONBLOCK */
+#define REQ_F_TIMEOUT_NOSEQ    8192    /* no timeout sequence */
        u64                     user_data;
        u32                     result;
        u32                     sequence;
@@ -453,9 +454,13 @@ static struct io_kiocb *io_get_timeout_req(struct io_ring_ctx *ctx)
        struct io_kiocb *req;
 
        req = list_first_entry_or_null(&ctx->timeout_list, struct io_kiocb, list);
-       if (req && !__io_sequence_defer(ctx, req)) {
-               list_del_init(&req->list);
-               return req;
+       if (req) {
+               if (req->flags & REQ_F_TIMEOUT_NOSEQ)
+                       return NULL;
+               if (!__io_sequence_defer(ctx, req)) {
+                       list_del_init(&req->list);
+                       return req;
+               }
        }
 
        return NULL;
@@ -1941,18 +1946,24 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        if (get_timespec64(&ts, u64_to_user_ptr(sqe->addr)))
                return -EFAULT;
 
+       req->flags |= REQ_F_TIMEOUT;
+
        /*
         * sqe->off holds how many events that need to occur for this
-        * timeout event to be satisfied.
+        * timeout event to be satisfied. If it isn't set, then this is
+        * a pure timeout request, sequence isn't used.
         */
        count = READ_ONCE(sqe->off);
-       if (!count)
-               count = 1;
+       if (!count) {
+               req->flags |= REQ_F_TIMEOUT_NOSEQ;
+               spin_lock_irq(&ctx->completion_lock);
+               entry = ctx->timeout_list.prev;
+               goto add;
+       }
 
        req->sequence = ctx->cached_sq_head + count - 1;
        /* reuse it to store the count */
        req->submit.sequence = count;
-       req->flags |= REQ_F_TIMEOUT;
 
        /*
         * Insertion sort, ensuring the first entry in the list is always
@@ -1964,6 +1975,9 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
                unsigned nxt_sq_head;
                long long tmp, tmp_nxt;
 
+               if (nxt->flags & REQ_F_TIMEOUT_NOSEQ)
+                       continue;
+
                /*
                 * Since cached_sq_head + count - 1 can overflow, use type long
                 * long to store it.
@@ -1990,6 +2004,7 @@ static int io_timeout(struct io_kiocb *req, const struct io_uring_sqe *sqe)
                nxt->sequence++;
        }
        req->sequence -= span;
+add:
        list_add(&req->list, entry);
        spin_unlock_irq(&ctx->completion_lock);