[PATCH] fuse: select alternative io_uring queue if local queue is full

Li Wang posted 1 patch 1 week, 4 days ago
fs/fuse/dev_uring.c | 47 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 46 insertions(+), 1 deletion(-)
[PATCH] fuse: select alternative io_uring queue if local queue is full
Posted by Li Wang 1 week, 4 days ago
fuse_uring_task_to_queue() always maps requests to the queue for
task_cpu(current). A single-threaded submitter with high queue depth
could leave other per-CPU queues idle while one queue handles all
traffic.

Prefer the local queue when it has available ring entries. If the local
queue is full, try other queues on the same NUMA node before falling
back to use the local queue.

Signed-off-by: Li Wang <liwang@kylinos.cn>
Tested-by: Jinxu Du <dujinxiu@kylinos.cn>
---
Benchmark results

Machine: dual 64-core XEON GOLD 6548Y CPUs, 512GB RAM, 4TB NVME
Program: fio ioengine=libaio numjobs=1 direct=1 iodepth=256

Pattern	                  Baseline    Patched    Diff
read bs=512K (MB/s)       1146        1910       66.67%
write bs=512K (MB/s)      1929        2486       28.88%
randread bs=4K (KIOPS)    21.5        29.7       38.14%
randwrite bs=4K (KIOPS)   92.1        134        45.49%

 fs/fuse/dev_uring.c | 47 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 77c8cec43d9c..c36438e79f8d 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -11,6 +11,7 @@
 
 #include <linux/fs.h>
 #include <linux/io_uring/cmd.h>
+#include <linux/topology.h>
 
 static bool __read_mostly enable_uring;
 module_param(enable_uring, bool, 0644);
@@ -1318,10 +1319,46 @@ static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw)
 	}
 }
 
+static inline bool fuse_uring_queue_has_avail_ent(struct fuse_ring_queue *queue)
+{
+	bool has;
+
+	if (!queue)
+		return false;
+
+	spin_lock(&queue->lock);
+	has = !list_empty(&queue->ent_avail_queue);
+	spin_unlock(&queue->lock);
+
+	return has;
+}
+
+static struct fuse_ring_queue *
+fuse_uring_find_avail_queue(struct fuse_ring *ring, int node, unsigned int skip)
+{
+	unsigned int i;
+	struct fuse_ring_queue *queue;
+
+	for (i = 0; i < ring->nr_queues; i++) {
+
+		if (i == skip)
+			continue;
+		if (node >= 0 && cpu_to_node(i) != node)
+			continue;
+
+		queue = ring->queues[i];
+		if (fuse_uring_queue_has_avail_ent(queue))
+			return queue;
+	}
+
+	return NULL;
+}
+
 static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring)
 {
 	unsigned int qid;
 	struct fuse_ring_queue *queue;
+	int node;
 
 	qid = task_cpu(current);
 
@@ -1331,7 +1368,15 @@ static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring)
 		qid = 0;
 
 	queue = ring->queues[qid];
-	WARN_ONCE(!queue, "Missing queue for qid %d\n", qid);
+	if (fuse_uring_queue_has_avail_ent(queue))
+		return queue;
+
+	node = cpu_to_node(qid);
+	queue = fuse_uring_find_avail_queue(ring, node, qid);
+	if (!queue) {
+		queue = ring->queues[qid];
+		WARN_ONCE(!queue, "Missing queue for qid %d\n", qid);
+	}
 
 	return queue;
 }
-- 
2.34.1
Re: [PATCH] fuse: select alternative io_uring queue if local queue is full
Posted by Joanne Koong 1 week, 3 days ago
On Tue, Jul 14, 2026 at 12:33 AM Li Wang <liwang@kylinos.cn> wrote:
>
> fuse_uring_task_to_queue() always maps requests to the queue for
> task_cpu(current). A single-threaded submitter with high queue depth
> could leave other per-CPU queues idle while one queue handles all
> traffic.
>
> Prefer the local queue when it has available ring entries. If the local
> queue is full, try other queues on the same NUMA node before falling
> back to use the local queue.

Hi Li,

I think this is already covered by Bernd's changes in his reduced
queues patchset [1].

Thanks,
Joanne

[1] https://lore.kernel.org/fuse-devel/20260529-reduced-nr-ring-queues_3-v5-0-1dc08c2fccf6@bsbernd.com/
Re: [PATCH] fuse: select alternative io_uring queue if local queue is full
Posted by Bernd Schubert 1 week, 2 days ago

On 7/15/26 19:51, Joanne Koong wrote:
> On Tue, Jul 14, 2026 at 12:33 AM Li Wang <liwang@kylinos.cn> wrote:
>>
>> fuse_uring_task_to_queue() always maps requests to the queue for
>> task_cpu(current). A single-threaded submitter with high queue depth
>> could leave other per-CPU queues idle while one queue handles all
>> traffic.
>>
>> Prefer the local queue when it has available ring entries. If the local
>> queue is full, try other queues on the same NUMA node before falling
>> back to use the local queue.
> 
> Hi Li,
> 
> I think this is already covered by Bernd's changes in his reduced
> queues patchset [1].
> 
> Thanks,
> Joanne
> 
> [1] https://lore.kernel.org/fuse-devel/20260529-reduced-nr-ring-queues_3-v5-0-1dc08c2fccf6@bsbernd.com/

It is a slightly different approach, but both have the same goal
to get more balanced queue/core usage.

I will submit a new version once I'm back from vacation.


Thanks,
Bernd