summaryrefslogtreecommitdiff
path: root/fs/fuse/dev_uring.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fuse/dev_uring.c')
-rw-r--r--fs/fuse/dev_uring.c617
1 files changed, 377 insertions, 240 deletions
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 82bf458fa9db..77c8cec43d9c 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -4,9 +4,10 @@
* Copyright (c) 2023-2024 DataDirect Networks.
*/
-#include "fuse_i.h"
+#include "dev.h"
+#include "args.h"
#include "dev_uring_i.h"
-#include "fuse_dev_i.h"
+#include "fuse_trace.h"
#include <linux/fs.h>
#include <linux/io_uring/cmd.h>
@@ -17,7 +18,8 @@ MODULE_PARM_DESC(enable_uring,
"Enable userspace communication through io-uring");
#define FUSE_URING_IOV_SEGS 2 /* header and payload */
-
+#define FUSE_URING_IOV_HEADERS 0
+#define FUSE_URING_IOV_PAYLOAD 1
bool fuse_uring_enabled(void)
{
@@ -30,6 +32,15 @@ struct fuse_uring_pdu {
static const struct fuse_iqueue_ops fuse_io_uring_ops;
+enum fuse_uring_header_type {
+ /* struct fuse_in_header / struct fuse_out_header */
+ FUSE_URING_HEADER_IN_OUT,
+ /* per op code header */
+ FUSE_URING_HEADER_OP,
+ /* struct fuse_uring_ent_in_out header */
+ FUSE_URING_HEADER_RING_ENT,
+};
+
static void uring_cmd_set_ring_ent(struct io_uring_cmd *cmd,
struct fuse_ring_ent *ring_ent)
{
@@ -50,10 +61,10 @@ static struct fuse_ring_ent *uring_cmd_to_ring_ent(struct io_uring_cmd *cmd)
static void fuse_uring_flush_bg(struct fuse_ring_queue *queue)
{
struct fuse_ring *ring = queue->ring;
- struct fuse_conn *fc = ring->fc;
+ struct fuse_chan *fch = ring->chan;
lockdep_assert_held(&queue->lock);
- lockdep_assert_held(&fc->bg_lock);
+ lockdep_assert_held(&fch->bg_lock);
/*
* Allow one bg request per queue, ignoring global fc limits.
@@ -61,14 +72,14 @@ static void fuse_uring_flush_bg(struct fuse_ring_queue *queue)
* eliminates the need for remote queue wake-ups when global
* limits are met but this queue has no more waiting requests.
*/
- while ((fc->active_background < fc->max_background ||
+ while ((fch->active_background < fch->max_background ||
!queue->active_background) &&
(!list_empty(&queue->fuse_req_bg_queue))) {
struct fuse_req *req;
req = list_first_entry(&queue->fuse_req_bg_queue,
struct fuse_req, list);
- fc->active_background++;
+ fch->active_background++;
queue->active_background++;
list_move_tail(&req->list, &queue->fuse_req_queue);
@@ -80,16 +91,18 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req,
{
struct fuse_ring_queue *queue = ent->queue;
struct fuse_ring *ring = queue->ring;
- struct fuse_conn *fc = ring->fc;
+ struct fuse_chan *fch = ring->chan;
lockdep_assert_not_held(&queue->lock);
spin_lock(&queue->lock);
ent->fuse_req = NULL;
+ list_del_init(&req->list);
if (test_bit(FR_BACKGROUND, &req->flags)) {
queue->active_background--;
- spin_lock(&fc->bg_lock);
+ spin_lock(&fch->bg_lock);
+ fuse_request_bg_finish(fch, req);
fuse_uring_flush_bg(queue);
- spin_unlock(&fc->bg_lock);
+ spin_unlock(&fch->bg_lock);
}
spin_unlock(&queue->lock);
@@ -121,28 +134,70 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring)
{
int qid;
struct fuse_ring_queue *queue;
- struct fuse_conn *fc = ring->fc;
+ struct fuse_chan *fch = ring->chan;
for (qid = 0; qid < ring->nr_queues; qid++) {
queue = READ_ONCE(ring->queues[qid]);
if (!queue)
continue;
- queue->stopped = true;
-
- WARN_ON_ONCE(ring->fc->max_background != UINT_MAX);
+ WARN_ON_ONCE(fch->max_background != UINT_MAX);
spin_lock(&queue->lock);
- spin_lock(&fc->bg_lock);
+ queue->stopped = true;
+ spin_lock(&fch->bg_lock);
fuse_uring_flush_bg(queue);
- spin_unlock(&fc->bg_lock);
+ spin_unlock(&fch->bg_lock);
spin_unlock(&queue->lock);
fuse_uring_abort_end_queue_requests(queue);
}
}
-void fuse_uring_destruct(struct fuse_conn *fc)
+static bool ent_list_request_expired(struct fuse_chan *fch, struct list_head *list)
+{
+ struct fuse_ring_ent *ent;
+ struct fuse_req *req;
+
+ ent = list_first_entry_or_null(list, struct fuse_ring_ent, list);
+ if (!ent)
+ return false;
+
+ req = ent->fuse_req;
+
+ return time_is_before_jiffies(req->create_time +
+ fch->timeout.req_timeout);
+}
+
+bool fuse_uring_request_expired(struct fuse_chan *fch)
{
- struct fuse_ring *ring = fc->ring;
+ struct fuse_ring *ring = fch->ring;
+ struct fuse_ring_queue *queue;
+ int qid;
+
+ if (!ring)
+ return false;
+
+ for (qid = 0; qid < ring->nr_queues; qid++) {
+ queue = READ_ONCE(ring->queues[qid]);
+ if (!queue)
+ continue;
+
+ spin_lock(&queue->lock);
+ if (fuse_request_expired(fch, &queue->fuse_req_queue) ||
+ fuse_request_expired(fch, &queue->fuse_req_bg_queue) ||
+ ent_list_request_expired(fch, &queue->ent_w_req_queue) ||
+ ent_list_request_expired(fch, &queue->ent_in_userspace)) {
+ spin_unlock(&queue->lock);
+ return true;
+ }
+ spin_unlock(&queue->lock);
+ }
+
+ return false;
+}
+
+void fuse_uring_destruct(struct fuse_chan *fch)
+{
+ struct fuse_ring *ring = fch->ring;
int qid;
if (!ring)
@@ -173,48 +228,51 @@ void fuse_uring_destruct(struct fuse_conn *fc)
kfree(ring->queues);
kfree(ring);
- fc->ring = NULL;
+ fch->ring = NULL;
}
/*
* Basic ring setup for this connection based on the provided configuration
*/
-static struct fuse_ring *fuse_uring_create(struct fuse_conn *fc)
+static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch)
{
struct fuse_ring *ring;
size_t nr_queues = num_possible_cpus();
struct fuse_ring *res = NULL;
size_t max_payload_size;
- ring = kzalloc(sizeof(*fc->ring), GFP_KERNEL_ACCOUNT);
+ ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT);
if (!ring)
return NULL;
- ring->queues = kcalloc(nr_queues, sizeof(struct fuse_ring_queue *),
- GFP_KERNEL_ACCOUNT);
+ ring->queues = kzalloc_objs(struct fuse_ring_queue *, nr_queues,
+ GFP_KERNEL_ACCOUNT);
if (!ring->queues)
goto out_err;
- max_payload_size = max(FUSE_MIN_READ_BUFFER, fc->max_write);
- max_payload_size = max(max_payload_size, fc->max_pages * PAGE_SIZE);
+ max_payload_size = max(FUSE_MIN_READ_BUFFER, fch->max_write);
+ max_payload_size = max(max_payload_size, fch->max_pages * PAGE_SIZE);
- spin_lock(&fc->lock);
- if (fc->ring) {
+ spin_lock(&fch->lock);
+ if (!fch->connected) {
+ spin_unlock(&fch->lock);
+ goto out_err;
+ }
+ if (fch->ring) {
/* race, another thread created the ring in the meantime */
- spin_unlock(&fc->lock);
- res = fc->ring;
+ spin_unlock(&fch->lock);
+ res = fch->ring;
goto out_err;
}
init_waitqueue_head(&ring->stop_waitq);
ring->nr_queues = nr_queues;
- ring->fc = fc;
+ ring->chan = fch;
ring->max_payload_sz = max_payload_size;
- atomic_set(&ring->queue_refs, 0);
- smp_store_release(&fc->ring, ring);
+ smp_store_release(&fch->ring, ring);
- spin_unlock(&fc->lock);
+ spin_unlock(&fch->lock);
return ring;
out_err:
@@ -226,14 +284,14 @@ out_err:
static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
int qid)
{
- struct fuse_conn *fc = ring->fc;
+ struct fuse_chan *fch = ring->chan;
struct fuse_ring_queue *queue;
struct list_head *pq;
- queue = kzalloc(sizeof(*queue), GFP_KERNEL_ACCOUNT);
+ queue = kzalloc_obj(*queue, GFP_KERNEL_ACCOUNT);
if (!queue)
return NULL;
- pq = kcalloc(FUSE_PQ_HASH_SIZE, sizeof(struct list_head), GFP_KERNEL);
+ pq = fuse_pqueue_alloc();
if (!pq) {
kfree(queue);
return NULL;
@@ -251,12 +309,12 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
INIT_LIST_HEAD(&queue->fuse_req_bg_queue);
INIT_LIST_HEAD(&queue->ent_released);
- queue->fpq.processing = pq;
fuse_pqueue_init(&queue->fpq);
+ queue->fpq.processing = pq;
- spin_lock(&fc->lock);
+ spin_lock(&fch->lock);
if (ring->queues[qid]) {
- spin_unlock(&fc->lock);
+ spin_unlock(&fch->lock);
kfree(queue->fpq.processing);
kfree(queue);
return ring->queues[qid];
@@ -266,7 +324,7 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring,
* write_once and lock as the caller mostly doesn't take the lock at all
*/
WRITE_ONCE(ring->queues[qid], queue);
- spin_unlock(&fc->lock);
+ spin_unlock(&fch->lock);
return queue;
}
@@ -309,7 +367,7 @@ static void fuse_uring_entry_teardown(struct fuse_ring_ent *ent)
spin_unlock(&queue->lock);
if (cmd)
- io_uring_cmd_done(cmd, -ENOTCONN, 0, IO_URING_F_UNLOCKED);
+ io_uring_cmd_done(cmd, -ENOTCONN, IO_URING_F_UNLOCKED);
if (req)
fuse_uring_stop_fuse_req_end(req);
@@ -353,6 +411,20 @@ static void fuse_uring_teardown_entries(struct fuse_ring_queue *queue)
FRRS_AVAILABLE);
}
+static void fuse_uring_teardown_all_queues(struct fuse_ring *ring)
+{
+ int qid;
+
+ for (qid = 0; qid < ring->nr_queues; qid++) {
+ struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]);
+
+ if (!queue)
+ continue;
+
+ fuse_uring_teardown_entries(queue);
+ }
+}
+
/*
* Log state debug info
*/
@@ -387,19 +459,10 @@ static void fuse_uring_log_ent_state(struct fuse_ring *ring)
static void fuse_uring_async_stop_queues(struct work_struct *work)
{
- int qid;
struct fuse_ring *ring =
container_of(work, struct fuse_ring, async_teardown_work.work);
- /* XXX code dup */
- for (qid = 0; qid < ring->nr_queues; qid++) {
- struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]);
-
- if (!queue)
- continue;
-
- fuse_uring_teardown_entries(queue);
- }
+ fuse_uring_teardown_all_queues(ring);
/*
* Some ring entries might be in the middle of IO operations,
@@ -417,6 +480,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work)
FUSE_URING_TEARDOWN_INTERVAL);
} else {
wake_up_all(&ring->stop_waitq);
+ fuse_conn_put(ring->chan->conn);
}
}
@@ -425,18 +489,10 @@ static void fuse_uring_async_stop_queues(struct work_struct *work)
*/
void fuse_uring_stop_queues(struct fuse_ring *ring)
{
- int qid;
-
- for (qid = 0; qid < ring->nr_queues; qid++) {
- struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]);
-
- if (!queue)
- continue;
-
- fuse_uring_teardown_entries(queue);
- }
+ fuse_uring_teardown_all_queues(ring);
if (atomic_read(&ring->queue_refs) > 0) {
+ fuse_conn_get(ring->chan->conn);
ring->teardown_time = jiffies;
INIT_DELAYED_WORK(&ring->async_teardown_work,
fuse_uring_async_stop_queues);
@@ -467,8 +523,7 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd,
queue = ent->queue;
spin_lock(&queue->lock);
if (ent->state == FRRS_AVAILABLE) {
- ent->state = FRRS_USERSPACE;
- list_move(&ent->list, &queue->ent_in_userspace);
+ list_del_init(&ent->list);
need_cmd_done = true;
ent->cmd = NULL;
}
@@ -476,7 +531,10 @@ static void fuse_uring_cancel(struct io_uring_cmd *cmd,
if (need_cmd_done) {
/* no queue lock to avoid lock order issues */
- io_uring_cmd_done(cmd, -ENOTCONN, 0, issue_flags);
+ io_uring_cmd_done(cmd, -ENOTCONN, issue_flags);
+ kfree(ent);
+ if (atomic_dec_and_test(&queue->ring->queue_refs))
+ wake_up_all(&queue->ring->stop_waitq);
}
}
@@ -491,8 +549,7 @@ static void fuse_uring_prepare_cancel(struct io_uring_cmd *cmd, int issue_flags,
* Checks for errors and stores it into the request
*/
static int fuse_uring_out_header_has_err(struct fuse_out_header *oh,
- struct fuse_req *req,
- struct fuse_conn *fc)
+ struct fuse_req *req)
{
int err;
@@ -531,6 +588,82 @@ err:
return err;
}
+static int ring_header_type_offset(enum fuse_uring_header_type type)
+{
+ switch (type) {
+ case FUSE_URING_HEADER_IN_OUT:
+ return 0;
+ case FUSE_URING_HEADER_OP:
+ return offsetof(struct fuse_uring_req_header, op_in);
+ case FUSE_URING_HEADER_RING_ENT:
+ return offsetof(struct fuse_uring_req_header, ring_ent_in_out);
+ default:
+ WARN_ONCE(1, "Invalid header type: %d\n", type);
+ return -EINVAL;
+ }
+}
+
+static int copy_header_to_ring(struct fuse_ring_ent *ent,
+ enum fuse_uring_header_type type,
+ const void *header, size_t header_size)
+{
+ int offset = ring_header_type_offset(type);
+ void __user *ring;
+
+ if (offset < 0)
+ return offset;
+
+ ring = (void __user *)ent->headers + offset;
+
+ if (copy_to_user(ring, header, header_size)) {
+ pr_info_ratelimited("Copying header to ring failed.\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int copy_header_from_ring(struct fuse_ring_ent *ent,
+ enum fuse_uring_header_type type, void *header,
+ size_t header_size)
+{
+ int offset = ring_header_type_offset(type);
+ const void __user *ring;
+
+ if (offset < 0)
+ return offset;
+
+ ring = (void __user *)ent->headers + offset;
+
+ if (copy_from_user(header, ring, header_size)) {
+ pr_info_ratelimited("Copying header from ring failed.\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int setup_fuse_copy_state(struct fuse_copy_state *cs,
+ struct fuse_ring *ring, struct fuse_req *req,
+ struct fuse_ring_ent *ent, int dir,
+ struct iov_iter *iter)
+{
+ int err;
+
+ err = import_ubuf(dir, ent->payload, ring->max_payload_sz, iter);
+ if (err) {
+ pr_info_ratelimited("fuse: Import of user buffer failed\n");
+ return err;
+ }
+
+ fuse_copy_init(cs, dir == ITER_DEST, iter);
+
+ cs->is_uring = true;
+ cs->req = req;
+
+ return 0;
+}
+
static int fuse_uring_copy_from_ring(struct fuse_ring *ring,
struct fuse_req *req,
struct fuse_ring_ent *ent)
@@ -541,26 +674,23 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring,
int err;
struct fuse_uring_ent_in_out ring_in_out;
- err = copy_from_user(&ring_in_out, &ent->headers->ring_ent_in_out,
- sizeof(ring_in_out));
+ err = copy_header_from_ring(ent, FUSE_URING_HEADER_RING_ENT,
+ &ring_in_out, sizeof(ring_in_out));
if (err)
- return -EFAULT;
+ return err;
- err = import_ubuf(ITER_SOURCE, ent->payload, ring->max_payload_sz,
- &iter);
+ err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_SOURCE, &iter);
if (err)
return err;
- fuse_copy_init(&cs, 0, &iter);
- cs.is_uring = 1;
- cs.req = req;
-
- return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz);
+ err = fuse_copy_out_args(&cs, args, ring_in_out.payload_sz);
+ fuse_copy_finish(&cs);
+ return err;
}
- /*
- * Copy data from the req to the ring buffer
- */
+/*
+ * Copy data from the req to the ring buffer
+ */
static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
struct fuse_ring_ent *ent)
{
@@ -575,15 +705,9 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
.commit_id = req->in.h.unique,
};
- err = import_ubuf(ITER_DEST, ent->payload, ring->max_payload_sz, &iter);
- if (err) {
- pr_info_ratelimited("fuse: Import of user buffer failed\n");
+ err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_DEST, &iter);
+ if (err)
return err;
- }
-
- fuse_copy_init(&cs, 1, &iter);
- cs.is_uring = 1;
- cs.req = req;
if (num_args > 0) {
/*
@@ -591,13 +715,11 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
* Some op code have that as zero size.
*/
if (args->in_args[0].size > 0) {
- err = copy_to_user(&ent->headers->op_in, in_args->value,
- in_args->size);
- if (err) {
- pr_info_ratelimited(
- "Copying the header failed.\n");
- return -EFAULT;
- }
+ err = copy_header_to_ring(ent, FUSE_URING_HEADER_OP,
+ in_args->value,
+ in_args->size);
+ if (err)
+ return err;
}
in_args++;
num_args--;
@@ -606,15 +728,15 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req,
/* copy the payload */
err = fuse_copy_args(&cs, num_args, args->in_pages,
(struct fuse_arg *)in_args, 0);
+ fuse_copy_finish(&cs);
if (err) {
pr_info_ratelimited("%s fuse_copy_args failed\n", __func__);
return err;
}
ent_in_out.payload_sz = cs.ring.copied_sz;
- err = copy_to_user(&ent->headers->ring_ent_in_out, &ent_in_out,
- sizeof(ent_in_out));
- return err ? -EFAULT : 0;
+ return copy_header_to_ring(ent, FUSE_URING_HEADER_RING_ENT,
+ &ent_in_out, sizeof(ent_in_out));
}
static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
@@ -643,14 +765,8 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent,
}
/* copy fuse_in_header */
- err = copy_to_user(&ent->headers->in_out, &req->in.h,
- sizeof(req->in.h));
- if (err) {
- err = -EFAULT;
- return err;
- }
-
- return 0;
+ return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h,
+ sizeof(req->in.h));
}
static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
@@ -659,40 +775,36 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent,
int err;
err = fuse_uring_copy_to_ring(ent, req);
- if (!err)
+ if (!err) {
set_bit(FR_SENT, &req->flags);
- else
+ trace_fuse_request_sent(req);
+ } else {
+ /*
+ * Copying the request failed. Remove the entry from the
+ * ent_w_req_queue list and terminate the request
+ */
+ spin_lock(&ent->queue->lock);
+ list_del_init(&ent->list);
+ ent->state = FRRS_INVALID;
+ spin_unlock(&ent->queue->lock);
+
fuse_uring_req_end(ent, req, err);
+ }
return err;
}
-/*
- * Write data to the ring buffer and send the request to userspace,
- * userspace will read it
- * This is comparable with classical read(/dev/fuse)
- */
-static int fuse_uring_send_next_to_ring(struct fuse_ring_ent *ent,
- struct fuse_req *req,
- unsigned int issue_flags)
+/* Used to find the request on SQE commit */
+static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent)
{
struct fuse_ring_queue *queue = ent->queue;
- int err;
- struct io_uring_cmd *cmd;
-
- err = fuse_uring_prepare_send(ent, req);
- if (err)
- return err;
-
- spin_lock(&queue->lock);
- cmd = ent->cmd;
- ent->cmd = NULL;
- ent->state = FRRS_USERSPACE;
- list_move(&ent->list, &queue->ent_in_userspace);
- spin_unlock(&queue->lock);
+ struct fuse_pqueue *fpq = &queue->fpq;
+ unsigned int hash;
+ struct fuse_req *req = ent->fuse_req;
- io_uring_cmd_done(cmd, 0, 0, issue_flags);
- return 0;
+ req->ring_entry = ent;
+ hash = fuse_req_hash(req->in.h.unique);
+ list_move_tail(&req->list, &fpq->processing[hash]);
}
/*
@@ -706,19 +818,6 @@ static void fuse_uring_ent_avail(struct fuse_ring_ent *ent,
ent->state = FRRS_AVAILABLE;
}
-/* Used to find the request on SQE commit */
-static void fuse_uring_add_to_pq(struct fuse_ring_ent *ent,
- struct fuse_req *req)
-{
- struct fuse_ring_queue *queue = ent->queue;
- struct fuse_pqueue *fpq = &queue->fpq;
- unsigned int hash;
-
- req->ring_entry = ent;
- hash = fuse_req_hash(req->in.h.unique);
- list_move_tail(&req->list, &fpq->processing[hash]);
-}
-
/*
* Assign a fuse queue entry to the given entry
*/
@@ -726,8 +825,6 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
struct fuse_req *req)
{
struct fuse_ring_queue *queue = ent->queue;
- struct fuse_conn *fc = req->fm->fc;
- struct fuse_iqueue *fiq = &fc->iq;
lockdep_assert_held(&queue->lock);
@@ -737,13 +834,14 @@ static void fuse_uring_add_req_to_ring_ent(struct fuse_ring_ent *ent,
ent->state);
}
- spin_lock(&fiq->lock);
clear_bit(FR_PENDING, &req->flags);
- spin_unlock(&fiq->lock);
+
+ /* Until fuse_uring_add_to_pq() the req is not attached to any list */
+ list_del_init(&req->list);
+
ent->fuse_req = req;
ent->state = FRRS_FUSE_REQ;
- list_move(&ent->list, &queue->ent_w_req_queue);
- fuse_uring_add_to_pq(ent, req);
+ list_move_tail(&ent->list, &queue->ent_w_req_queue);
}
/* Fetch the next fuse request if available */
@@ -773,17 +871,13 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req,
unsigned int issue_flags)
{
struct fuse_ring *ring = ent->queue->ring;
- struct fuse_conn *fc = ring->fc;
- ssize_t err = 0;
+ ssize_t err = -EFAULT;
- err = copy_from_user(&req->out.h, &ent->headers->in_out,
- sizeof(req->out.h));
- if (err) {
- req->out.h.error = -EFAULT;
+ if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h,
+ sizeof(req->out.h)))
goto out;
- }
- err = fuse_uring_out_header_has_err(&req->out.h, req, fc);
+ err = fuse_uring_out_header_has_err(&req->out.h, req);
if (err) {
/* req->out.h.error already set */
goto out;
@@ -795,11 +889,13 @@ out:
}
/*
- * Get the next fuse req and send it
+ * Get the next fuse req.
+ *
+ * Returns true if the next fuse request has been assigned to the ent.
+ * Else, there is no next fuse request and this returns false.
*/
-static void fuse_uring_next_fuse_req(struct fuse_ring_ent *ent,
- struct fuse_ring_queue *queue,
- unsigned int issue_flags)
+static bool fuse_uring_get_next_fuse_req(struct fuse_ring_ent *ent,
+ struct fuse_ring_queue *queue)
{
int err;
struct fuse_req *req;
@@ -811,10 +907,12 @@ retry:
spin_unlock(&queue->lock);
if (req) {
- err = fuse_uring_send_next_to_ring(ent, req, issue_flags);
+ err = fuse_uring_prepare_send(ent, req);
if (err)
goto retry;
}
+
+ return req != NULL;
}
static int fuse_ring_ent_set_commit(struct fuse_ring_ent *ent)
@@ -832,14 +930,30 @@ static int fuse_ring_ent_set_commit(struct fuse_ring_ent *ent)
return 0;
}
+static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd,
+ ssize_t ret, unsigned int issue_flags)
+{
+ struct fuse_ring_queue *queue = ent->queue;
+
+ spin_lock(&queue->lock);
+ ent->state = FRRS_USERSPACE;
+ list_move_tail(&ent->list, &queue->ent_in_userspace);
+ ent->cmd = NULL;
+ fuse_uring_add_to_pq(ent);
+ spin_unlock(&queue->lock);
+
+ io_uring_cmd_done(cmd, ret, issue_flags);
+}
+
/* FUSE_URING_CMD_COMMIT_AND_FETCH handler */
static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags,
- struct fuse_conn *fc)
+ struct fuse_chan *fch)
{
- const struct fuse_uring_cmd_req *cmd_req = io_uring_sqe_cmd(cmd->sqe);
+ const struct fuse_uring_cmd_req *cmd_req = io_uring_sqe128_cmd(cmd->sqe,
+ struct fuse_uring_cmd_req);
struct fuse_ring_ent *ent;
int err;
- struct fuse_ring *ring = fc->ring;
+ struct fuse_ring *ring = fch->ring;
struct fuse_ring_queue *queue;
uint64_t commit_id = READ_ONCE(cmd_req->commit_id);
unsigned int qid = READ_ONCE(cmd_req->qid);
@@ -858,10 +972,15 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags,
return err;
fpq = &queue->fpq;
- if (!READ_ONCE(fc->connected) || READ_ONCE(queue->stopped))
+ if (!READ_ONCE(fch->connected))
return err;
spin_lock(&queue->lock);
+ if (unlikely(queue->stopped)) {
+ spin_unlock(&queue->lock);
+ return err;
+ }
+
/* Find a request based on the unique ID of the fuse request
* This should get revised, as it needs a hash calculation and list
* search. And full struct fuse_pqueue is needed (memory overhead).
@@ -884,9 +1003,7 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags,
pr_info_ratelimited("qid=%d commit_id %llu state %d",
queue->qid, commit_id, ent->state);
spin_unlock(&queue->lock);
- req->out.h.error = err;
- clear_bit(FR_SENT, &req->flags);
- fuse_request_end(req);
+ fuse_uring_req_end(ent, req, err);
return err;
}
@@ -903,7 +1020,8 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags,
* and fetching is done in one step vs legacy fuse, which has separated
* read (fetch request) and write (commit result).
*/
- fuse_uring_next_fuse_req(ent, queue, issue_flags);
+ if (fuse_uring_get_next_fuse_req(ent, queue))
+ fuse_uring_send(ent, cmd, 0, issue_flags);
return 0;
}
@@ -935,14 +1053,25 @@ static bool is_ring_ready(struct fuse_ring *ring, int current_qid)
/*
* fuse_uring_req_fetch command handling
*/
-static void fuse_uring_do_register(struct fuse_ring_ent *ent,
- struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static int fuse_uring_do_register(struct fuse_ring_ent *ent,
+ struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
{
struct fuse_ring_queue *queue = ent->queue;
struct fuse_ring *ring = queue->ring;
- struct fuse_conn *fc = ring->fc;
- struct fuse_iqueue *fiq = &fc->iq;
+ struct fuse_chan *fch = ring->chan;
+ struct fuse_iqueue *fiq = &fch->iq;
+
+ spin_lock(&fch->lock);
+ /* abort teardown path is running or has run */
+ if (!fch->connected) {
+ spin_unlock(&fch->lock);
+ if (atomic_dec_and_test(&ring->queue_refs))
+ wake_up_all(&ring->stop_waitq);
+ kfree(ent);
+ return -ECONNABORTED;
+ }
+ spin_unlock(&fch->lock);
fuse_uring_prepare_cancel(cmd, issue_flags, ent);
@@ -951,20 +1080,21 @@ static void fuse_uring_do_register(struct fuse_ring_ent *ent,
fuse_uring_ent_avail(ent, queue);
spin_unlock(&queue->lock);
- if (!ring->ready) {
+ if (!READ_ONCE(ring->ready)) {
bool ready = is_ring_ready(ring, queue->qid);
if (ready) {
WRITE_ONCE(fiq->ops, &fuse_io_uring_ops);
- WRITE_ONCE(ring->ready, true);
- wake_up_all(&fc->blocked_waitq);
+ smp_store_release(&ring->ready, true);
+ wake_up_all(&fch->blocked_waitq);
}
}
+ return 0;
}
/*
- * sqe->addr is a ptr to an iovec array, iov[0] has the headers, iov[1]
- * the payload
+ * sqe->addr is a ptr to an iovec array, iov[FUSE_URING_IOV_HEADERS] has the
+ * headers, iov[FUSE_URING_IOV_PAYLOAD] the payload
*/
static int fuse_uring_get_iovec_from_sqe(const struct io_uring_sqe *sqe,
struct iovec iov[FUSE_URING_IOV_SEGS])
@@ -994,8 +1124,8 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd,
{
struct fuse_ring *ring = queue->ring;
struct fuse_ring_ent *ent;
- size_t payload_size;
struct iovec iov[FUSE_URING_IOV_SEGS];
+ struct iovec *headers, *payload;
int err;
err = fuse_uring_get_iovec_from_sqe(cmd->sqe, iov);
@@ -1006,28 +1136,29 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd,
}
err = -EINVAL;
- if (iov[0].iov_len < sizeof(struct fuse_uring_req_header)) {
- pr_info_ratelimited("Invalid header len %zu\n", iov[0].iov_len);
+ headers = &iov[FUSE_URING_IOV_HEADERS];
+ if (headers->iov_len < sizeof(struct fuse_uring_req_header)) {
+ pr_info_ratelimited("Invalid header len %zu\n", headers->iov_len);
return ERR_PTR(err);
}
- payload_size = iov[1].iov_len;
- if (payload_size < ring->max_payload_sz) {
+ payload = &iov[FUSE_URING_IOV_PAYLOAD];
+ if (payload->iov_len < ring->max_payload_sz) {
pr_info_ratelimited("Invalid req payload len %zu\n",
- payload_size);
+ payload->iov_len);
return ERR_PTR(err);
}
err = -ENOMEM;
- ent = kzalloc(sizeof(*ent), GFP_KERNEL_ACCOUNT);
+ ent = kzalloc_obj(*ent, GFP_KERNEL_ACCOUNT);
if (!ent)
return ERR_PTR(err);
INIT_LIST_HEAD(&ent->list);
ent->queue = queue;
- ent->headers = iov[0].iov_base;
- ent->payload = iov[1].iov_base;
+ ent->headers = headers->iov_base;
+ ent->payload = payload->iov_base;
atomic_inc(&ring->queue_refs);
return ent;
@@ -1038,10 +1169,11 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd,
* entry as "ready to get fuse requests" on the queue
*/
static int fuse_uring_register(struct io_uring_cmd *cmd,
- unsigned int issue_flags, struct fuse_conn *fc)
+ unsigned int issue_flags, struct fuse_chan *fch)
{
- const struct fuse_uring_cmd_req *cmd_req = io_uring_sqe_cmd(cmd->sqe);
- struct fuse_ring *ring = smp_load_acquire(&fc->ring);
+ const struct fuse_uring_cmd_req *cmd_req = io_uring_sqe128_cmd(cmd->sqe,
+ struct fuse_uring_cmd_req);
+ struct fuse_ring *ring = smp_load_acquire(&fch->ring);
struct fuse_ring_queue *queue;
struct fuse_ring_ent *ent;
int err;
@@ -1049,7 +1181,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
err = -ENOMEM;
if (!ring) {
- ring = fuse_uring_create(fc);
+ ring = fuse_uring_create(fch);
if (!ring)
return err;
}
@@ -1075,9 +1207,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
if (IS_ERR(ent))
return PTR_ERR(ent);
- fuse_uring_do_register(ent, cmd, issue_flags);
-
- return 0;
+ return fuse_uring_do_register(ent, cmd, issue_flags);
}
/*
@@ -1087,7 +1217,7 @@ static int fuse_uring_register(struct io_uring_cmd *cmd,
int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
{
struct fuse_dev *fud;
- struct fuse_conn *fc;
+ struct fuse_chan *fch;
u32 cmd_op = cmd->cmd_op;
int err;
@@ -1101,43 +1231,43 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
return -EINVAL;
fud = fuse_get_dev(cmd->file);
- if (!fud) {
+ if (IS_ERR(fud)) {
pr_info_ratelimited("No fuse device found\n");
- return -ENOTCONN;
+ return PTR_ERR(fud);
}
- fc = fud->fc;
+ fch = fud->chan;
/* Once a connection has io-uring enabled on it, it can't be disabled */
- if (!enable_uring && !fc->io_uring) {
+ if (!enable_uring && !fch->io_uring) {
pr_info_ratelimited("fuse-io-uring is disabled\n");
return -EOPNOTSUPP;
}
- if (fc->aborted)
+ if (fch->abort_with_err)
return -ECONNABORTED;
- if (!fc->connected)
+ if (!fch->connected)
return -ENOTCONN;
/*
* fuse_uring_register() needs the ring to be initialized,
* we need to know the max payload size
*/
- if (!fc->initialized)
+ if (!fch->initialized)
return -EAGAIN;
switch (cmd_op) {
case FUSE_IO_URING_CMD_REGISTER:
- err = fuse_uring_register(cmd, issue_flags, fc);
+ err = fuse_uring_register(cmd, issue_flags, fch);
if (err) {
pr_info_once("FUSE_IO_URING_CMD_REGISTER failed err=%d\n",
err);
- fc->io_uring = 0;
- wake_up_all(&fc->blocked_waitq);
+ fch->io_uring = 0;
+ wake_up_all(&fch->blocked_waitq);
return err;
}
break;
case FUSE_IO_URING_CMD_COMMIT_AND_FETCH:
- err = fuse_uring_commit_fetch(cmd, issue_flags, fc);
+ err = fuse_uring_commit_fetch(cmd, issue_flags, fch);
if (err) {
pr_info_once("FUSE_IO_URING_COMMIT_AND_FETCH failed err=%d\n",
err);
@@ -1151,43 +1281,41 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
return -EIOCBQUEUED;
}
-static void fuse_uring_send(struct fuse_ring_ent *ent, struct io_uring_cmd *cmd,
- ssize_t ret, unsigned int issue_flags)
-{
- struct fuse_ring_queue *queue = ent->queue;
-
- spin_lock(&queue->lock);
- ent->state = FRRS_USERSPACE;
- list_move(&ent->list, &queue->ent_in_userspace);
- ent->cmd = NULL;
- spin_unlock(&queue->lock);
-
- io_uring_cmd_done(cmd, ret, 0, issue_flags);
-}
-
/*
* This prepares and sends the ring request in fuse-uring task context.
* User buffers are not mapped yet - the application does not have permission
* to write to it - this has to be executed in ring task context.
*/
-static void fuse_uring_send_in_task(struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct fuse_ring_ent *ent = uring_cmd_to_ring_ent(cmd);
struct fuse_ring_queue *queue = ent->queue;
int err;
- if (!(issue_flags & IO_URING_F_TASK_DEAD)) {
+ if (!tw.cancel) {
err = fuse_uring_prepare_send(ent, ent->fuse_req);
if (err) {
- fuse_uring_next_fuse_req(ent, queue, issue_flags);
- return;
+ if (!fuse_uring_get_next_fuse_req(ent, queue))
+ return;
+ err = 0;
}
+ fuse_uring_send(ent, cmd, err, issue_flags);
} else {
err = -ECANCELED;
- }
- fuse_uring_send(ent, cmd, err, issue_flags);
+ spin_lock(&queue->lock);
+ list_del_init(&ent->list);
+ spin_unlock(&queue->lock);
+
+ io_uring_cmd_done(cmd, err, issue_flags);
+
+ fuse_uring_req_end(ent, ent->fuse_req, err);
+ kfree(ent);
+ if (atomic_dec_and_test(&queue->ring->queue_refs))
+ wake_up_all(&queue->ring->stop_waitq);
+ }
}
static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring)
@@ -1219,8 +1347,7 @@ static void fuse_uring_dispatch_ent(struct fuse_ring_ent *ent)
/* queue a fuse request and send it if a ring entry is available */
void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req)
{
- struct fuse_conn *fc = req->fm->fc;
- struct fuse_ring *ring = fc->ring;
+ struct fuse_ring *ring = req->chan->ring;
struct fuse_ring_queue *queue;
struct fuse_ring_ent *ent = NULL;
int err;
@@ -1230,14 +1357,15 @@ void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req)
if (!queue)
goto err;
- if (req->in.h.opcode != FUSE_NOTIFY_REPLY)
- req->in.h.unique = fuse_get_unique(fiq);
+ fuse_request_assign_unique(fiq, req);
spin_lock(&queue->lock);
err = -ENOTCONN;
if (unlikely(queue->stopped))
goto err_unlock;
+ set_bit(FR_URING, &req->flags);
+ req->ring_queue = queue;
ent = list_first_entry_or_null(&queue->ent_avail_queue,
struct fuse_ring_ent, list);
if (ent)
@@ -1261,8 +1389,8 @@ err:
bool fuse_uring_queue_bq_req(struct fuse_req *req)
{
- struct fuse_conn *fc = req->fm->fc;
- struct fuse_ring *ring = fc->ring;
+ struct fuse_chan *fch = req->chan;
+ struct fuse_ring *ring = fch->ring;
struct fuse_ring_queue *queue;
struct fuse_ring_ent *ent = NULL;
@@ -1276,16 +1404,18 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
return false;
}
+ set_bit(FR_URING, &req->flags);
+ req->ring_queue = queue;
list_add_tail(&req->list, &queue->fuse_req_bg_queue);
ent = list_first_entry_or_null(&queue->ent_avail_queue,
struct fuse_ring_ent, list);
- spin_lock(&fc->bg_lock);
- fc->num_background++;
- if (fc->num_background == fc->max_background)
- fc->blocked = 1;
+ spin_lock(&fch->bg_lock);
+ fch->num_background++;
+ if (fch->num_background == fch->max_background)
+ fch->blocked = 1;
fuse_uring_flush_bg(queue);
- spin_unlock(&fc->bg_lock);
+ spin_unlock(&fch->bg_lock);
/*
* Due to bg_queue flush limits there might be other bg requests
@@ -1306,6 +1436,13 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req)
return true;
}
+bool fuse_uring_remove_pending_req(struct fuse_req *req)
+{
+ struct fuse_ring_queue *queue = req->ring_queue;
+
+ return fuse_remove_pending_req(req, &queue->lock);
+}
+
static const struct fuse_iqueue_ops fuse_io_uring_ops = {
/* should be send over io-uring as enhancement */
.send_forget = fuse_dev_queue_forget,