Unverified Commit 3ba3aaf2 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!13991 virtio-fs: introduce multi-queue support

Merge Pull Request from: @ci-robot 
 
PR sync from: Shuai Wu <wushuai51@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/VHD4V2XELQ5H52I4NH7DEJQWBKQFKCO5/ 
Patch1 to patch3 are the interfaces for exporting virtio-fs tags
from sysfs during multi-queue modification.
patches 4 to 5 add multi-queue support to the virtio-fs driver
to enhance performance. Although the multi-queue feature has been
in the virtio-fs specification since its inception, the current
implementation in the Linux kernel creates multiple queues but
enqueues requests to only the first request queue.

Shuai Wu (5):
  virtiofs: export filesystem tags through sysfs
  virtiofs: emit uevents on filesystem events
  virtiofs: include a newline in sysfs tag
  virtio-fs: limit number of request queues
  virtio-fs: add multi-queue support


-- 
2.33.0
 
https://gitee.com/openeuler/kernel/issues/IB81UH 
 
Link:https://gitee.com/openeuler/kernel/pulls/13991

 

Reviewed-by: default avatarYu Kuai <yukuai3@huawei.com>
Reviewed-by: default avatarzhangyi (F) <yi.zhang@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents fa76b7f1 a67a4524
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
What:		/sys/fs/virtiofs/<n>/tag
Date:		Feb 2024
Contact:	virtio-fs@lists.linux.dev
Description:
		[RO] The mount "tag" that can be used to mount this filesystem.

What:		/sys/fs/virtiofs/<n>/device
Date:		Feb 2024
Contact:	virtio-fs@lists.linux.dev
Description:
		Symlink to the virtio device that exports this filesystem.
+170 −30
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@
#include <linux/fs.h>
#include <linux/dax.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/group_cpus.h>
#include <linux/pfn_t.h>
#include <linux/memremap.h>
#include <linux/module.h>
@@ -31,6 +33,9 @@
static DEFINE_MUTEX(virtio_fs_mutex);
static LIST_HEAD(virtio_fs_instances);

/* The /sys/fs/virtio_fs/ kset */
static struct kset *virtio_fs_kset;

enum {
	VQ_HIPRIO,
	VQ_REQUEST
@@ -55,7 +60,7 @@ struct virtio_fs_vq {

/* A virtio-fs device instance */
struct virtio_fs {
	struct kref refcount;
	struct kobject kobj;
	struct list_head list;    /* on virtio_fs_instances */
	char *tag;
	struct virtio_fs_vq *vqs;
@@ -63,6 +68,8 @@ struct virtio_fs {
	unsigned int num_request_queues; /* number of request queues */
	struct dax_device *dax_dev;

	unsigned int *mq_map; /* index = cpu id, value = request vq id */

	/* DAX memory window where file contents are mapped */
	void *window_kaddr;
	phys_addr_t window_phys_addr;
@@ -161,18 +168,41 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq)
		complete(&fsvq->in_flight_zero);
}

static void release_virtio_fs_obj(struct kref *ref)
static ssize_t tag_show(struct kobject *kobj,
		struct kobj_attribute *attr, char *buf)
{
	struct virtio_fs *fs = container_of(kobj, struct virtio_fs, kobj);

	return sysfs_emit(buf, "%s\n", fs->tag);
}

static struct kobj_attribute virtio_fs_tag_attr = __ATTR_RO(tag);

static struct attribute *virtio_fs_attrs[] = {
	&virtio_fs_tag_attr.attr,
	NULL
};
ATTRIBUTE_GROUPS(virtio_fs);

static void virtio_fs_ktype_release(struct kobject *kobj)
{
	struct virtio_fs *vfs = container_of(ref, struct virtio_fs, refcount);
	struct virtio_fs *vfs = container_of(kobj, struct virtio_fs, kobj);

	kfree(vfs->mq_map);
	kfree(vfs->vqs);
	kfree(vfs);
}

static const struct kobj_type virtio_fs_ktype = {
	.release = virtio_fs_ktype_release,
	.sysfs_ops = &kobj_sysfs_ops,
	.default_groups = virtio_fs_groups,
};

/* Make sure virtiofs_mutex is held */
static void virtio_fs_put(struct virtio_fs *fs)
{
	kref_put(&fs->refcount, release_virtio_fs_obj);
	kobject_put(&fs->kobj);
}

static void virtio_fs_fiq_release(struct fuse_iqueue *fiq)
@@ -243,25 +273,46 @@ static void virtio_fs_start_all_queues(struct virtio_fs *fs)
}

/* Add a new instance to the list or return -EEXIST if tag name exists*/
static int virtio_fs_add_instance(struct virtio_fs *fs)
static int virtio_fs_add_instance(struct virtio_device *vdev,
				  struct virtio_fs *fs)
{
	struct virtio_fs *fs2;
	bool duplicate = false;
	int ret;

	mutex_lock(&virtio_fs_mutex);

	list_for_each_entry(fs2, &virtio_fs_instances, list) {
		if (strcmp(fs->tag, fs2->tag) == 0)
			duplicate = true;
		if (strcmp(fs->tag, fs2->tag) == 0) {
			mutex_unlock(&virtio_fs_mutex);
			return -EEXIST;
		}
	}

