[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <622460b11118be66d5ec380b3d5771be77fc1e91.1753865268.git.viresh.kumar@linaro.org>
Date: Wed, 30 Jul 2025 14:59:35 +0530
From: Viresh Kumar <viresh.kumar@...aro.org>
To: linux-kernel@...r.kernel.org,
"Michael S. Tsirkin" <mst@...hat.com>,
Jason Wang <jasowang@...hat.com>,
Xuan Zhuo <xuanzhuo@...ux.alibaba.com>,
Eugenio Pérez <eperezma@...hat.com>,
Viresh Kumar <viresh.kumar@...aro.org>
Cc: Arnd Bergmann <arnd@...nel.org>,
Vincent Guittot <vincent.guittot@...aro.org>,
Alex Bennée <alex.bennee@...aro.org>,
Bill Mills <bill.mills@...aro.org>,
Rob Herring <robh@...nel.org>,
Saravana Kannan <saravanak@...gle.com>,
devicetree@...r.kernel.org,
virtualization@...ts.linux.dev,
Sudeep Holla <sudeep.holla@....com>,
Bertrand Marquis <bertrand.marquis@....com>,
"Edgar E . Iglesias" <edgar.iglesias@....com>,
Arnaud Pouliquen <arnaud.pouliquen@...s.st.com>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>
Subject: [RFC PATCH 6/6] virtio-msg: Add support for loopback bus
Add a loopback bus implementation for the virtio-msg transport.
This bus simulates a backend that echoes messages to itself, allowing
testing and development of virtio-msg without requiring an actual remote
backend or transport hardware.
The loopback bus requires a reserved memory region for its operation.
All DMA-coherent and streaming DMA allocations are restricted to this
region, enabling safe mapping into user space and helping validate the
memory-sharing model.
The reserved-memory region must be named "vmsglb" in the device tree.
Example:
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
vmsglb@...000000 {
compatible = "restricted-dma-pool";
reg = <0x00000001 0x00000000 0x0 0x00400000>; /* 4 MiB */
};
};
This bus is primarily intended for functional testing, development, and
validation of the virtio-msg transport and its userspace interface.
Signed-off-by: Viresh Kumar <viresh.kumar@...aro.org>
---
drivers/virtio/Kconfig | 9 +
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio_msg_loopback.c | 323 +++++++++++++++++++++++++++
include/uapi/linux/virtio_msg_lb.h | 22 ++
4 files changed, 355 insertions(+)
create mode 100644 drivers/virtio/virtio_msg_loopback.c
create mode 100644 include/uapi/linux/virtio_msg_lb.h
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 683152477e3f..934e8ccb3a01 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -196,6 +196,15 @@ config VIRTIO_MSG_FFA
If unsure, say N.
+config VIRTIO_MSG_LOOPBACK
+ tristate "Loopback bus driver for virtio message transport"
+ select VIRTIO_MSG
+ select VIRTIO_MSG_USER
+ help
+ This implements the Loopback bus for Virtio msg transport.
+
+ If unsure, say N.
+
config VIRTIO_DMA_SHARED_BUFFER
tristate
depends on DMA_SHARED_BUFFER
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 96ec0a9c4a7a..90a2f1d86937 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
virtio_msg_transport-y := virtio_msg.o
virtio_msg_transport-$(CONFIG_VIRTIO_MSG_USER) += virtio_msg_user.o
obj-$(CONFIG_VIRTIO_MSG) += virtio_msg_transport.o
+obj-$(CONFIG_VIRTIO_MSG_LOOPBACK) += virtio_msg_loopback.o
obj-$(CONFIG_VIRTIO_MSG_FFA) += virtio_msg_ffa.o
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
diff --git a/drivers/virtio/virtio_msg_loopback.c b/drivers/virtio/virtio_msg_loopback.c
new file mode 100644
index 000000000000..d1d454fadc6f
--- /dev/null
+++ b/drivers/virtio/virtio_msg_loopback.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Loopback bus implementation for Virtio message transport.
+ *
+ * Copyright (C) 2025 Google LLC and Linaro.
+ * Viresh Kumar <viresh.kumar@...aro.org>
+ *
+ * This implements the Loopback bus for Virtio msg transport.
+ */
+
+#define pr_fmt(fmt) "virtio-msg-loopback: " fmt
+
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/virtio.h>
+#include <uapi/linux/virtio_msg.h>
+#include <uapi/linux/virtio_msg_lb.h>
+
+#include "virtio_msg_internal.h"
+
+struct vmlb_device {
+ struct virtio_msg_device vmdev;
+ struct list_head list;
+};
+
+struct virtio_msg_lb {
+ /* Serializes transfers and protects list */
+ struct mutex lock;
+ struct list_head list;
+ struct miscdevice misc;
+ struct virtio_msg_user_device vmudev;
+ struct virtio_msg *response;
+ struct reserved_mem *rmem;
+ struct device *dev;
+};
+
+static struct virtio_msg_lb *vmlb;
+
+#define to_vmlbdev(_vmdev) ((struct vmlb_device *)(_vmdev)->bus_data)
+
+static struct vmlb_device *find_vmlbdev(u16 dev_id)
+{
+ struct vmlb_device *vmlbdev;
+
+ list_for_each_entry(vmlbdev, &vmlb->list, list) {
+ if (vmlbdev->vmdev.dev_id == dev_id)
+ return vmlbdev;
+ }
+
+ return NULL;
+}
+
+static const char *virtio_msg_lb_bus_info(struct virtio_msg_device *vmdev,
+ u16 *msg_size, u32 *rev)
+{
+ *msg_size = VIRTIO_MSG_MIN_SIZE;
+ *rev = VIRTIO_MSG_REVISION_1;
+
+ return dev_name(vmlb->dev);
+}
+
+static int virtio_msg_lb_transfer(struct virtio_msg_device *vmdev,
+ struct virtio_msg *request,
+ struct virtio_msg *response)
+{
+ struct virtio_msg_user_device *vmudev = &vmlb->vmudev;
+ int ret;
+
+ /*
+ * Allow only one transaction to progress at once.
+ */
+ guard(mutex)(&vmlb->lock);
+
+ /*
+ * Set `vmsg` to `request` and finish the completion to wake up the
+ * `read()` thread.
+ */
+ vmudev->vmsg = request;
+ vmlb->response = response;
+ complete(&vmudev->r_completion);
+
+ /*
+ * Wait here for the `write()` thread to finish and not return before
+ * the operation is finished to avoid any potential races.
+ */
+ ret = wait_for_completion_interruptible_timeout(&vmudev->w_completion, 1000);
+ if (ret < 0) {
+ dev_err(vmlb->dev, "Interrupted while waiting for response: %d\n", ret);
+ } else if (!ret) {
+ dev_err(vmlb->dev, "Timed out waiting for response\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+
+ /* Clear the pointers, just to be safe */
+ vmudev->vmsg = NULL;
+ vmlb->response = NULL;
+
+ return ret;
+}
+
+static struct virtio_msg_ops virtio_msg_lb_ops = {
+ .transfer = virtio_msg_lb_transfer,
+ .bus_info = virtio_msg_lb_bus_info,
+};
+
+static int virtio_msg_lb_user_handle(struct virtio_msg_user_device *vmudev,
+ struct virtio_msg *vmsg)
+{
+ struct vmlb_device *vmlbdev;
+
+ /* Response message */
+ if (vmsg->type & VIRTIO_MSG_TYPE_RESPONSE) {
+ if (vmlb->response)
+ memcpy(vmlb->response, vmsg, VIRTIO_MSG_MIN_SIZE);
+
+ return 0;
+ }
+
+ /* Only support EVENT_USED virtio request messages */
+ if (vmsg->type & VIRTIO_MSG_TYPE_BUS || vmsg->msg_id != VIRTIO_MSG_EVENT_USED) {
+ dev_err(vmlb->dev, "Unsupported message received\n");
+ return 0;
+ }
+
+ vmlbdev = find_vmlbdev(le16_to_cpu(vmsg->dev_id));
+ if (!vmlbdev)
+ return 0;
+
+ virtio_msg_event(&vmlbdev->vmdev, vmsg);
+ return 0;
+}
+
+static struct virtio_msg_user_ops vmlb_user_ops = {
+ .handle = virtio_msg_lb_user_handle,
+};
+
+static int vmlbdev_add(struct file *file, struct vmsg_lb_dev_info *info)
+{
+ struct vmlb_device *vmlbdev;
+ int ret;
+
+ scoped_guard(mutex, &vmlb->lock) {
+ if (find_vmlbdev(info->dev_id))
+ return -EEXIST;
+
+ vmlbdev = kzalloc(sizeof(*vmlbdev), GFP_KERNEL);
+ if (!vmlbdev)
+ return -ENOMEM;
+
+ vmlbdev->vmdev.dev_id = info->dev_id;
+ vmlbdev->vmdev.ops = &virtio_msg_lb_ops;
+ vmlbdev->vmdev.vdev.dev.parent = vmlb->dev;
+ vmlbdev->vmdev.bus_data = vmlbdev;
+
+ list_add(&vmlbdev->list, &vmlb->list);
+ }
+
+ ret = virtio_msg_register(&vmlbdev->vmdev);
+ if (ret) {
+ dev_err(vmlb->dev, "Failed to register virtio msg lb device (%d)\n", ret);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ scoped_guard(mutex, &vmlb->lock)
+ list_del(&vmlbdev->list);
+
+ kfree(vmlbdev);
+ return ret;
+}
+
+static int vmlbdev_remove(struct file *file, struct vmsg_lb_dev_info *info)
+{
+ struct vmlb_device *vmlbdev;
+
+ scoped_guard(mutex, &vmlb->lock) {
+ vmlbdev = find_vmlbdev(info->dev_id);
+ if (vmlbdev) {
+ list_del(&vmlbdev->list);
+ virtio_msg_unregister(&vmlbdev->vmdev);
+ return 0;
+ }
+ }
+
+ dev_err(vmlb->dev, "Failed to find virtio msg lb device.\n");
+ return -ENODEV;
+}
+
+static void vmlbdev_remove_all(void)
+{
+ struct vmlb_device *vmlbdev, *tvmlbdev;
+
+ guard(mutex)(&vmlb->lock);
+
+ list_for_each_entry_safe(vmlbdev, tvmlbdev, &vmlb->list, list) {
+ virtio_msg_unregister(&vmlbdev->vmdev);
+ list_del(&vmlbdev->list);
+ }
+}
+
+static long vmlb_ioctl(struct file *file, unsigned int cmd, unsigned long data)
+{
+ struct vmsg_lb_dev_info info;
+
+ if (copy_from_user(&info, (void __user *)data, sizeof(info)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case IOCTL_VMSG_LB_ADD:
+ return vmlbdev_add(file, &info);
+
+ case IOCTL_VMSG_LB_REMOVE:
+ return vmlbdev_remove(file, &info);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int vmlb_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ if (offset > vmlb->rmem->size - size)
+ return -EINVAL;
+
+ return remap_pfn_range(vma, vma->vm_start,
+ (vmlb->rmem->base + offset) >> PAGE_SHIFT,
+ size,
+ vma->vm_page_prot);
+}
+
+static loff_t vmlb_llseek(struct file *file, loff_t offset, int whence)
+{
+ return fixed_size_llseek(file, offset, whence, vmlb->rmem->size);
+}
+
+static const struct file_operations vmlb_miscdev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = vmlb_ioctl,
+ .mmap = vmlb_mmap,
+ .llseek = vmlb_llseek,
+};
+
+static int virtio_msg_lb_init(void)
+{
+ int ret;
+
+ vmlb = kzalloc(sizeof(*vmlb), GFP_KERNEL);
+ if (!vmlb)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&vmlb->list);
+ mutex_init(&vmlb->lock);
+ vmlb->vmudev.ops = &vmlb_user_ops;
+
+ vmlb->misc.name = "virtio-msg-lb";
+ vmlb->misc.minor = MISC_DYNAMIC_MINOR;
+ vmlb->misc.fops = &vmlb_miscdev_fops;
+
+ ret = misc_register(&vmlb->misc);
+ if (ret)
+ goto vmlb_free;
+
+ vmlb->dev = vmlb->misc.this_device;
+ vmlb->vmudev.parent = vmlb->dev;
+
+ vmlb->rmem = of_reserved_mem_lookup_by_name("vmsglb");
+ if (IS_ERR(vmlb->rmem)) {
+ ret = PTR_ERR(vmlb->rmem);
+ goto unregister;
+ }
+ ret = reserved_mem_device_init(vmlb->dev, vmlb->rmem);
+ if (ret)
+ goto mem_release;
+
+ /* Register with virtio-msg UAPI */
+ ret = virtio_msg_user_register(&vmlb->vmudev);
+ if (ret) {
+ dev_err(vmlb->dev, "Could not register virtio-msg user API\n");
+ goto mem_release;
+ }
+
+ ret = dma_coerce_mask_and_coherent(vmlb->dev, DMA_BIT_MASK(64));
+ if (ret)
+ dev_warn(vmlb->dev, "Failed to enable 64-bit or 32-bit DMA\n");
+
+ return 0;
+
+mem_release:
+ of_reserved_mem_device_release(vmlb->dev);
+unregister:
+ misc_deregister(&vmlb->misc);
+vmlb_free:
+ kfree(vmlb);
+ return ret;
+}
+module_init(virtio_msg_lb_init);
+
+static void virtio_msg_lb_exit(void)
+{
+ virtio_msg_user_unregister(&vmlb->vmudev);
+ of_reserved_mem_device_release(vmlb->dev);
+ vmlbdev_remove_all();
+ misc_deregister(&vmlb->misc);
+ kfree(vmlb);
+}
+module_exit(virtio_msg_lb_exit);
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@...aro.org>");
+MODULE_DESCRIPTION("Virtio message loopback bus driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/virtio_msg_lb.h b/include/uapi/linux/virtio_msg_lb.h
new file mode 100644
index 000000000000..fe0ce6269a0a
--- /dev/null
+++ b/include/uapi/linux/virtio_msg_lb.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * Virtio message Loopback bus header.
+ *
+ * Copyright (C) 2025 Google LLC and Linaro.
+ * Viresh Kumar <viresh.kumar@...aro.org>
+ */
+
+#ifndef _LINUX_VIRTIO_MSG_LB_H
+#define _LINUX_VIRTIO_MSG_LB_H
+
+struct vmsg_lb_dev_info {
+ unsigned int dev_id;
+};
+
+#define IOCTL_VMSG_LB_ADD \
+ _IOC(_IOC_NONE, 'P', 0, sizeof(struct vmsg_lb_dev_info))
+
+#define IOCTL_VMSG_LB_REMOVE \
+ _IOC(_IOC_NONE, 'P', 1, sizeof(struct vmsg_lb_dev_info))
+
+#endif /* _LINUX_VIRTIO_MSG_LB_H */
--
2.31.1.272.g89b43f80a514
Powered by blists - more mailing lists