]> git.baikalelectronics.ru Git - kernel.git/commitdiff
io_uring: account fixed file references correctly in batch
authorJens Axboe <axboe@kernel.dk>
Thu, 9 Jan 2020 14:52:28 +0000 (07:52 -0700)
committerJens Axboe <axboe@kernel.dk>
Tue, 21 Jan 2020 00:04:06 +0000 (17:04 -0700)
We can't assume that the whole batch has fixed files in it. If it's a
mix, or none at all, then we can end up doing a ref put that either
messes up accounting, or causes an oops if we have no fixed files at
all.

Also ensure we free requests properly between inflight accounted and
normal requests.

Fixes: 82c721577011 ("io_uring: extend batch freeing to cover more cases")
Reported-by: Dmitrii Dolgov <9erthalion6@gmail.com>
Reported-by: Pavel Begunkov <asml.silence@gmail.com>
Tested-by: Dmitrii Dolgov <9erthalion6@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c

index 50233efd9445910531edc2d9d14f98e118b0cb2f..8a645a37b4c761cc4fadaeec778b4bc041e0f37a 100644 (file)
@@ -1207,21 +1207,24 @@ struct req_batch {
 
 static void io_free_req_many(struct io_ring_ctx *ctx, struct req_batch *rb)
 {
+       int fixed_refs = rb->to_free;
+
        if (!rb->to_free)
                return;
        if (rb->need_iter) {
                int i, inflight = 0;
                unsigned long flags;
 
+               fixed_refs = 0;
                for (i = 0; i < rb->to_free; i++) {
                        struct io_kiocb *req = rb->reqs[i];
 
-                       if (req->flags & REQ_F_FIXED_FILE)
+                       if (req->flags & REQ_F_FIXED_FILE) {
                                req->file = NULL;
+                               fixed_refs++;
+                       }
                        if (req->flags & REQ_F_INFLIGHT)
                                inflight++;
-                       else
-                               rb->reqs[i] = NULL;
                        __io_req_aux_free(req);
                }
                if (!inflight)
@@ -1231,7 +1234,7 @@ static void io_free_req_many(struct io_ring_ctx *ctx, struct req_batch *rb)
                for (i = 0; i < rb->to_free; i++) {
                        struct io_kiocb *req = rb->reqs[i];
 
-                       if (req) {
+                       if (req->flags & REQ_F_INFLIGHT) {
                                list_del(&req->inflight_entry);
                                if (!--inflight)
                                        break;
@@ -1244,8 +1247,9 @@ static void io_free_req_many(struct io_ring_ctx *ctx, struct req_batch *rb)
        }
 do_free:
        kmem_cache_free_bulk(req_cachep, rb->to_free, rb->reqs);
+       if (fixed_refs)
+               percpu_ref_put_many(&ctx->file_data->refs, fixed_refs);
        percpu_ref_put_many(&ctx->refs, rb->to_free);
-       percpu_ref_put_many(&ctx->file_data->refs, rb->to_free);
        rb->to_free = rb->need_iter = 0;
 }