	/* Use the virtio_device's index as a unique identifier, there is no
	 * need to allocate our own identifiers because the virtio_fs instance
	 * is only visible to userspace as long as the underlying virtio_device
	 * exists.
	 */
	fs->kobj.kset = virtio_fs_kset;
	ret = kobject_add(&fs->kobj, NULL, "%d", vdev->index);
	if (ret < 0) {
		mutex_unlock(&virtio_fs_mutex);
		return ret;
	}

	ret = sysfs_create_link(&fs->kobj, &vdev->dev.kobj, "device");
	if (ret < 0) {
		kobject_del(&fs->kobj);
		mutex_unlock(&virtio_fs_mutex);
		return ret;
	}

	if (!duplicate)
	list_add_tail(&fs->list, &virtio_fs_instances);

	mutex_unlock(&virtio_fs_mutex);

	if (duplicate)
		return -EEXIST;
	kobject_uevent(&fs->kobj, KOBJ_ADD);

	return 0;
}

@@ -274,7 +325,7 @@ static struct virtio_fs *virtio_fs_find_instance(const char *tag)

	list_for_each_entry(fs, &virtio_fs_instances, list) {
		if (strcmp(fs->tag, tag) == 0) {
			kref_get(&fs->refcount);
			kobject_get(&fs->kobj);
			goto found;
		}
	}
@@ -659,6 +710,44 @@ static void virtio_fs_requests_done_work(struct work_struct *work)
	}
}

static void virtio_fs_map_queues(struct virtio_device *vdev, struct virtio_fs *fs)
{
	const struct cpumask *mask, *masks;
	unsigned int q, cpu;

	/* First attempt to map using existing transport layer affinities
	 * e.g. PCIe MSI-X
	 */
	if (!vdev->config->get_vq_affinity)
		goto fallback;

	for (q = 0; q < fs->num_request_queues; q++) {
		mask = vdev->config->get_vq_affinity(vdev, VQ_REQUEST + q);
		if (!mask)
			goto fallback;

		for_each_cpu(cpu, mask)
			fs->mq_map[cpu] = q;
	}

	return;
fallback:
	/* Attempt to map evenly in groups over the CPUs */
	masks = group_cpus_evenly(fs->num_request_queues);
	/* If even this fails we default to all CPUs use queue zero */
	if (!masks) {
		for_each_possible_cpu(cpu)
			fs->mq_map[cpu] = 0;
		return;
	}

	for (q = 0; q < fs->num_request_queues; q++) {
		for_each_cpu(cpu, &masks[q])
			fs->mq_map[cpu] = q;
	}
	kfree(masks);
}

/* Virtqueue interrupt handler */
static void virtio_fs_vq_done(struct virtqueue *vq)
{
@@ -695,6 +784,11 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
{
	struct virtqueue **vqs;
	vq_callback_t **callbacks;
	/* Specify pre_vectors to ensure that the queues before the
	 * request queues (e.g. hiprio) don't claim any of the CPUs in
	 * the multi-queue mapping and interrupt affinities
	 */
	struct irq_affinity desc = { .pre_vectors = VQ_REQUEST };
	const char **names;
	unsigned int i;
	int ret = 0;
@@ -704,6 +798,9 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
	if (fs->num_request_queues == 0)
		return -EINVAL;

	/* Truncate nr of request queues to nr_cpu_id */
	fs->num_request_queues = min_t(unsigned int, fs->num_request_queues,
					nr_cpu_ids);
	fs->nvqs = VQ_REQUEST + fs->num_request_queues;
	fs->vqs = kcalloc(fs->nvqs, sizeof(fs->vqs[VQ_HIPRIO]), GFP_KERNEL);
	if (!fs->vqs)
@@ -713,7 +810,9 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
	callbacks = kmalloc_array(fs->nvqs, sizeof(callbacks[VQ_HIPRIO]),
					GFP_KERNEL);
	names = kmalloc_array(fs->nvqs, sizeof(names[VQ_HIPRIO]), GFP_KERNEL);
	if (!vqs || !callbacks || !names) {
	fs->mq_map = kcalloc_node(nr_cpu_ids, sizeof(*fs->mq_map), GFP_KERNEL,
					dev_to_node(&vdev->dev));
	if (!vqs || !callbacks || !names || !fs->mq_map) {
		ret = -ENOMEM;
		goto out;
	}
@@ -733,7 +832,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
		names[i] = fs->vqs[i].name;
	}

	ret = virtio_find_vqs(vdev, fs->nvqs, vqs, callbacks, names, NULL);
	ret = virtio_find_vqs(vdev, fs->nvqs, vqs, callbacks, names, &desc);
	if (ret < 0)
		goto out;

@@ -745,8 +844,10 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
	kfree(names);
	kfree(callbacks);
	kfree(vqs);
	if (ret)
	if (ret) {
		kfree(fs->vqs);
		kfree(fs->mq_map);
	}
	return ret;
}

@@ -875,7 +976,7 @@ static int virtio_fs_probe(struct virtio_device *vdev)
	fs = kzalloc(sizeof(*fs), GFP_KERNEL);
	if (!fs)
		return -ENOMEM;
	kref_init(&fs->refcount);
	kobject_init(&fs->kobj, &virtio_fs_ktype);
	vdev->priv = fs;

	ret = virtio_fs_read_tag(vdev, fs);
@@ -886,7 +987,7 @@ static int virtio_fs_probe(struct virtio_device *vdev)
	if (ret < 0)
		goto out;

	/* TODO vq affinity */
	virtio_fs_map_queues(vdev, fs);

	ret = virtio_fs_setup_dax(vdev, fs);
	if (ret < 0)
@@ -897,7 +998,7 @@ static int virtio_fs_probe(struct virtio_device *vdev)
	 */
	virtio_device_ready(vdev);

	ret = virtio_fs_add_instance(fs);
	ret = virtio_fs_add_instance(vdev, fs);
	if (ret < 0)
		goto out_vqs;

@@ -906,11 +1007,10 @@ static int virtio_fs_probe(struct virtio_device *vdev)
out_vqs:
	virtio_reset_device(vdev);
	virtio_fs_cleanup_vqs(vdev);
	kfree(fs->vqs);

out:
	vdev->priv = NULL;
	kfree(fs);
	kobject_put(&fs->kobj);
	return ret;
}

