aboutsummaryrefslogtreecommitdiffstats
path: root/virtio_loopback_device.c
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2024-10-14 15:44:29 +0300
committerTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2024-12-03 11:56:03 +0200
commit061ee070b409025af7e775c5ba9199673efed0bf (patch)
treed2aaa729ff690d03c2e932fa199a9ecbd4d65d83 /virtio_loopback_device.c
parent8948c9808eded80772de98cd4e8dd0cc71fdbe17 (diff)
Update virtio-loopback driver - notification mechanism
Updates: - README file - Notification mechanism: - Notifications triggered by virtio devices by-pass adapter application and are delivered directly to the vhost-user devices - Fix coding style Change-Id: I4b03f1c19dd527ad08bee80b5d515dcbd3b1c485 Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Diffstat (limited to 'virtio_loopback_device.c')
-rw-r--r--virtio_loopback_device.c313
1 files changed, 192 insertions, 121 deletions
diff --git a/virtio_loopback_device.c b/virtio_loopback_device.c
index e0b19a6..0c1c326 100644
--- a/virtio_loopback_device.c
+++ b/virtio_loopback_device.c
@@ -1,10 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
+ * Virtio loopback transport driver
+ *
* Based on virtio_mmio.c
* Copyright 2011-2014, ARM Ltd.
*
* Copyright 2022-2024 Virtual Open Systems SAS
*
+ * Authors:
+ * Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
+ * Anna Panagopoulou <anna@virtualopensystems.com>
+ * Alvise Rigo <a.rigo@virtualopensystems.com>
+ *
+ * This module allows virtio devices to be used in a non-virtualized
+ * environment, coupled with vhost-user device (user-space drivers).
+ *
+ * It is set as a transport driver by the virtio-loopback device
+ * driver for a group of virtio drivers and reroutes all read/write
+ * operations to the userspace. In user-space, virtio-loopback adapter
+ * (the user-space component of the design) handles the read/write ops
+ * translates them into the corresponding vhost-user messages and
+ * forwards them to the corresponding vhost-user device.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -14,10 +31,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define pr_fmt(fmt) "virtio-loopback-transport: " fmt
@@ -33,104 +46,105 @@ static void print_neg_flag(uint64_t neg_flag, bool read)
pr_debug("Write:\n");
switch (neg_flag) {
- case VIRTIO_MMIO_MAGIC_VALUE: //0x000
+ case VIRTIO_MMIO_MAGIC_VALUE:
pr_debug("\tVIRTIO_MMIO_MAGIC_VALUE\n");
break;
- case VIRTIO_MMIO_VERSION: //0x004
+ case VIRTIO_MMIO_VERSION:
pr_debug("\tVIRTIO_MMIO_VERSION\n");
break;
- case VIRTIO_MMIO_DEVICE_ID: //0x008
+ case VIRTIO_MMIO_DEVICE_ID:
pr_debug("\tVIRTIO_MMIO_DEVICE_ID\n");
break;
- case VIRTIO_MMIO_VENDOR_ID: //0x00c
+ case VIRTIO_MMIO_VENDOR_ID:
pr_debug("\tVIRTIO_MMIO_VENDOR_ID\n");
break;
- case VIRTIO_MMIO_DEVICE_FEATURES: //0x010
+ case VIRTIO_MMIO_DEVICE_FEATURES:
pr_debug("\tVIRTIO_MMIO_DEVICE_FEATURES\n");
break;
- case VIRTIO_MMIO_DEVICE_FEATURES_SEL: //0x014
+ case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
pr_debug("\tVIRTIO_MMIO_DEVICE_FEATURES_SEL\n");
break;
- case VIRTIO_MMIO_DRIVER_FEATURES: //0x020
+ case VIRTIO_MMIO_DRIVER_FEATURES:
pr_debug("\tVIRTIO_MMIO_DRIVER_FEATURES\n");
break;
- case VIRTIO_MMIO_DRIVER_FEATURES_SEL: //0x024
+ case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
pr_debug("\tVIRTIO_MMIO_DRIVER_FEATURES_SEL\n");
break;
- case VIRTIO_MMIO_GUEST_PAGE_SIZE: //0x028
+ case VIRTIO_MMIO_GUEST_PAGE_SIZE:
pr_debug("\tVIRTIO_MMIO_GUEST_PAGE_SIZE\n");
break;
- case VIRTIO_MMIO_QUEUE_SEL: //0x030
+ case VIRTIO_MMIO_QUEUE_SEL:
pr_debug("\tVIRTIO_MMIO_QUEUE_SEL\n");
break;
- case VIRTIO_MMIO_QUEUE_NUM_MAX: //0x034
+ case VIRTIO_MMIO_QUEUE_NUM_MAX:
pr_debug("\tVIRTIO_MMIO_QUEUE_NUM_MAX\n");
break;
- case VIRTIO_MMIO_QUEUE_NUM: //0x038
+ case VIRTIO_MMIO_QUEUE_NUM:
pr_debug("\tVIRTIO_MMIO_QUEUE_NUM\n");
break;
- case VIRTIO_MMIO_QUEUE_ALIGN: //0x03c
+ case VIRTIO_MMIO_QUEUE_ALIGN:
pr_debug("\tVIRTIO_MMIO_QUEUE_ALIGN\n");
break;
- case VIRTIO_MMIO_QUEUE_PFN: //0x040
+ case VIRTIO_MMIO_QUEUE_PFN:
pr_debug("\tVIRTIO_MMIO_QUEUE_PFN\n");
break;
- case VIRTIO_MMIO_QUEUE_READY: //0x044
+ case VIRTIO_MMIO_QUEUE_READY:
pr_debug("\tVIRTIO_MMIO_QUEUE_READY\n");
break;
- case VIRTIO_MMIO_QUEUE_NOTIFY: //0x050
+ case VIRTIO_MMIO_QUEUE_NOTIFY:
pr_debug("\tVIRTIO_MMIO_QUEUE_NOTIFY\n");
break;
- case VIRTIO_MMIO_INTERRUPT_STATUS: //0x060
+ case VIRTIO_MMIO_INTERRUPT_STATUS:
pr_debug("\tVIRTIO_MMIO_INTERRUPT_STATUS\n");
break;
- case VIRTIO_MMIO_INTERRUPT_ACK: //0x064
+ case VIRTIO_MMIO_INTERRUPT_ACK:
pr_debug("\tVIRTIO_MMIO_INTERRUPT_ACK\n");
break;
- case VIRTIO_MMIO_STATUS: //0x070
+ case VIRTIO_MMIO_STATUS:
pr_debug("\tVIRTIO_MMIO_STATUS\n");
break;
- case VIRTIO_MMIO_QUEUE_DESC_LOW: //0x080
+ case VIRTIO_MMIO_QUEUE_DESC_LOW:
pr_debug("\tVIRTIO_MMIO_QUEUE_DESC_LOW\n");
break;
- case VIRTIO_MMIO_QUEUE_DESC_HIGH: //0x084
+ case VIRTIO_MMIO_QUEUE_DESC_HIGH:
pr_debug("\tVIRTIO_MMIO_QUEUE_DESC_HIGH\n");
break;
- case VIRTIO_MMIO_QUEUE_AVAIL_LOW: //0x090
+ case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
pr_debug("\tVIRTIO_MMIO_QUEUE_AVAIL_LOW\n");
break;
- case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: //0x094
+ case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
pr_debug("\tVIRTIO_MMIO_QUEUE_AVAIL_HIGH\n");
break;
- case VIRTIO_MMIO_QUEUE_USED_LOW: //0x0a0
+ case VIRTIO_MMIO_QUEUE_USED_LOW:
pr_debug("\tVIRTIO_MMIO_QUEUE_USED_LOW\n");
break;
- case VIRTIO_MMIO_QUEUE_USED_HIGH: //0x0a4
+ case VIRTIO_MMIO_QUEUE_USED_HIGH:
pr_debug("\tVIRTIO_MMIO_QUEUE_USED_HIGH\n");
break;
- case VIRTIO_MMIO_SHM_SEL: //0x0ac
+ case VIRTIO_MMIO_SHM_SEL:
pr_debug("\tVIRTIO_MMIO_SHM_SEL\n");
break;
- case VIRTIO_MMIO_SHM_LEN_LOW: //0x0b0
+ case VIRTIO_MMIO_SHM_LEN_LOW:
pr_debug("\tVIRTIO_MMIO_SHM_LEN_LOW\n");
break;
- case VIRTIO_MMIO_SHM_LEN_HIGH: //0x0b4
+ case VIRTIO_MMIO_SHM_LEN_HIGH:
pr_debug("\tVIRTIO_MMIO_SHM_LEN_HIGH\n");
break;
- case VIRTIO_MMIO_SHM_BASE_LOW: //0x0b8
+ case VIRTIO_MMIO_SHM_BASE_LOW:
pr_debug("\tVIRTIO_MMIO_SHM_BASE_LOW\n");
break;
- case VIRTIO_MMIO_SHM_BASE_HIGH: //0x0bc
+ case VIRTIO_MMIO_SHM_BASE_HIGH:
pr_debug("\tVIRTIO_MMIO_SHM_BASE_HIGH\n");
break;
- case VIRTIO_MMIO_CONFIG_GENERATION: //0x0fc
+ case VIRTIO_MMIO_CONFIG_GENERATION:
pr_debug("\tVIRTIO_MMIO_CONFIG_GENERATION\n");
break;
default:
if (neg_flag >= VIRTIO_MMIO_CONFIG)
pr_debug("\tVIRTIO_MMIO_CONFIG\n");
else
- pr_debug("\tNegotiation flag Unknown: %lld\n", neg_flag);
+ pr_debug("\tNegotiation flag Unknown: %lld\n",
+ neg_flag);
return;
}
}
@@ -171,13 +185,15 @@ static void print_neg_flag(uint64_t neg_flag, bool read)
*/
/* function declaration */
-static uint64_t read_adapter(uint64_t fn_id, uint64_t size, struct device_data *dev_data);
-static void write_adapter(uint64_t data, uint64_t fn_id, uint64_t size, struct device_data *dev_data);
+static uint64_t read_adapter(uint64_t fn_id, uint64_t size,
+ struct device_data *dev_data);
+static void write_adapter(uint64_t data, uint64_t fn_id, uint64_t size,
+ struct device_data *dev_data);
/* Configuration interface */
static u64 vl_get_features(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
u64 features;
@@ -195,7 +211,7 @@ static u64 vl_get_features(struct virtio_device *vdev)
static int vl_finalize_features(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
/* Give virtio_ring a chance to accept features. */
@@ -203,17 +219,19 @@ static int vl_finalize_features(struct virtio_device *vdev)
/* Make sure there are no mixed devices */
if (vl_dev->version == 2 &&
- !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) {
- dev_err(&vdev->dev, "New virtio-loopback devices (version 2) "
- "must provide VIRTIO_F_VERSION_1 feature!\n");
+ !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) {
+ dev_err(&vdev->dev,
+ "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n");
return -EINVAL;
}
write_adapter(1, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 4, data);
- write_adapter((u32)(vdev->features >> 32), VIRTIO_MMIO_DRIVER_FEATURES, 4, data);
+ write_adapter((u32)(vdev->features >> 32), VIRTIO_MMIO_DRIVER_FEATURES,
+ 4, data);
write_adapter(0, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 4, data);
- write_adapter((u32)vdev->features, VIRTIO_MMIO_DRIVER_FEATURES, 4, data);
+ write_adapter((u32)vdev->features, VIRTIO_MMIO_DRIVER_FEATURES,
+ 4, data);
return 0;
}
@@ -221,7 +239,7 @@ static int vl_finalize_features(struct virtio_device *vdev)
static void vl_get(struct virtio_device *vdev, unsigned int offset,
void *buf, unsigned int len)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
u8 b;
@@ -233,7 +251,8 @@ static void vl_get(struct virtio_device *vdev, unsigned int offset,
int i;
for (i = 0; i < len; i++)
- ptr[i] = read_adapter(VIRTIO_MMIO_CONFIG + offset + i, 1, data);
+ ptr[i] = read_adapter(VIRTIO_MMIO_CONFIG + offset + i,
+ 1, data);
return;
}
@@ -243,17 +262,22 @@ static void vl_get(struct virtio_device *vdev, unsigned int offset,
memcpy(buf, &b, sizeof(b));
break;
case 2:
- w = cpu_to_le16(read_adapter(VIRTIO_MMIO_CONFIG + offset, 2, data));
+ w = cpu_to_le16(read_adapter(VIRTIO_MMIO_CONFIG + offset,
+ 2, data));
memcpy(buf, &w, sizeof(w));
break;
case 4:
- l = cpu_to_le32(read_adapter(VIRTIO_MMIO_CONFIG + offset, 4, data));
+ l = cpu_to_le32(read_adapter(VIRTIO_MMIO_CONFIG + offset,
+ 4, data));
memcpy(buf, &l, sizeof(l));
break;
case 8:
- l = cpu_to_le32(read_adapter(VIRTIO_MMIO_CONFIG + offset, 4, data));
+ l = cpu_to_le32(read_adapter(VIRTIO_MMIO_CONFIG + offset,
+ 4, data));
memcpy(buf, &l, sizeof(l));
- l = cpu_to_le32(read_adapter(VIRTIO_MMIO_CONFIG + offset + sizeof(l), 4, data));
+ l = cpu_to_le32(read_adapter(
+ VIRTIO_MMIO_CONFIG + offset + sizeof(l),
+ 4, data));
memcpy(buf + sizeof(l), &l, sizeof(l));
break;
default:
@@ -264,7 +288,7 @@ static void vl_get(struct virtio_device *vdev, unsigned int offset,
static void vl_set(struct virtio_device *vdev, unsigned int offset,
const void *buf, unsigned int len)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
u8 b;
@@ -276,7 +300,8 @@ static void vl_set(struct virtio_device *vdev, unsigned int offset,
int i;
for (i = 0; i < len; i++)
- write_adapter(ptr[i], VIRTIO_MMIO_CONFIG + offset + i, 1, data);
+ write_adapter(ptr[i], VIRTIO_MMIO_CONFIG + offset + i,
+ 1, data);
return;
}
@@ -288,17 +313,22 @@ static void vl_set(struct virtio_device *vdev, unsigned int offset,
break;
case 2:
memcpy(&w, buf, sizeof(w));
- write_adapter(le16_to_cpu(w), VIRTIO_MMIO_CONFIG + offset, 2, data);
+ write_adapter(le16_to_cpu(w), VIRTIO_MMIO_CONFIG + offset,
+ 2, data);
break;
case 4:
memcpy(&l, buf, sizeof(l));
- write_adapter(le32_to_cpu(l), VIRTIO_MMIO_CONFIG + offset, 4, data);
+ write_adapter(le32_to_cpu(l), VIRTIO_MMIO_CONFIG + offset,
+ 4, data);
break;
case 8:
memcpy(&l, buf, sizeof(l));
- write_adapter(le32_to_cpu(l), VIRTIO_MMIO_CONFIG + offset, 4, data);
+ write_adapter(le32_to_cpu(l), VIRTIO_MMIO_CONFIG + offset,
+ 4, data);
memcpy(&l, buf + sizeof(l), sizeof(l));
- write_adapter(le32_to_cpu(l), VIRTIO_MMIO_CONFIG + offset + sizeof(l), 4, data);
+ write_adapter(le32_to_cpu(l),
+ VIRTIO_MMIO_CONFIG + offset + sizeof(l),
+ 4, data);
break;
default:
BUG();
@@ -307,7 +337,7 @@ static void vl_set(struct virtio_device *vdev, unsigned int offset,
static u32 vl_generation(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
if (vl_dev->version == 1)
@@ -318,7 +348,7 @@ static u32 vl_generation(struct virtio_device *vdev)
static u8 vl_get_status(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
return read_adapter(VIRTIO_MMIO_STATUS, 4, data) & 0xff;
@@ -326,7 +356,7 @@ static u8 vl_get_status(struct virtio_device *vdev)
static void vl_set_status(struct virtio_device *vdev, u8 status)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
write_adapter(status, VIRTIO_MMIO_STATUS, 4, data);
@@ -334,7 +364,7 @@ static void vl_set_status(struct virtio_device *vdev, u8 status)
static void vl_reset(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
/* 0 status means a reset. */
@@ -344,7 +374,8 @@ static void vl_reset(struct virtio_device *vdev)
/* Notify work handling function */
static void notify_work_handler(struct work_struct *work)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(work, notify_work);
+ struct virtio_loopback_device *vl_dev =
+ container_of(work, struct virtio_loopback_device, notify_work);
struct device_data *dev_data = vl_dev->data;
struct notify_data *entry, *tmp;
uint32_t index;
@@ -365,30 +396,46 @@ static void notify_work_handler(struct work_struct *work)
/* The notify function used when creating a virtqueue */
static bool vl_notify(struct virtqueue *vq)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vq->vdev, vdev);
+ struct virtio_loopback_device *vl_dev =
+ to_virtio_loopback_device(vq->vdev);
+ struct eventfd_ctx **vq_notifiers = vl_dev->data->vq_data.vq_notifiers;
+ bool vq_notifiers_enabled = vl_dev->data->vq_data.vq_notifiers_enabled;
struct notify_data *data;
int ret = 1;
- /* Create the new node */
- data = kmalloc(sizeof(struct notify_data), GFP_ATOMIC);
- if (!data)
- return false;
-
- data->index = vq->index;
- INIT_LIST_HEAD(&data->list);
+ if (vq_notifiers_enabled && (vq_notifiers[vq->index])) {
+ /* Notify directly vhost-user-device bypassing the adapter */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(6, 7, 12)
+ eventfd_signal(vq_notifiers[vq->index]);
+#else
+ eventfd_signal(vq_notifiers[vq->index], 1);
+#endif
+ } else {
+ /* Create the new node */
+ data = kmalloc(sizeof(struct notify_data), GFP_ATOMIC);
+ if (!data)
+ return false;
- /* Add in the notify_list, which should be protected! */
- spin_lock(&vl_dev->notify_q_lock);
- list_add_tail(&data->list, &vl_dev->notify_list);
- spin_unlock(&vl_dev->notify_q_lock);
+ data->index = vq->index;
+ INIT_LIST_HEAD(&data->list);
- /* Schedule the element */
- while (ret) {
- /* Force scheduling if queue_work fails and list is not empty */
- ret = !queue_work(loopback_data.notify_workqueue, &vl_dev->notify_work);
+ /* Add in the notify_list, which should be protected! */
spin_lock(&vl_dev->notify_q_lock);
- ret &= !list_empty(&vl_dev->notify_list);
+ list_add_tail(&data->list, &vl_dev->notify_list);
spin_unlock(&vl_dev->notify_q_lock);
+
+ /* Schedule the element */
+ while (ret) {
+ /*
+ * Force scheduling if queue_work fails and
+ * list is not empty
+ */
+ ret = !queue_work(vl_dev->notify_workqueue,
+ &vl_dev->notify_work);
+ spin_lock(&vl_dev->notify_q_lock);
+ ret &= !list_empty(&vl_dev->notify_list);
+ spin_unlock(&vl_dev->notify_q_lock);
+ }
}
return true;
@@ -401,9 +448,12 @@ bool vl_interrupt(struct virtio_loopback_device *vl_dev, int irq)
struct virtio_loopback_vq_info *info;
unsigned long status;
- pr_debug("Received interrupt!\n");
- /* STATUS and ACK should be done without any intermediate status change */
- /* Read and acknowledge interrupts */
+ /*
+ * Read and acknowledge interrupts
+ *
+ * Those two operations should be executed without any
+ * intermediate status change.
+ */
status = read_adapter(VIRTIO_MMIO_INTERRUPT_STATUS, 4, data);
write_adapter(status, VIRTIO_MMIO_INTERRUPT_ACK, 4, data);
@@ -423,7 +473,8 @@ bool vl_interrupt(struct virtio_loopback_device *vl_dev, int irq)
static void vl_del_vq(struct virtqueue *vq)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vq->vdev, vdev);
+ struct virtio_loopback_device *vl_dev =
+ to_virtio_loopback_device(vq->vdev);
struct device_data *data = vl_dev->data;
struct virtio_loopback_vq_info *info = vq->priv;
@@ -456,11 +507,12 @@ static void vl_del_vqs(struct virtio_device *vdev)
vl_del_vq(vq);
}
-static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int index,
- void (*callback)(struct virtqueue *vq),
- const char *name, bool ctx)
+static struct virtqueue *vl_setup_vq(struct virtio_device *vdev,
+ unsigned int index,
+ void (*callback)(struct virtqueue *vq),
+ const char *name, bool ctx)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
struct virtio_loopback_vq_info *info;
struct virtqueue *vq;
@@ -476,7 +528,8 @@ static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int in
/* Queue shouldn't already be set up. */
if (read_adapter((vl_dev->version == 1 ?
- VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY), 4, data)) {
+ VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY),
+ 4, data)) {
err = -ENOENT;
goto error_available;
}
@@ -496,7 +549,7 @@ static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int in
/* Create the vring */
vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, vdev,
- true, true, ctx, vl_notify, callback, name);
+ true, true, ctx, vl_notify, callback, name);
if (!vq) {
err = -ENOMEM;
goto error_new_virtqueue;
@@ -507,7 +560,8 @@ static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int in
#endif
/* Activate the queue */
- write_adapter(virtqueue_get_vring_size(vq), VIRTIO_MMIO_QUEUE_NUM, 4, data);
+ write_adapter(virtqueue_get_vring_size(vq), VIRTIO_MMIO_QUEUE_NUM, 4,
+ data);
if (vl_dev->version == 1) {
u64 q_pfn = virtqueue_get_desc_addr(vq);
@@ -518,14 +572,13 @@ static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int in
data->vq_data.vq_pfns[data->vq_data.vq_index++] = q_pfn;
/*
- * virtio-loopback v1 uses a 32bit QUEUE PFN. If we have something
- * that doesn't fit in 32bit, fail the setup rather than
- * pretending to be successful.
+ * virtio-loopback v1 uses a 32bit QUEUE PFN. If we have
+ * something that doesn't fit in 32bit, fail the setup rather
+ * than pretending to be successful.
*/
if (q_pfn >> 32) {
dev_err(&vdev->dev,
- "platform bug: legacy virtio-loopback must not "
- "be used with RAM above 0x%llxGB\n",
+ "platform bug: legacy virtio-loopback must not be used with RAM above 0x%llxGB\n",
0x1ULL << (32 + PAGE_SHIFT - 30));
err = -E2BIG;
goto error_bad_pfn;
@@ -538,15 +591,18 @@ static struct virtqueue *vl_setup_vq(struct virtio_device *vdev, unsigned int in
addr = virtqueue_get_desc_addr(vq);
write_adapter((u32)addr, VIRTIO_MMIO_QUEUE_DESC_LOW, 4, data);
- write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_DESC_HIGH, 4, data);
+ write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_DESC_HIGH,
+ 4, data);
addr = virtqueue_get_avail_addr(vq);
write_adapter((u32)addr, VIRTIO_MMIO_QUEUE_AVAIL_LOW, 4, data);
- write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_AVAIL_HIGH, 4, data);
+ write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_AVAIL_HIGH,
+ 4, data);
addr = virtqueue_get_used_addr(vq);
write_adapter((u32)addr, VIRTIO_MMIO_QUEUE_USED_LOW, 4, data);
- write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_USED_HIGH, 4, data);
+ write_adapter((u32)(addr >> 32), VIRTIO_MMIO_QUEUE_USED_HIGH,
+ 4, data);
write_adapter(1, VIRTIO_MMIO_QUEUE_READY, 4, data);
}
@@ -618,7 +674,7 @@ static int vl_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
}
vqs[i] = vl_setup_vq(vdev, queue_idx++, vqi->callback,
- vqi->name, vqi->ctx);
+ vqi->name, vqi->ctx);
if (IS_ERR(vqs[i])) {
vl_del_vqs(vdev);
return PTR_ERR(vqs[i]);
@@ -631,15 +687,15 @@ static int vl_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
static const char *vl_bus_name(struct virtio_device *vdev)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
return vl_dev->pdev->name;
}
static bool vl_get_shm_region(struct virtio_device *vdev,
- struct virtio_shm_region *region, u8 id)
+ struct virtio_shm_region *region, u8 id)
{
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct device_data *data = vl_dev->data;
u64 len, addr;
@@ -668,24 +724,25 @@ static bool vl_get_shm_region(struct virtio_device *vdev,
}
static const struct virtio_config_ops virtio_loopback_config_ops = {
- .get = vl_get,
- .set = vl_set,
- .generation = vl_generation,
- .get_status = vl_get_status,
- .set_status = vl_set_status,
- .reset = vl_reset,
- .find_vqs = vl_find_vqs,
- .del_vqs = vl_del_vqs,
+ .get = vl_get,
+ .set = vl_set,
+ .generation = vl_generation,
+ .get_status = vl_get_status,
+ .set_status = vl_set_status,
+ .reset = vl_reset,
+ .find_vqs = vl_find_vqs,
+ .del_vqs = vl_del_vqs,
.get_features = vl_get_features,
.finalize_features = vl_finalize_features,
- .bus_name = vl_bus_name,
+ .bus_name = vl_bus_name,
.get_shm_region = vl_get_shm_region,
};
static void virtio_loopback_release_dev(struct device *_d)
{
- struct virtio_device *vdev = container_of(_d, struct virtio_device, dev);
- struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev, vdev);
+ struct virtio_device *vdev =
+ container_of(_d, struct virtio_device, dev);
+ struct virtio_loopback_device *vl_dev = to_virtio_loopback_device(vdev);
struct platform_device *pdev = vl_dev->pdev;
devm_kfree(&pdev->dev, vl_dev);
@@ -745,8 +802,8 @@ int loopback_register_virtio_dev(struct virtio_loopback_device *vl_dev)
if (rc)
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (rc)
- dev_warn(&pdev->dev, "Failed to enable 64-bit or 32-bit DMA."
- "Trying to continue, but this might not work.\n");
+ dev_warn(&pdev->dev,
+ "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n");
/* Register the virtio device in the system */
rc = register_virtio_device(&vl_dev->vdev);
@@ -775,6 +832,8 @@ static int virtio_loopback_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&vl_dev->virtqueues);
spin_lock_init(&vl_dev->lock);
/* Initialize the workqueue */
+ vl_dev->notify_workqueue =
+ create_singlethread_workqueue("notify_workqueue");
INIT_WORK(&vl_dev->notify_work, notify_work_handler);
INIT_LIST_HEAD(&vl_dev->notify_list);
spin_lock_init(&vl_dev->notify_q_lock);
@@ -796,6 +855,10 @@ int virtio_loopback_remove(struct platform_device *pdev)
{
struct virtio_loopback_device *vl_dev = platform_get_drvdata(pdev);
+ /* Destroy the notify workqueue */
+ flush_workqueue(vl_dev->notify_workqueue);
+ destroy_workqueue(vl_dev->notify_workqueue);
+
if (vl_dev->data) {
unregister_virtio_device(&vl_dev->vdev);
pr_info("unregister_virtio_device!\n");
@@ -817,7 +880,8 @@ struct platform_driver virtio_loopback_driver = {
},
};
-static uint64_t read_adapter(uint64_t fn_id, uint64_t size, struct device_data *dev_data)
+static uint64_t read_adapter(uint64_t fn_id, uint64_t size,
+ struct device_data *dev_data)
{
uint64_t result;
@@ -852,8 +916,11 @@ static uint64_t read_adapter(uint64_t fn_id, uint64_t size, struct device_data *
* "wake_up" kick, check the updated "done" value and return.
*/
- while (dev_data->valid_eventfd && atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) != 1)
- wait_event_timeout(dev_data->wq, atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) == 1, 1 * HZ);
+ while (dev_data->valid_eventfd &&
+ atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) != 1)
+ wait_event_timeout(dev_data->wq,
+ atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) == 1,
+ 1 * HZ);
result = ((struct virtio_neg *)(dev_data->info->data))->data;
@@ -862,7 +929,8 @@ static uint64_t read_adapter(uint64_t fn_id, uint64_t size, struct device_data *
return result;
}
-static void write_adapter(uint64_t data, uint64_t fn_id, uint64_t size, struct device_data *dev_data)
+static void write_adapter(uint64_t data, uint64_t fn_id, uint64_t size,
+ struct device_data *dev_data)
{
mutex_lock(&(dev_data)->read_write_lock);
@@ -895,8 +963,11 @@ static void write_adapter(uint64_t data, uint64_t fn_id, uint64_t size, struct d
* way, virtio-loopback driver will wake up even if has missed the
* "wake_up" kick, check the updated "done" value and return.
*/
- while (dev_data->valid_eventfd && atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) != 1)
- wait_event_timeout(dev_data->wq, atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) == 1, 1 * HZ);
+ while (dev_data->valid_eventfd &&
+ atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) != 1)
+ wait_event_timeout(dev_data->wq,
+ atomic_read(&((struct virtio_neg *)(dev_data->info->data))->done) == 1,
+ 1 * HZ);
mutex_unlock(&(dev_data)->read_write_lock);
}