summaryrefslogtreecommitdiff
path: root/drivers/virtio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 11:49:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-17 11:49:00 -0700
commitd44ade05aa21468bd30652bc4492891b854a400a (patch)
treee146ae0c13621fc867f739bc84f98f8ce0875915 /drivers/virtio
parent3dc0df03396a3329c644b29b421892a32ecb9387 (diff)
parent8cb2c9285e4ce9154f45fb15633ebd45dfd8d9cf (diff)
Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
Pull virtio updates from Michael Tsirkin: - new virtio CAN driver - support for LoongArch architecture in fw_cfg - support for firmware notifications in vdpa/octeon_ep - support for VFs in virtio core - fixes, cleanups all over the place, notably: - vhost: fix vhost_get_avail_idx for a non empty ring fixing an significant old perf regression - READ_ONCE() annotations mean virtio ring is now free of KCSAN warnings * tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: (37 commits) can: virtio: Fix comment in UAPI header can: virtio: Add virtio CAN driver virtio: add num_vf callback to virtio_bus fw_cfg: Add support for LoongArch architecture vdpa/octeon_ep: fix IRQ-to-ring mapping in interrupt handler vdpa/octeon_ep: Add vDPA device event handling for firmware notifications vdpa/octeon_ep: Use 4 bytes for mailbox signature vdpa/octeon_ep: Fix PF->VF mailbox data address calculation vhost_task_create: kill unnecessary .exit_signal initialization vhost: remove unnecessary module_init/exit functions vdpa/mlx5: Use kvzalloc_flex() for MTT command memory vdpa_sim_net: switch to dynamic root device vdpa_sim_blk: switch to dynamic root device virtio-mem: Destroy mutex before freeing virtio_mem virtio-balloon: Destroy mutex before freeing virtio_balloon tools/virtio: fix build for kmalloc_obj API and missing stubs virtio_ring: Add READ_ONCE annotations for device-writable fields vduse: fix compat handling for VDUSE_IOTLB_GET_FD/VDUSE_VQ_GET_INFO tools/virtio: check mmap return value in vringh_test vhost/net: complete zerocopy ubufs only once ...
Diffstat (limited to 'drivers/virtio')
-rw-r--r--drivers/virtio/virtio.c9
-rw-r--r--drivers/virtio/virtio_balloon.c2
-rw-r--r--drivers/virtio/virtio_mem.c2
-rw-r--r--drivers/virtio/virtio_mmio.c26
-rw-r--r--drivers/virtio/virtio_pci_common.c10
-rw-r--r--drivers/virtio/virtio_ring.c77
-rw-r--r--drivers/virtio/virtio_rtc_driver.c28
7 files changed, 112 insertions, 42 deletions
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 5bdc6b82b30b..299fa83be1d5 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -435,6 +435,14 @@ static void virtio_dev_shutdown(struct device *_d)
dev->config->reset(dev);
}
+static int virtio_dev_num_vf(struct device *dev)
+{
+ struct virtio_device *vdev = dev_to_virtio(dev);
+
+ return dev_num_vf(vdev->dev.parent);
+}
+
+
static const struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_dev_match,
@@ -444,6 +452,7 @@ static const struct bus_type virtio_bus = {
.remove = virtio_dev_remove,
.irq_get_affinity = virtio_irq_get_affinity,
.shutdown = virtio_dev_shutdown,
+ .num_vf = virtio_dev_num_vf,
};
int __register_virtio_driver(struct virtio_driver *driver, struct module *owner)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f6c2dff33f8a..088b3a0e6ce6 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1075,6 +1075,7 @@ out_del_balloon_wq:
out_del_vqs:
vdev->config->del_vqs(vdev);
out_free_vb:
+ mutex_destroy(&vb->balloon_lock);
kfree(vb);
out:
return err;
@@ -1119,6 +1120,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
}
remove_common(vb);
+ mutex_destroy(&vb->balloon_lock);
kfree(vb);
}
diff --git a/drivers/virtio/virtio_mem.c b/drivers/virtio/virtio_mem.c
index 48051e9e98ab..11c441501582 100644
--- a/drivers/virtio/virtio_mem.c
+++ b/drivers/virtio/virtio_mem.c
@@ -2975,6 +2975,7 @@ static int virtio_mem_probe(struct virtio_device *vdev)
out_del_vq:
vdev->config->del_vqs(vdev);
out_free_vm:
+ mutex_destroy(&vm->hotplug_mutex);
kfree(vm);
vdev->priv = NULL;
@@ -3067,6 +3068,7 @@ static void virtio_mem_remove(struct virtio_device *vdev)
virtio_reset_device(vdev);
vdev->config->del_vqs(vdev);
+ mutex_destroy(&vm->hotplug_mutex);
kfree(vm);
vdev->priv = NULL;
}
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 595c2274fbb5..510b7c4efdff 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -662,9 +662,7 @@ static void virtio_mmio_remove(struct platform_device *pdev)
#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES)
-static struct device vm_cmdline_parent = {
- .init_name = "virtio-mmio-cmdline",
-};
+static struct device *vm_cmdline_parent;
static int vm_cmdline_parent_registered;
static int vm_cmdline_id;
@@ -672,7 +670,6 @@ static int vm_cmdline_id;
static int vm_cmdline_set(const char *device,
const struct kernel_param *kp)
{
- int err;
struct resource resources[2] = {};
char *str;
long long base, size;
@@ -704,11 +701,10 @@ static int vm_cmdline_set(const char *device,
resources[1].start = resources[1].end = irq;
if (!vm_cmdline_parent_registered) {
- err = device_register(&vm_cmdline_parent);
- if (err) {
- put_device(&vm_cmdline_parent);
+ vm_cmdline_parent = __root_device_register("virtio-mmio-cmdline", NULL);
+ if (IS_ERR(vm_cmdline_parent)) {
pr_err("Failed to register parent device!\n");
- return err;
+ return PTR_ERR(vm_cmdline_parent);
}
vm_cmdline_parent_registered = 1;
}
@@ -719,7 +715,7 @@ static int vm_cmdline_set(const char *device,
(unsigned long long)resources[0].end,
(int)resources[1].start);
- pdev = platform_device_register_resndata(&vm_cmdline_parent,
+ pdev = platform_device_register_resndata(vm_cmdline_parent,
"virtio-mmio", vm_cmdline_id++,
resources, ARRAY_SIZE(resources), NULL, 0);
@@ -743,8 +739,12 @@ static int vm_cmdline_get_device(struct device *dev, void *data)
static int vm_cmdline_get(char *buffer, const struct kernel_param *kp)
{
buffer[0] = '\0';
- device_for_each_child(&vm_cmdline_parent, buffer,
- vm_cmdline_get_device);
+
+ if (vm_cmdline_parent_registered) {
+ device_for_each_child(vm_cmdline_parent, buffer,
+ vm_cmdline_get_device);
+ }
+
return strlen(buffer) + 1;
}
@@ -766,9 +766,9 @@ static int vm_unregister_cmdline_device(struct device *dev,
static void vm_unregister_cmdline_devices(void)
{
if (vm_cmdline_parent_registered) {
- device_for_each_child(&vm_cmdline_parent, NULL,
+ device_for_each_child(vm_cmdline_parent, NULL,
vm_unregister_cmdline_device);
- device_unregister(&vm_cmdline_parent);
+ root_device_unregister(vm_cmdline_parent);
vm_cmdline_parent_registered = 0;
}
}
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index da97b6a988de..164f480b18a6 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -423,10 +423,11 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs,
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_find_one_vq_msix(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_find_one_vq_msix(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx, false,
&allocated_vectors, vector_policy,
- &vp_dev->vqs[i]);
+ &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto error_find;
@@ -485,9 +486,10 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs,
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_setup_vq(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_setup_vq(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx,
- VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[i]);
+ VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto out_del_vqs;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index fbca7ce1c6bf..b438dc2ce1b8 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -272,6 +272,55 @@ struct vring_virtqueue {
#endif
};
+/*
+ * Accessors for device-writable fields in virtio rings.
+ * These fields are concurrently written by the device and read by the driver.
+ * Use READ_ONCE() to prevent compiler optimizations, document the
+ * intentional data race and prevent KCSAN warnings.
+ */
+static inline u16 vring_read_split_used_idx(const struct vring_virtqueue *vq)
+{
+ return virtio16_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->idx));
+}
+
+static inline u32 vring_read_split_used_id(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return virtio32_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->ring[idx].id));
+}
+
+static inline u32 vring_read_split_used_len(const struct vring_virtqueue *vq, u16 idx)
+{
+ return virtio32_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->ring[idx].len));
+}
+
+static inline u16 vring_read_split_avail_event(const struct vring_virtqueue *vq)
+{
+ return virtio16_to_cpu(vq->vq.vdev,
+ READ_ONCE(vring_avail_event(&vq->split.vring)));
+}
+
+static inline u16 vring_read_packed_desc_flags(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le16_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].flags));
+}
+
+static inline u16 vring_read_packed_desc_id(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le16_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].id));
+}
+
+static inline u32 vring_read_packed_desc_len(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le32_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].len));
+}
+
static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num);
static void vring_free(struct virtqueue *_vq);
@@ -809,8 +858,7 @@ static bool virtqueue_kick_prepare_split(struct vring_virtqueue *vq)
LAST_ADD_TIME_INVALID(vq);
if (vq->event) {
- needs_kick = vring_need_event(virtio16_to_cpu(vq->vq.vdev,
- vring_avail_event(&vq->split.vring)),
+ needs_kick = vring_need_event(vring_read_split_avail_event(vq),
new, old);
} else {
needs_kick = !(vq->split.vring.used->flags &
@@ -897,8 +945,7 @@ static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head,
static bool virtqueue_poll_split(const struct vring_virtqueue *vq,
unsigned int last_used_idx)
{
- return (u16)last_used_idx != virtio16_to_cpu(vq->vq.vdev,
- vq->split.vring.used->idx);
+ return (u16)last_used_idx != vring_read_split_used_idx(vq);
}
static bool more_used_split(const struct vring_virtqueue *vq)
@@ -939,10 +986,8 @@ static void *virtqueue_get_buf_ctx_split(struct vring_virtqueue *vq,
virtio_rmb(vq->weak_barriers);
last_used = (vq->last_used_idx & (vq->split.vring.num - 1));
- i = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used].id);
- *len = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used].len);
+ i = vring_read_split_used_id(vq, last_used);
+ *len = vring_read_split_used_len(vq, last_used);
if (unlikely(i >= vq->split.vring.num)) {
BAD_RING(vq, "id %u out of range\n", i);
@@ -1003,10 +1048,8 @@ static void *virtqueue_get_buf_ctx_split_in_order(struct vring_virtqueue *vq,
*/
virtio_rmb(vq->weak_barriers);
- vq->batch_last.id = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used_idx].id);
- vq->batch_last.len = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used_idx].len);
+ vq->batch_last.id = vring_read_split_used_id(vq, last_used_idx);
+ vq->batch_last.len = vring_read_split_used_len(vq, last_used_idx);
}
if (vq->batch_last.id == last_used) {
@@ -1112,7 +1155,7 @@ static bool virtqueue_enable_cb_delayed_split(struct vring_virtqueue *vq)
&vring_used_event(&vq->split.vring),
cpu_to_virtio16(vq->vq.vdev, vq->last_used_idx + bufs));
- if (unlikely((u16)(virtio16_to_cpu(vq->vq.vdev, vq->split.vring.used->idx)
+ if (unlikely((u16)(vring_read_split_used_idx(vq)
- vq->last_used_idx) > bufs)) {
END_USE(vq);
return false;
@@ -2036,10 +2079,10 @@ static void detach_buf_packed(struct vring_virtqueue *vq,
static inline bool is_used_desc_packed(const struct vring_virtqueue *vq,
u16 idx, bool used_wrap_counter)
{
- bool avail, used;
u16 flags;
+ bool avail, used;
- flags = le16_to_cpu(vq->packed.vring.desc[idx].flags);
+ flags = vring_read_packed_desc_flags(vq, idx);
avail = !!(flags & (1 << VRING_PACKED_DESC_F_AVAIL));
used = !!(flags & (1 << VRING_PACKED_DESC_F_USED));
@@ -2186,8 +2229,8 @@ static void *virtqueue_get_buf_ctx_packed(struct vring_virtqueue *vq,
last_used_idx = READ_ONCE(vq->last_used_idx);
used_wrap_counter = packed_used_wrap_counter(last_used_idx);
last_used = packed_last_used(last_used_idx);
- id = le16_to_cpu(vq->packed.vring.desc[last_used].id);
- *len = le32_to_cpu(vq->packed.vring.desc[last_used].len);
+ id = vring_read_packed_desc_id(vq, last_used);
+ *len = vring_read_packed_desc_len(vq, last_used);
if (unlikely(id >= num)) {
BAD_RING(vq, "id %u out of range\n", id);
diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc_driver.c
index a57d5e06e19d..4419735b0f0d 100644
--- a/drivers/virtio/virtio_rtc_driver.c
+++ b/drivers/virtio/virtio_rtc_driver.c
@@ -1257,6 +1257,15 @@ static int viortc_init_vqs(struct viortc_dev *viortc)
return 0;
}
+static void __viortc_remove(struct viortc_dev *viortc)
+{
+ struct virtio_device *vdev = viortc->vdev;
+
+ viortc_clocks_deinit(viortc);
+ virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
+}
+
/**
* viortc_probe() - probe a virtio_rtc virtio device
* @vdev: virtio device
@@ -1282,7 +1291,7 @@ static int viortc_probe(struct virtio_device *vdev)
ret = viortc_init_vqs(viortc);
if (ret)
- return ret;
+ goto err_reset_vdev;
virtio_device_ready(vdev);
@@ -1329,10 +1338,7 @@ static void viortc_remove(struct virtio_device *vdev)
{
struct viortc_dev *viortc = vdev->priv;
- viortc_clocks_deinit(viortc);
-
- virtio_reset_device(vdev);
- vdev->config->del_vqs(vdev);
+ __viortc_remove(viortc);
}
static int viortc_freeze(struct virtio_device *dev)
@@ -1353,9 +1359,11 @@ static int viortc_restore(struct virtio_device *dev)
bool notify = false;
int ret;
+ dev->config->del_vqs(dev);
+
ret = viortc_init_vqs(viortc);
if (ret)
- return ret;
+ goto err_remove;
alarm_viortc_vq = &viortc->vqs[VIORTC_ALARMQ];
alarm_vq = alarm_viortc_vq->vq;
@@ -1364,7 +1372,7 @@ static int viortc_restore(struct virtio_device *dev)
ret = viortc_populate_vq(viortc, alarm_viortc_vq,
VIORTC_ALARMQ_BUF_CAP, false);
if (ret)
- return ret;
+ goto err_remove;
notify = virtqueue_kick_prepare(alarm_vq);
}
@@ -1372,8 +1380,12 @@ static int viortc_restore(struct virtio_device *dev)
virtio_device_ready(dev);
if (notify && !virtqueue_notify(alarm_vq))
- ret = -EIO;
+ return -EIO;
+
+ return 0;
+err_remove:
+ __viortc_remove(viortc);
return ret;
}