@@ -934,6 +1034,8 @@ static void virtio_fs_remove(struct virtio_device *vdev)
	mutex_lock(&virtio_fs_mutex);
	/* This device is going away. No one should get new reference */
	list_del_init(&fs->list);
	sysfs_remove_link(&fs->kobj, "device");
	kobject_del(&fs->kobj);
	virtio_fs_stop_all_queues(fs);
	virtio_fs_drain_all_queues_locked(fs);
	virtio_reset_device(vdev);
@@ -1234,7 +1336,7 @@ static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
static void virtio_fs_wake_pending_and_unlock(struct fuse_iqueue *fiq)
__releases(fiq->lock)
{
	unsigned int queue_id = VQ_REQUEST; /* TODO multiqueue */
	unsigned int queue_id;
	struct virtio_fs *fs;
	struct fuse_req *req;
	struct virtio_fs_vq *fsvq;
@@ -1248,11 +1350,13 @@ __releases(fiq->lock)
	spin_unlock(&fiq->lock);

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

	pr_debug("%s: opcode %u unique %#llx nodeid %#llx in.len %u out.len %u\n",
	pr_debug("%s: opcode %u unique %#llx nodeid %#llx in.len %u out.len %u queue_id %u\n",
		 __func__, req->in.h.opcode, req->in.h.unique,
		 req->in.h.nodeid, req->in.h.len,
		 fuse_len_args(req->args->out_numargs, req->args->out_args));
		 fuse_len_args(req->args->out_numargs, req->args->out_args),
		 queue_id);

	fsvq = &fs->vqs[queue_id];
	ret = virtio_fs_enqueue_req(fsvq, req, false);
@@ -1520,21 +1624,56 @@ static struct file_system_type virtio_fs_type = {
	.kill_sb	= virtio_kill_sb,
};

static int virtio_fs_uevent(const struct kobject *kobj, struct kobj_uevent_env *env)
{
	const struct virtio_fs *fs = container_of(kobj, struct virtio_fs, kobj);

	add_uevent_var(env, "TAG=%s", fs->tag);
	return 0;
}

static const struct kset_uevent_ops virtio_fs_uevent_ops = {
	.uevent = virtio_fs_uevent,
};

static int __init virtio_fs_sysfs_init(void)
{
	virtio_fs_kset = kset_create_and_add("virtiofs", &virtio_fs_uevent_ops,
					     fs_kobj);
	if (!virtio_fs_kset)
		return -ENOMEM;
	return 0;
}

static void __exit virtio_fs_sysfs_exit(void)
{
	kset_unregister(virtio_fs_kset);
	virtio_fs_kset = NULL;
}

static int __init virtio_fs_init(void)
{
	int ret;

	ret = register_virtio_driver(&virtio_fs_driver);
	ret = virtio_fs_sysfs_init();
	if (ret < 0)
		return ret;

	ret = register_virtio_driver(&virtio_fs_driver);
	if (ret < 0)
		goto sysfs_exit;

	ret = register_filesystem(&virtio_fs_type);
	if (ret < 0) {
		unregister_virtio_driver(&virtio_fs_driver);
		return ret;
	}
	if (ret < 0)
		goto unregister_virtio_driver;

	return 0;

unregister_virtio_driver:
	unregister_virtio_driver(&virtio_fs_driver);
sysfs_exit:
	virtio_fs_sysfs_exit();
	return ret;
}
module_init(virtio_fs_init);

@@ -1542,6 +1681,7 @@ static void __exit virtio_fs_exit(void)
{
	unregister_filesystem(&virtio_fs_type);
	unregister_virtio_driver(&virtio_fs_driver);
	virtio_fs_sysfs_exit();
}
module_exit(virtio_fs_exit);