Thread (63 messages) read the whole thread 63 messages, 5 authors, 2019-02-11

Re: [PATCH 09/18] io_uring: use fget/fput_many() for file references

From: Jens Axboe <axboe@kernel.dk>
Date: 2019-01-29 23:44:53
Also in: linux-block

On 1/29/19 4:31 PM, Jann Horn wrote:
On Tue, Jan 29, 2019 at 8:27 PM Jens Axboe [off-list ref] wrote:
quoted
Add a separate io_submit_state structure, to cache some of the things
we need for IO submission.

One such example is file reference batching. io_submit_state. We get as
many references as the number of sqes we are submitting, and drop
unused ones if we end up switching files. The assumption here is that
we're usually only dealing with one fd, and if there are multiple,
hopefuly they are at least somewhat ordered. Could trivially be extended
to cover multiple fds, if needed.

On the completion side we do the same thing, except this is trivially
done just locally in io_iopoll_reap().
[...]
quoted
+static void io_file_put(struct io_submit_state *state, struct file *file)
+{
+       if (!state) {
+               fput(file);
+       } else if (state->file) {
+               int diff = state->has_refs - state->used_refs;
+
+               if (diff)
+                       fput_many(state->file, diff);
+               state->file = NULL;
+       }
+}
Hmm, this function confuses me.
The state==NULL path works as I'd expect, it calls fput() on the file.
But if `state!=NULL && state->file==NULL`, it does nothing, it never
uses `file`.
And if `state->file!=NULL`, it drops the excess bias on the file's
refcount, but it doesn't drop the current reference - and again
without even looking at `file`.

So when io_prep_rw() uses io_file_get() to grab a reference on a file
it hasn't seen before, it will acquire `ios_left` references and
actually use one of them; then if it goes through the out_fput error
path, it goes through the path for `state->file!=NULL`, drops
`ios_left-1` references (leaving the refcount elevated by 1), and
forgets about the file?
I'll take a look, it's not impossible there's an off-by-one there.
quoted
+/*
+ * Get as many references to a file as we have IOs left in this submission,
+ * assuming most submissions are for one file, or at least that each file
+ * has more than one submission.
+ */
+static struct file *io_file_get(struct io_submit_state *state, int fd)
+{
+       if (!state)
+               return fget(fd);
+
+       if (state->file) {
+               if (state->fd == fd) {
+                       state->used_refs++;
+                       state->ios_left--;
+                       return state->file;
+               }
+               io_file_put(state, NULL);
+       }
+       state->file = fget_many(fd, state->ios_left);
+       if (!state->file)
+               return NULL;
+
+       state->fd = fd;
+       state->has_refs = state->ios_left;
+       state->used_refs = 1;
+       state->ios_left--;
+       return state->file;
+}
+
 static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
-                     bool force_nonblock)
+                     bool force_nonblock, struct io_submit_state *state)
 {
        struct io_ring_ctx *ctx = req->ctx;
        struct kiocb *kiocb = &req->rw;
@@ -487,7 +560,7 @@ static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
        int fd, ret;

        fd = READ_ONCE(sqe->fd);
-       kiocb->ki_filp = fget(fd);
+       kiocb->ki_filp = io_file_get(state, fd);
        if (unlikely(!kiocb->ki_filp))
                return -EBADF;
        kiocb->ki_pos = READ_ONCE(sqe->off);
@@ -528,7 +601,7 @@ static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
        }
        return 0;
 out_fput:
-       fput(kiocb->ki_filp);
+       io_file_put(state, kiocb->ki_filp);
        return ret;
 }
[...]
quoted
+static void io_submit_state_start(struct io_submit_state *state,
+                                 struct io_ring_ctx *ctx, unsigned max_ios)
There are various places in your series where you use raw "unsigned"
instead of "unsigned int"; when I run your tree through checkpatch.pl,
it complains about that and a few other things. Please fix the
checkpatch warnings (except for warnings where you know that they
shouldn't apply here for some reason).
Using unsigned is just fine, it's the same thing. checkpatch.pl
complains about a lot of stuff that doesn't matter, that's one of them.
I don't mind fixing valid warnings, but this particular one is just
noise.

-- 
Jens Axboe

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org.  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help