Skip to content

Commit 529395d

Browse files
Peter-JanGootzenMiklos Szeredi
authored andcommitted
virtio-fs: add multi-queue support
This commit creates a multi-queue mapping at device bring-up. The driver first attempts to use the existing MSI-X interrupt affinities (previously disabled), and if not present, will distribute the request queues evenly over the CPUs. If the latter fails as well, all CPUs are mapped to request queue zero. When a request is handed from FUSE to the virtio-fs device driver, the driver will use the current CPU to index into the multi-queue mapping and determine the optimal request queue to use. We measured the performance of this patch with the fio benchmarking tool, increasing the number of queues results in a significant speedup for both read and write operations, demonstrating the effectiveness of multi-queue support. Host: - Dell PowerEdge R760 - CPU: Intel(R) Xeon(R) Gold 6438M, 128 cores - VM: KVM with 32 cores Virtio-fs device: - BlueField-3 DPU - CPU: ARM Cortex-A78AE, 16 cores - One thread per queue, each busy polling on one request queue - Each queue is 1024 descriptors deep Workload: - fio, sequential read or write, ioengine=libaio, numjobs=32, 4GiB file per job, iodepth=8, bs=256KiB, runtime=30s Performance Results: +===========================+==========+===========+ | Number of queues | Fio read | Fio write | +===========================+==========+===========+ | 1 request queue (GiB/s) | 6.1 | 4.6 | +---------------------------+----------+-----------+ | 8 request queues (GiB/s) | 25.8 | 10.3 | +---------------------------+----------+-----------+ | 16 request queues (GiB/s) | 30.9 | 19.5 | +---------------------------+----------+-----------+ | 32 request queue (GiB/s) | 33.2 | 22.6 | +---------------------------+----------+-----------+ | Speedup | 5.5x | 5x | +---------------=-----------+----------+-----------+ Signed-off-by: Peter-Jan Gootzen <pgootzen@nvidia.com> Signed-off-by: Yoray Zack <yorayz@nvidia.com> Signed-off-by: Max Gurtovoy <mgurtovoy@nvidia.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 103c2de commit 529395d

File tree

1 file changed

+62
-8
lines changed

1 file changed

+62
-8
lines changed

fs/fuse/virtio_fs.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <linux/fs.h>
88
#include <linux/dax.h>
99
#include <linux/pci.h>
10+
#include <linux/interrupt.h>
11+
#include <linux/group_cpus.h>
1012
#include <linux/pfn_t.h>
1113
#include <linux/memremap.h>
1214
#include <linux/module.h>
@@ -67,6 +69,8 @@ struct virtio_fs {
6769
unsigned int num_request_queues; /* number of request queues */
6870
struct dax_device *dax_dev;
6971

72+
unsigned int *mq_map; /* index = cpu id, value = request vq id */
73+
7074
/* DAX memory window where file contents are mapped */
7175
void *window_kaddr;
7276
phys_addr_t window_phys_addr;
@@ -185,6 +189,7 @@ static void virtio_fs_ktype_release(struct kobject *kobj)
185189
{
186190
struct virtio_fs *vfs = container_of(kobj, struct virtio_fs, kobj);
187191

192+
kfree(vfs->mq_map);
188193
kfree(vfs->vqs);
189194
kfree(vfs);
190195
}
@@ -706,6 +711,44 @@ static void virtio_fs_requests_done_work(struct work_struct *work)
706711
}
707712
}
708713

