On Thu, Mar 18, 2021 at 7:31 AM Jens Axboe <axboe@xxxxxxxxx> wrote: > > On 3/16/21 8:01 AM, Kanchan Joshi wrote: > > Completion of a uring_cmd ioctl may involve referencing certain > > ioctl-specific fields, requiring original subitter context. > > Introduce 'uring_cmd_complete_in_task' that driver can use for this > > purpose. The API facilitates task-work infra, while driver gets to > > implement cmd-specific handling in a callback. > > > > Signed-off-by: Kanchan Joshi <joshi.k@xxxxxxxxxxx> > > --- > > fs/io_uring.c | 36 ++++++++++++++++++++++++++++++++---- > > include/linux/io_uring.h | 8 ++++++++ > > 2 files changed, 40 insertions(+), 4 deletions(-) > > > > diff --git a/fs/io_uring.c b/fs/io_uring.c > > index 583f8fd735d8..ca459ea9cb83 100644 > > --- a/fs/io_uring.c > > +++ b/fs/io_uring.c > > @@ -772,9 +772,12 @@ struct io_kiocb { > > /* use only after cleaning per-op data, see io_clean_op() */ > > struct io_completion compl; > > }; > > - > > - /* opcode allocated if it needs to store data for async defer */ > > - void *async_data; > > + union { > > + /* opcode allocated if it needs to store data for async defer */ > > + void *async_data; > > + /* used for uring-cmd, when driver needs to update in task */ > > + void (*driver_cb)(struct io_uring_cmd *cmd); > > + }; > > I don't like this at all, it's very possible that we'd need async > data for passthrough commands as well in certain cases. And what it > gets to that point, we'll have no other recourse than to un-unionize > this and pay the cost. It also means we end up with: > > > @@ -1716,7 +1719,7 @@ static void io_dismantle_req(struct io_kiocb *req) > > { > > io_clean_op(req); > > > > - if (req->async_data) > > + if (io_op_defs[req->opcode].async_size && req->async_data) > > kfree(req->async_data); > > if (req->file) > > io_put_file(req, req->file, (req->flags & REQ_F_FIXED_FILE)); > > which are also very fragile. I did not want to have it this way....but faced troubles with the more natural way of doing this. Please see below. > We already have the task work, just have the driver init and/or call a > helper to get it run from task context with the callback it desires? > > If you look at this: > > > @@ -2032,6 +2035,31 @@ static void io_req_task_submit(struct callback_head *cb) > > __io_req_task_submit(req); > > } > > > > +static void uring_cmd_work(struct callback_head *cb) > > +{ > > + struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work); > > + struct io_uring_cmd *cmd = &req->uring_cmd; > > + > > + req->driver_cb(cmd); > > +} > > +int uring_cmd_complete_in_task(struct io_uring_cmd *ioucmd, > > + void (*driver_cb)(struct io_uring_cmd *)) > > +{ > > + int ret; > > + struct io_kiocb *req = container_of(ioucmd, struct io_kiocb, uring_cmd); > > + > > + req->driver_cb = driver_cb; > > + req->task_work.func = uring_cmd_work; > > + ret = io_req_task_work_add(req); > > + if (unlikely(ret)) { > > + req->result = -ECANCELED; > > + percpu_ref_get(&req->ctx->refs); > > + io_req_task_work_add_fallback(req, io_req_task_cancel); > > + } > > + return ret; > > +} > > +EXPORT_SYMBOL(uring_cmd_complete_in_task); > > Then you're basically jumping through hoops to get that callback. > Why don't you just have: > > io_uring_schedule_task(struct io_uring_cmd *cmd, task_work_func_t cb) > { > struct io_kiocb *req = container_of(cmd, struct io_kiocb, uring_cmd); > int ret; > > req->task_work.func = cb; > ret = io_req_task_work_add(req); > if (unlikely(ret)) { > req->result = -ECANCELED; > io_req_task_work_add_fallback(req, io_req_task_cancel); > } > return ret; > } > > ? I started with that, but the problem was implementing the driver callback . The callbacks receive only one argument which is "struct callback_head *", and the driver needs to extract "io_uring_cmd *" out of it. This part - +static void uring_cmd_work(struct callback_head *cb) +{ + struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work); + struct io_uring_cmd *cmd = &req->uring_cmd; If the callback has to move to the driver (nvme), the driver needs visibility to "struct io_kiocb" which is uring-local. Do you see a better way to handle this? I also thought about keeping the driver_cb inside the unused part of uring_cmd (instead of union with req->async_data), but it had two problems - 1. uring needs to peek inside driver-part of uring_cmd to invoke this callback 2. losing precious space (I am using that space to avoid per-command dynamic-allocation in driver) > Also, please export any symbol with _GPL. I don't want non-GPL drivers > using this infrastructure. Got it. -- Kanchan