714+
static void virtio_fs_map_queues(struct virtio_device *vdev, struct virtio_fs *fs)
715+
{
716+
const struct cpumask *mask, *masks;
717+
unsigned int q, cpu;
718+
719+
/* First attempt to map using existing transport layer affinities
720+
* e.g. PCIe MSI-X
721+
*/
722+
if (!vdev->config->get_vq_affinity)
723+
goto fallback;
724+
725+
for (q = 0; q < fs->num_request_queues; q++) {
726+
mask = vdev->config->get_vq_affinity(vdev, VQ_REQUEST + q);
727+
if (!mask)
728+
goto fallback;
729+
730+
for_each_cpu(cpu, mask)
731+
fs->mq_map[cpu] = q;
732+
}
733+
734+
return;
735+
fallback:
736+
/* Attempt to map evenly in groups over the CPUs */
737+
masks = group_cpus_evenly(fs->num_request_queues);
738+
/* If even this fails we default to all CPUs use queue zero */
739+
if (!masks) {
740+
for_each_possible_cpu(cpu)
741+
fs->mq_map[cpu] = 0;
742+
return;
743+
}
744+
745+
for (q = 0; q < fs->num_request_queues; q++) {
746+
for_each_cpu(cpu, &masks[q])
747+
fs->mq_map[cpu] = q;
748+
}
749+
kfree(masks);
750+
}
751+
709752
/* Virtqueue interrupt handler */
710753
static void virtio_fs_vq_done(struct virtqueue *vq)
711754
{
@@ -742,6 +785,11 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
742785
{
743786
struct virtqueue **vqs;
744787
vq_callback_t **callbacks;
788+
/* Specify pre_vectors to ensure that the queues before the
789+
* request queues (e.g. hiprio) don't claim any of the CPUs in
790+
* the multi-queue mapping and interrupt affinities
791+
*/
792+
struct irq_affinity desc = { .pre_vectors = VQ_REQUEST };
745793
const char **names;
746794
unsigned int i;
747795
int ret = 0;
@@ -763,7 +811,9 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
763811
callbacks = kmalloc_array(fs->nvqs, sizeof(callbacks[VQ_HIPRIO]),
764812
GFP_KERNEL);
765813
names = kmalloc_array(fs->nvqs, sizeof(names[VQ_HIPRIO]), GFP_KERNEL);
766-
if (!vqs || !callbacks || !names) {
814+
fs->mq_map = kcalloc_node(nr_cpu_ids, sizeof(*fs->mq_map), GFP_KERNEL,
815+
dev_to_node(&vdev->dev));
816+
if (!vqs || !callbacks || !names || !fs->mq_map) {
767817
ret = -ENOMEM;
768818
goto out;
769819
}
@@ -783,7 +833,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
783833
names[i] = fs->vqs[i].name;
784834
}
785835

786-
ret = virtio_find_vqs(vdev, fs->nvqs, vqs, callbacks, names, NULL);
836+
ret = virtio_find_vqs(vdev, fs->nvqs, vqs, callbacks, names, &desc);
787837
if (ret < 0)
788838
goto out;
789839

@@ -795,8 +845,10 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
795845
kfree(names);
796846
kfree(callbacks);
797847
kfree(vqs);
798-
if (ret)
848+
if (ret) {
799849
kfree(fs->vqs);
850+
kfree(fs->mq_map);
851+
}
800852
return ret;
801853
}
802854

@@ -942,7 +994,7 @@ static int virtio_fs_probe(struct virtio_device *vdev)
942994
if (ret < 0)
943995
goto out;
944996

945-
/* TODO vq affinity */
997+
virtio_fs_map_queues(vdev, fs);
946998

947999
ret = virtio_fs_setup_dax(vdev, fs);
9481000
if (ret < 0)
@@ -1291,7 +1343,7 @@ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
12911343
static void virtio_fs_wake_pending_and_unlock(struct fuse_iqueue *fiq)
12921344
__releases(fiq->lock)
12931345
{
1294-
unsigned int queue_id = VQ_REQUEST; /* TODO multiqueue */
1346+
unsigned int queue_id;
12951347
struct virtio_fs *fs;
12961348
struct fuse_req *req;
12971349
struct virtio_fs_vq *fsvq;
@@ -1305,11 +1357,13 @@ __releases(fiq->lock)
13051357
spin_unlock(&fiq->lock);
13061358

13071359
fs = fiq->priv;
1360+
queue_id = VQ_REQUEST + fs->mq_map[raw_smp_processor_id()];
13081361

1309-
pr_debug("%s: opcode %u unique %#llx nodeid %#llx in.len %u out.len %u\n",
1310-
__func__, req->in.h.opcode, req->in.h.unique,
1362+
pr_debug("%s: opcode %u unique %#llx nodeid %#llx in.len %u out.len %u queue_id %u\n",
1363+
__func__, req->in.h.opcode, req->in.h.unique,
13111364
req->in.h.nodeid, req->in.h.len,
1312-
fuse_len_args(req->args->out_numargs, req->args->out_args));
1365+
fuse_len_args(req->args->out_numargs, req->args->out_args),
1366+
queue_id);
13131367

13141368
fsvq = &fs->vqs[queue_id];
13151369
ret = virtio_fs_enqueue_req(fsvq, req, false);

0 commit comments

Comments
 (0)