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
  ...
This commit is contained in:
Linus Torvalds
2026-06-17 11:49:00 -07:00
35 changed files with 1690 additions and 184 deletions
+9
View File
@@ -28326,6 +28326,15 @@ F: drivers/scsi/virtio_scsi.c
F: include/uapi/linux/virtio_blk.h
F: include/uapi/linux/virtio_scsi.h
VIRTIO CAN DRIVER
M: "Harald Mommer" <harald.mommer@oss.qualcomm.com>
M: "Matias Ezequiel Vara Larsen" <mvaralar@redhat.com>
L: virtualization@lists.linux.dev
L: linux-can@vger.kernel.org
S: Maintained
F: drivers/net/can/virtio_can.c
F: include/uapi/linux/virtio_can.h
VIRTIO CONSOLE DRIVER
M: Amit Shah <amit@kernel.org>
L: virtualization@lists.linux.dev
+21 -2
View File
@@ -7,6 +7,7 @@
#include <asm/barrier.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/nospec.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/virtio.h>
@@ -69,8 +70,26 @@ static void request_entropy(struct virtrng_info *vi)
static unsigned int copy_data(struct virtrng_info *vi, void *buf,
unsigned int size)
{
size = min_t(unsigned int, size, vi->data_avail);
memcpy(buf, vi->data + vi->data_idx, size);
unsigned int idx, avail;
/*
* vi->data_avail was set from the device-reported used.len and
* vi->data_idx was advanced by previous copy_data() calls. A
* malicious or buggy virtio-rng backend can drive either past
* sizeof(vi->data). Clamp at point of use and harden the index
* with array_index_nospec() so the memcpy() below cannot be
* steered into adjacent slab memory, including under
* speculation.
*/
avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data));
if (vi->data_idx >= avail) {
vi->data_avail = 0;
request_entropy(vi);
return 0;
}
size = min_t(unsigned int, size, avail - vi->data_idx);
idx = array_index_nospec(vi->data_idx, sizeof(vi->data));
memcpy(buf, vi->data + idx, size);
vi->data_idx += size;
vi->data_avail -= size;
if (vi->data_avail == 0)
+31 -21
View File
@@ -1771,32 +1771,40 @@ static void config_intr(struct virtio_device *vdev)
schedule_work(&portdev->config_work);
}
static void update_size_from_config(struct ports_device *portdev)
{
struct virtio_device *vdev;
struct port *port;
u16 rows, cols;
vdev = portdev->vdev;
/*
* We'll use this way of resizing only for legacy support.
* For multiport devices, use control messages to indicate
* console size changes so that it can be done per-port.
*
* Don't test F_SIZE at all if we're rproc: not a valid feature.
*/
if (is_rproc_serial(vdev) ||
use_multiport(portdev) ||
!virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE))
return;
virtio_cread(vdev, struct virtio_console_config, cols, &cols);
virtio_cread(vdev, struct virtio_console_config, rows, &rows);
port = find_port_by_id(portdev, 0);
set_console_size(port, rows, cols);
resize_console(port);
}
static void config_work_handler(struct work_struct *work)
{
struct ports_device *portdev;
portdev = container_of(work, struct ports_device, config_work);
if (!use_multiport(portdev)) {
struct virtio_device *vdev;
struct port *port;
u16 rows, cols;
vdev = portdev->vdev;
virtio_cread(vdev, struct virtio_console_config, cols, &cols);
virtio_cread(vdev, struct virtio_console_config, rows, &rows);
port = find_port_by_id(portdev, 0);
set_console_size(port, rows, cols);
/*
* We'll use this way of resizing only for legacy
* support. For newer userspace
* (VIRTIO_CONSOLE_F_MULTPORT+), use control messages
* to indicate console size changes so that it can be
* done per-port.
*/
resize_console(port);
}
update_size_from_config(portdev);
}
static int init_vqs(struct ports_device *portdev)
@@ -2052,6 +2060,8 @@ static int virtcons_probe(struct virtio_device *vdev)
__send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID,
VIRTIO_CONSOLE_DEVICE_READY, 1);
update_size_from_config(portdev);
return 0;
free_chrdev:
+1 -1
View File
@@ -124,7 +124,7 @@ config RASPBERRYPI_FIRMWARE
config FW_CFG_SYSFS
tristate "QEMU fw_cfg device support in sysfs"
depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || RISCV || SPARC || X86)
depends on SYSFS && (ARM || ARM64 || LOONGARCH || PARISC || PPC_PMAC || RISCV || SPARC || X86)
depends on HAS_IOPORT_MAP
default n
help
+1 -1
View File
@@ -211,7 +211,7 @@ static void fw_cfg_io_cleanup(void)
/* arch-specific ctrl & data register offsets are not available in ACPI, DT */
#if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF))
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV))
# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_LOONGARCH) || defined(CONFIG_RISCV))
# define FW_CFG_CTRL_OFF 0x08
# define FW_CFG_DATA_OFF 0x00
# define FW_CFG_DMA_OFF 0x10
+12
View File
@@ -226,6 +226,18 @@ config CAN_TI_HECC
Driver for TI HECC (High End CAN Controller) module found on many
TI devices. The device specifications are available from www.ti.com
config CAN_VIRTIO_CAN
depends on VIRTIO
tristate "Virtio CAN device support"
default n
help
Say Y here if you want to support for Virtio CAN.
To compile this driver as a module, choose M here: the
module will be called virtio-can.
If unsure, say N.
config CAN_XILINXCAN
tristate "Xilinx CAN"
depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST
+1
View File
@@ -33,6 +33,7 @@ obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/
obj-$(CONFIG_CAN_SJA1000) += sja1000/
obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_VIRTIO_CAN) += virtio_can.o
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG
File diff suppressed because it is too large Load Diff
+9 -2
View File
@@ -734,15 +734,22 @@ static int ifcvf_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
ret = dev_set_name(&vdpa_dev->dev, "%s", name);
else
ret = dev_set_name(&vdpa_dev->dev, "vdpa%u", vdpa_dev->index);
if (ret) {
IFCVF_ERR(pdev, "Failed to set device name");
goto err;
}
ret = _vdpa_register_device(&adapter->vdpa, vf->nr_vring);
if (ret) {
put_device(&adapter->vdpa.dev);
IFCVF_ERR(pdev, "Failed to register to vDPA bus");
return ret;
goto err;
}
return 0;
err:
put_device(&adapter->vdpa.dev);
return ret;
}
static void ifcvf_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev)
+3 -4
View File
@@ -221,11 +221,10 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *
list_for_each_entry(dmr, &mr->head, list) {
struct mlx5_create_mkey_mem *cmd_mem;
int mttlen, mttcount;
int mttcount;
mttlen = roundup(MLX5_ST_SZ_BYTES(mtt) * dmr->nsg, MLX5_VDPA_MTT_ALIGN);
mttcount = mttlen / sizeof(cmd_mem->mtt[0]);
cmd_mem = kvcalloc(1, struct_size(cmd_mem, mtt, mttcount), GFP_KERNEL);
mttcount = ALIGN(dmr->nsg, MLX5_VDPA_MTT_ALIGN / sizeof(cmd_mem->mtt[0]));
cmd_mem = kvzalloc_flex(*cmd_mem, mtt, mttcount);
if (!cmd_mem) {
err = -ENOMEM;
goto done;
+21 -1
View File
@@ -30,8 +30,10 @@
#define OCTEP_EPF_RINFO(x) (0x000209f0 | ((x) << 25))
#define OCTEP_VF_MBOX_DATA(x) (0x00010210 | ((x) << 17))
#define OCTEP_PF_MBOX_DATA(x) (0x00022000 | ((x) << 4))
#define OCTEP_VF_EVENT_STATE(x) (0x00010030 | ((x) << 17))
#define OCTEP_VF_EVENT_REG(x) (0x00010060 | ((x) << 17))
#define OCTEP_VF_IN_CTRL(x) (0x00010000 | ((x) << 17))
#define OCTEP_VF_IN_CTRL_RPVF(val) (((val) >> 48) & 0xF)
#define OCTEP_VF_IN_CTRL_RPVF(val) (FIELD_GET(GENMASK_ULL(51, 48), val))
#define OCTEP_FW_READY_SIGNATURE0 0xFEEDFEED
#define OCTEP_FW_READY_SIGNATURE1 0x3355ffaa
@@ -43,9 +45,26 @@ enum octep_vdpa_dev_status {
OCTEP_VDPA_DEV_STATUS_WAIT_FOR_BAR_INIT,
OCTEP_VDPA_DEV_STATUS_INIT,
OCTEP_VDPA_DEV_STATUS_READY,
OCTEP_VDPA_DEV_STATUS_ADDED,
OCTEP_VDPA_DEV_STATUS_REMOVED,
OCTEP_VDPA_DEV_STATUS_UNINIT
};
enum octep_vdpa_dev_event_state {
OCTEP_VDPA_DEV_NO_EVENT,
OCTEP_VDPA_DEV_NEW_EVENT,
OCTEP_VDPA_DEV_EVENT_ACTIVE,
OCTEP_VDPA_DEV_EVENT_DONE,
};
enum octep_vdpa_dev_event {
OCTEP_VDPA_DEV_EVENT_NONE,
OCTEP_VDPA_DEV_EVENT_ACK,
OCTEP_VDPA_DEV_EVENT_NACK,
OCTEP_VDPA_DEV_ADD_EVENT,
OCTEP_VDPA_DEV_DEL_EVENT,
};
struct octep_vring_info {
struct vdpa_callback cb;
void __iomem *notify_addr;
@@ -86,6 +105,7 @@ struct octep_hw {
u64 features;
u16 nr_vring;
u32 config_size;
int requested_irqs;
int nb_irqs;
int *irqs;
u8 dev_id;
+113 -18
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2024 Marvell. */
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
@@ -8,6 +9,7 @@
#include "octep_vdpa.h"
#define OCTEP_VDPA_DRIVER_NAME "octep_vdpa"
#define OCTEP_VDPA_NAME_BUFSIZE 16
struct octep_pf {
u8 __iomem *base[PCI_STD_NUM_BARS];
@@ -19,6 +21,11 @@ struct octep_pf {
u16 vf_devid;
};
struct octep_vdpa_event_wk {
struct work_struct work;
void *ctxptr;
};
struct octep_vdpa {
struct vdpa_device vdpa;
struct octep_hw *oct_hw;
@@ -33,6 +40,8 @@ struct octep_vdpa_mgmt_dev {
struct work_struct setup_task;
/* Device status */
atomic_t status;
struct octep_vdpa *oct_vdpa;
struct octep_vdpa_event_wk event_wk;
};
static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev)
@@ -44,10 +53,31 @@ static struct octep_hw *vdpa_to_octep_hw(struct vdpa_device *vdpa_dev)
return oct_vdpa->oct_hw;
}
static inline void octep_vdpa_dev_event_schedule(struct octep_hw *oct_hw)
{
u8 __iomem *addr = oct_hw->base[OCTEP_HW_MBOX_BAR];
struct octep_vdpa_mgmt_dev *mgmt_dev;
mgmt_dev = container_of(oct_hw, struct octep_vdpa_mgmt_dev, oct_hw);
writeb(OCTEP_VDPA_DEV_EVENT_ACTIVE, addr + OCTEP_VF_EVENT_STATE(0));
schedule_work(&mgmt_dev->event_wk.work);
}
static irqreturn_t octep_vdpa_dev_event_handler(int irq, void *data)
{
struct octep_hw *oct_hw = data;
if (readb(oct_hw->base[OCTEP_HW_MBOX_BAR] + OCTEP_VF_EVENT_STATE(0)) ==
OCTEP_VDPA_DEV_NEW_EVENT)
octep_vdpa_dev_event_schedule(oct_hw);
return IRQ_HANDLED;
}
static irqreturn_t octep_vdpa_intr_handler(int irq, void *data)
{
struct octep_hw *oct_hw = data;
int i;
int i, start_ring_idx = -1;
/* Each device has multiple interrupts (nb_irqs) shared among rings
* (nr_vring). Device interrupts are mapped to the rings in a
@@ -60,7 +90,16 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data)
* 7 -> 7, 15, 23, 31, 39, 47, 55, 63;
*/
for (i = irq - oct_hw->irqs[0]; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) {
for (i = 0; i < oct_hw->nb_irqs; i++) {
if (oct_hw->irqs[i] == irq) {
start_ring_idx = i;
break;
}
}
if (start_ring_idx == -1)
return IRQ_NONE;
for (i = start_ring_idx; i < oct_hw->nr_vring; i += oct_hw->nb_irqs) {
if (ioread8(oct_hw->vqs[i].cb_notify_addr)) {
/* Acknowledge the per ring notification to the device */
iowrite8(0, oct_hw->vqs[i].cb_notify_addr);
@@ -72,11 +111,14 @@ static irqreturn_t octep_vdpa_intr_handler(int irq, void *data)
}
/* Check for config interrupt. Config uses the first interrupt */
if (unlikely(irq == oct_hw->irqs[0] && ioread8(oct_hw->isr))) {
iowrite8(0, oct_hw->isr);
if (unlikely(irq == oct_hw->irqs[0])) {
if (ioread8(oct_hw->isr)) {
iowrite8(0, oct_hw->isr);
if (oct_hw->config_cb.callback)
oct_hw->config_cb.callback(oct_hw->config_cb.private);
if (oct_hw->config_cb.callback)
oct_hw->config_cb.callback(oct_hw->config_cb.private);
}
octep_vdpa_dev_event_handler(irq, data);
}
return IRQ_HANDLED;
@@ -100,33 +142,41 @@ static void octep_free_irqs(struct octep_hw *oct_hw)
pci_free_irq_vectors(pdev);
devm_kfree(&pdev->dev, oct_hw->irqs);
oct_hw->irqs = NULL;
oct_hw->requested_irqs = 0;
}
static int octep_request_irqs(struct octep_hw *oct_hw)
static int octep_request_irqs(struct octep_hw *oct_hw, irqreturn_t (*irq_handler)(int, void *),
int nb_irqs)
{
struct pci_dev *pdev = oct_hw->pdev;
int ret, irq, idx;
oct_hw->irqs = devm_kcalloc(&pdev->dev, oct_hw->nb_irqs, sizeof(int), GFP_KERNEL);
if ((oct_hw->requested_irqs != nb_irqs) || (nb_irqs == 1))
octep_free_irqs(oct_hw);
else
return 0;
oct_hw->irqs = devm_kcalloc(&pdev->dev, nb_irqs, sizeof(int), GFP_KERNEL);
if (!oct_hw->irqs)
return -ENOMEM;
ret = pci_alloc_irq_vectors(pdev, 1, oct_hw->nb_irqs, PCI_IRQ_MSIX);
ret = pci_alloc_irq_vectors(pdev, 1, nb_irqs, PCI_IRQ_MSIX);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to alloc msix vector");
return ret;
}
for (idx = 0; idx < oct_hw->nb_irqs; idx++) {
for (idx = 0; idx < nb_irqs; idx++) {
irq = pci_irq_vector(pdev, idx);
ret = devm_request_irq(&pdev->dev, irq, octep_vdpa_intr_handler, 0,
dev_name(&pdev->dev), oct_hw);
ret = devm_request_irq(&pdev->dev, irq, irq_handler, 0, dev_name(&pdev->dev),
oct_hw);
if (ret) {
dev_err(&pdev->dev, "Failed to register interrupt handler\n");
goto free_irqs;
}
oct_hw->irqs[idx] = irq;
}
oct_hw->requested_irqs = nb_irqs;
return 0;
@@ -188,7 +238,7 @@ static void octep_vdpa_set_status(struct vdpa_device *vdpa_dev, u8 status)
if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
!(status_old & VIRTIO_CONFIG_S_DRIVER_OK)) {
if (octep_request_irqs(oct_hw))
if (octep_request_irqs(oct_hw, octep_vdpa_intr_handler, oct_hw->nb_irqs))
status = status_old | VIRTIO_CONFIG_S_FAILED;
}
octep_hw_set_status(oct_hw, status);
@@ -211,8 +261,10 @@ static int octep_vdpa_reset(struct vdpa_device *vdpa_dev)
}
octep_hw_reset(oct_hw);
if (status & VIRTIO_CONFIG_S_DRIVER_OK)
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
octep_free_irqs(oct_hw);
octep_request_irqs(oct_hw, octep_vdpa_dev_event_handler, 1);
}
return 0;
}
@@ -477,7 +529,8 @@ static void octep_vdpa_remove_vf(struct pci_dev *pdev)
atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_UNINIT);
cancel_work_sync(&mgmt_dev->setup_task);
if (status == OCTEP_VDPA_DEV_STATUS_READY)
if ((status == OCTEP_VDPA_DEV_STATUS_READY) || (status == OCTEP_VDPA_DEV_STATUS_ADDED) ||
(status == OCTEP_VDPA_DEV_STATUS_REMOVED))
vdpa_mgmtdev_unregister(&mgmt_dev->mdev);
if (oct_hw->base[OCTEP_HW_CAPS_BAR])
@@ -487,6 +540,7 @@ static void octep_vdpa_remove_vf(struct pci_dev *pdev)
octep_iounmap_region(pdev, oct_hw->base, OCTEP_HW_MBOX_BAR);
octep_vdpa_vf_bar_shrink(pdev);
octep_free_irqs(oct_hw);
}
static void octep_vdpa_remove(struct pci_dev *pdev)
@@ -520,6 +574,7 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
oct_vdpa->vdpa.mdev = mdev;
oct_vdpa->oct_hw = oct_hw;
vdpa_dev = &oct_vdpa->vdpa;
mgmt_dev->oct_vdpa = oct_vdpa;
device_features = oct_hw->features;
if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
@@ -553,6 +608,7 @@ static int octep_vdpa_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
dev_err(&pdev->dev, "Failed to register to vDPA bus");
goto vdpa_dev_put;
}
atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_ADDED);
return 0;
vdpa_dev_put:
@@ -562,7 +618,9 @@ vdpa_dev_put:
static void octep_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *vdpa_dev)
{
struct octep_vdpa_mgmt_dev *mgmt_dev = container_of(mdev, struct octep_vdpa_mgmt_dev, mdev);
_vdpa_unregister_device(vdpa_dev);
atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_REMOVED);
}
static const struct vdpa_mgmtdev_ops octep_vdpa_mgmt_dev_ops = {
@@ -572,10 +630,10 @@ static const struct vdpa_mgmtdev_ops octep_vdpa_mgmt_dev_ops = {
static bool get_device_ready_status(u8 __iomem *addr)
{
u64 signature = readq(addr + OCTEP_VF_MBOX_DATA(0));
u32 signature = readl(addr + OCTEP_VF_MBOX_DATA(0));
if (signature == OCTEP_DEV_READY_SIGNATURE) {
writeq(0, addr + OCTEP_VF_MBOX_DATA(0));
writel(0, addr + OCTEP_VF_MBOX_DATA(0));
return true;
}
@@ -587,6 +645,36 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
static void octep_event_work(struct work_struct *work)
{
struct octep_vdpa_event_wk *wk = container_of(work, struct octep_vdpa_event_wk, work);
struct octep_vdpa_mgmt_dev *mgmt_dev = (struct octep_vdpa_mgmt_dev *)wk->ctxptr;
u8 __iomem *addr = mgmt_dev->oct_hw.base[OCTEP_HW_MBOX_BAR];
u8 event = readb(addr + OCTEP_VF_EVENT_REG(0));
struct vdpa_dev_set_config config = {0};
char name[OCTEP_VDPA_NAME_BUFSIZE];
int ret = 0;
switch (event) {
case OCTEP_VDPA_DEV_ADD_EVENT:
if (atomic_read(&mgmt_dev->status) != OCTEP_VDPA_DEV_STATUS_ADDED) {
snprintf(name, sizeof(name), "%s-%x", "vdpa", mgmt_dev->pdev->devfn);
ret = octep_vdpa_dev_add(&mgmt_dev->mdev, name, &config);
}
break;
case OCTEP_VDPA_DEV_DEL_EVENT:
if (atomic_read(&mgmt_dev->status) == OCTEP_VDPA_DEV_STATUS_ADDED)
octep_vdpa_dev_del(&mgmt_dev->mdev, &mgmt_dev->oct_vdpa->vdpa);
break;
default:
break;
}
event = ret ? OCTEP_VDPA_DEV_EVENT_NACK : OCTEP_VDPA_DEV_EVENT_ACK;
writeb(event, addr + OCTEP_VF_EVENT_REG(0));
writeb(OCTEP_VDPA_DEV_EVENT_DONE, addr + OCTEP_VF_EVENT_STATE(0));
}
static void octep_vdpa_setup_task(struct work_struct *work)
{
struct octep_vdpa_mgmt_dev *mgmt_dev = container_of(work, struct octep_vdpa_mgmt_dev,
@@ -652,6 +740,9 @@ static void octep_vdpa_setup_task(struct work_struct *work)
}
atomic_set(&mgmt_dev->status, OCTEP_VDPA_DEV_STATUS_READY);
INIT_WORK(&mgmt_dev->event_wk.work, octep_event_work);
mgmt_dev->event_wk.ctxptr = mgmt_dev;
octep_request_irqs(&mgmt_dev->oct_hw, octep_vdpa_dev_event_handler, 1);
return;
@@ -722,6 +813,8 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs)
bool done = false;
int index = 0;
int ret, i;
u8 rpvf;
u64 val;
ret = pci_enable_sriov(pdev, num_vfs);
if (ret)
@@ -741,9 +834,11 @@ static int octep_sriov_enable(struct pci_dev *pdev, int num_vfs)
}
}
val = readq(addr + OCTEP_EPF_RINFO(0));
rpvf = FIELD_GET(GENMASK_ULL(35, 32), val);
if (done) {
for (i = 0; i < pf->enabled_vfs; i++)
writeq(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i));
writel(OCTEP_DEV_READY_SIGNATURE, addr + OCTEP_PF_MBOX_DATA(i * rpvf));
}
return num_vfs;
+8 -16
View File
@@ -397,14 +397,7 @@ static void vdpasim_blk_free(struct vdpasim *vdpasim)
kvfree(blk->buffer);
}
static void vdpasim_blk_mgmtdev_release(struct device *dev)
{
}
static struct device vdpasim_blk_mgmtdev = {
.init_name = "vdpasim_blk",
.release = vdpasim_blk_mgmtdev_release,
};
static struct device *vdpasim_blk_mgmtdev;
static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
const struct vdpa_dev_set_config *config)
@@ -475,7 +468,6 @@ static struct virtio_device_id id_table[] = {
};
static struct vdpa_mgmt_dev mgmt_dev = {
.device = &vdpasim_blk_mgmtdev,
.id_table = id_table,
.ops = &vdpasim_blk_mgmtdev_ops,
};
@@ -484,12 +476,11 @@ static int __init vdpasim_blk_init(void)
{
int ret;
ret = device_register(&vdpasim_blk_mgmtdev);
if (ret) {
put_device(&vdpasim_blk_mgmtdev);
return ret;
}
vdpasim_blk_mgmtdev = root_device_register("vdpasim_blk");
if (IS_ERR(vdpasim_blk_mgmtdev))
return PTR_ERR(vdpasim_blk_mgmtdev);
mgmt_dev.device = vdpasim_blk_mgmtdev;
ret = vdpa_mgmtdev_register(&mgmt_dev);
if (ret)
goto parent_err;
@@ -507,7 +498,8 @@ static int __init vdpasim_blk_init(void)
mgmt_dev_err:
vdpa_mgmtdev_unregister(&mgmt_dev);
parent_err:
device_unregister(&vdpasim_blk_mgmtdev);
root_device_unregister(vdpasim_blk_mgmtdev);
return ret;
}
@@ -515,7 +507,7 @@ static void __exit vdpasim_blk_exit(void)
{
kvfree(shared_buffer);
vdpa_mgmtdev_unregister(&mgmt_dev);
device_unregister(&vdpasim_blk_mgmtdev);
root_device_unregister(vdpasim_blk_mgmtdev);
}
module_init(vdpasim_blk_init)
+7 -16
View File
@@ -453,14 +453,7 @@ static void vdpasim_net_free(struct vdpasim *vdpasim)
kvfree(net->buffer);
}
static void vdpasim_net_mgmtdev_release(struct device *dev)
{
}
static struct device vdpasim_net_mgmtdev = {
.init_name = "vdpasim_net",
.release = vdpasim_net_mgmtdev_release,
};
static struct device *vdpasim_net_mgmtdev;
static int vdpasim_net_dev_add(struct vdpa_mgmt_dev *mdev, const char *name,
const struct vdpa_dev_set_config *config)
@@ -538,7 +531,6 @@ static struct virtio_device_id id_table[] = {
};
static struct vdpa_mgmt_dev mgmt_dev = {
.device = &vdpasim_net_mgmtdev,
.id_table = id_table,
.ops = &vdpasim_net_mgmtdev_ops,
.config_attr_mask = (1 << VDPA_ATTR_DEV_NET_CFG_MACADDR |
@@ -552,26 +544,25 @@ static int __init vdpasim_net_init(void)
{
int ret;
ret = device_register(&vdpasim_net_mgmtdev);
if (ret) {
put_device(&vdpasim_net_mgmtdev);
return ret;
}
vdpasim_net_mgmtdev = root_device_register("vdpasim_net");
if (IS_ERR(vdpasim_net_mgmtdev))
return PTR_ERR(vdpasim_net_mgmtdev);
mgmt_dev.device = vdpasim_net_mgmtdev;
ret = vdpa_mgmtdev_register(&mgmt_dev);
if (ret)
goto parent_err;
return 0;
parent_err:
device_unregister(&vdpasim_net_mgmtdev);
root_device_unregister(vdpasim_net_mgmtdev);
return ret;
}
static void __exit vdpasim_net_exit(void)
{
vdpa_mgmtdev_unregister(&mgmt_dev);
device_unregister(&vdpasim_net_mgmtdev);
root_device_unregister(vdpasim_net_mgmtdev);
}
module_init(vdpasim_net_init);
+1 -1
View File
@@ -124,7 +124,7 @@ static int vduse_domain_map_bounce_page(struct vduse_iova_domain *domain,
if (!map->bounce_page) {
head_map = &domain->bounce_maps[(iova & PAGE_MASK) >> BOUNCE_MAP_SHIFT];
if (!head_map->bounce_page) {
tmp_page = alloc_page(GFP_ATOMIC);
tmp_page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
if (!tmp_page)
return -ENOMEM;
if (cmpxchg(&head_map->bounce_page, NULL, tmp_page))
+167 -30
View File
@@ -221,6 +221,12 @@ static void vduse_enqueue_msg(struct list_head *head,
list_add_tail(&msg->list, head);
}
static void vduse_enqueue_msg_head(struct list_head *head,
struct vduse_dev_msg *msg)
{
list_add(&msg->list, head);
}
static void vduse_dev_broken(struct vduse_dev *dev)
{
struct vduse_dev_msg *msg, *tmp;
@@ -358,6 +364,7 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
struct file *file = iocb->ki_filp;
struct vduse_dev *dev = file->private_data;
struct vduse_dev_msg *msg;
struct vduse_dev_request req;
int size = sizeof(struct vduse_dev_request);
ssize_t ret;
@@ -369,12 +376,11 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
msg = vduse_dequeue_msg(&dev->send_list);
if (msg)
break;
ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
goto unlock;
spin_unlock(&dev->msg_lock);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible_exclusive(dev->waitq,
!list_empty(&dev->send_list));
if (ret)
@@ -382,18 +388,35 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
spin_lock(&dev->msg_lock);
}
spin_unlock(&dev->msg_lock);
ret = copy_to_iter(&msg->req, size, to);
spin_lock(&dev->msg_lock);
if (ret != size) {
ret = -EFAULT;
vduse_enqueue_msg(&dev->send_list, msg);
goto unlock;
}
memcpy(&req, &msg->req, sizeof(req));
/*
* We must ensure vduse_msg is on send_list or recv_list before unlock
* dev->msg_lock. Because vduse_dev_msg_sync() may be timeout when we
* copy data to userspace, and will call list_del() for this msg.
*/
vduse_enqueue_msg(&dev->recv_list, msg);
unlock:
spin_unlock(&dev->msg_lock);
ret = copy_to_iter(&req, size, to);
if (ret != size) {
/*
* Roll back: move msg back to send_list if still pending.
*
* NOTE:
* vduse_find_msg() must use req.request_id instead of `msg`.
* A malicious userspace may reply to this request, and wake up
* the caller, after which `msg` will have already been freed.
* And here vduse_find_msg() will return NULL then do nothing.
*/
spin_lock(&dev->msg_lock);
msg = vduse_find_msg(&dev->recv_list, req.request_id);
if (msg)
vduse_enqueue_msg_head(&dev->send_list, msg);
spin_unlock(&dev->msg_lock);
ret = -EFAULT;
}
return ret;
}
@@ -976,7 +999,7 @@ static void *vduse_dev_alloc_coherent(union virtio_map token, size_t size,
if (!token.group)
return NULL;
addr = alloc_pages_exact(size, flag);
addr = alloc_pages_exact(size, flag | __GFP_ZERO);
if (!addr)
return NULL;
@@ -1618,6 +1641,127 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
return ret;
}
#ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT
/*
* i386 has different alignment constraints than x86_64,
* so there are only 3 bytes of padding instead of 7.
*/
struct compat_vduse_iotlb_entry {
compat_u64 offset;
compat_u64 start;
compat_u64 last;
__u8 perm;
__u8 padding[3];
};
#define COMPAT_VDUSE_IOTLB_GET_FD _IOWR(VDUSE_BASE, 0x10, struct compat_vduse_iotlb_entry)
struct compat_vduse_vq_info {
__u32 index;
__u32 num;
compat_u64 desc_addr;
compat_u64 driver_addr;
compat_u64 device_addr;
union {
struct vduse_vq_state_split split;
struct vduse_vq_state_packed packed;
};
__u8 ready;
__u8 padding[3];
};
#define COMPAT_VDUSE_VQ_GET_INFO _IOWR(VDUSE_BASE, 0x15, struct compat_vduse_vq_info)
static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct vduse_dev *dev = file->private_data;
void __user *argp = (void __user *)arg;
int ret;
if (unlikely(dev->broken))
return -EPERM;
switch (cmd) {
case COMPAT_VDUSE_IOTLB_GET_FD: {
struct vduse_iotlb_entry_v2 entry = {0};
struct file *f = NULL;
ret = -EFAULT;
if (copy_from_user(&entry, argp, _IOC_SIZE(cmd)))
break;
ret = vduse_dev_iotlb_entry(dev, &entry, &f, NULL);
if (ret)
break;
ret = -EINVAL;
if (!f)
break;
ret = copy_to_user(argp, &entry, _IOC_SIZE(cmd));
if (ret) {
ret = -EFAULT;
fput(f);
break;
}
ret = receive_fd(f, NULL, perm_to_file_flags(entry.perm));
fput(f);
break;
}
case COMPAT_VDUSE_VQ_GET_INFO: {
struct vduse_vq_info vq_info = {};
struct vduse_virtqueue *vq;
u32 index;
ret = -EFAULT;
if (copy_from_user(&vq_info, argp,
sizeof(struct compat_vduse_vq_info)))
break;
ret = -EINVAL;
if (vq_info.index >= dev->vq_num)
break;
index = array_index_nospec(vq_info.index, dev->vq_num);
vq = dev->vqs[index];
vq_info.desc_addr = vq->desc_addr;
vq_info.driver_addr = vq->driver_addr;
vq_info.device_addr = vq->device_addr;
vq_info.num = vq->num;
if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) {
vq_info.packed.last_avail_counter =
vq->state.packed.last_avail_counter;
vq_info.packed.last_avail_idx =
vq->state.packed.last_avail_idx;
vq_info.packed.last_used_counter =
vq->state.packed.last_used_counter;
vq_info.packed.last_used_idx =
vq->state.packed.last_used_idx;
} else
vq_info.split.avail_index =
vq->state.split.avail_index;
vq_info.ready = vq->ready;
ret = -EFAULT;
if (copy_to_user(argp, &vq_info,
sizeof(struct compat_vduse_vq_info)))
break;
ret = 0;
break;
}
default:
ret = -ENOIOCTLCMD;
break;
}
return vduse_dev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
}
#else
#define vduse_dev_compat_ioctl compat_ptr_ioctl
#endif
static int vduse_dev_release(struct inode *inode, struct file *file)
{
struct vduse_dev *dev = file->private_data;
@@ -1637,26 +1781,18 @@ static int vduse_dev_release(struct inode *inode, struct file *file)
return 0;
}
static struct vduse_dev *vduse_dev_get_from_minor(int minor)
static int vduse_dev_open(struct inode *inode, struct file *file)
{
int ret = -EBUSY;
struct vduse_dev *dev;
mutex_lock(&vduse_lock);
dev = idr_find(&vduse_idr, minor);
mutex_unlock(&vduse_lock);
return dev;
}
static int vduse_dev_open(struct inode *inode, struct file *file)
{
int ret;
struct vduse_dev *dev = vduse_dev_get_from_minor(iminor(inode));
if (!dev)
dev = idr_find(&vduse_idr, iminor(inode));
if (!dev) {
mutex_unlock(&vduse_lock);
return -ENODEV;
}
ret = -EBUSY;
mutex_lock(&dev->lock);
if (dev->connected)
goto unlock;
@@ -1666,6 +1802,7 @@ static int vduse_dev_open(struct inode *inode, struct file *file)
file->private_data = dev;
unlock:
mutex_unlock(&dev->lock);
mutex_unlock(&vduse_lock);
return ret;
}
@@ -1678,7 +1815,7 @@ static const struct file_operations vduse_dev_fops = {
.write_iter = vduse_dev_write_iter,
.poll = vduse_dev_poll,
.unlocked_ioctl = vduse_dev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.compat_ioctl = vduse_dev_compat_ioctl,
.llseek = noop_llseek,
};
+11 -4
View File
@@ -399,13 +399,20 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net,
static void vhost_zerocopy_complete(struct sk_buff *skb,
struct ubuf_info *ubuf_base, bool success)
{
struct ubuf_info_msgzc *ubuf = uarg_to_msgzc(ubuf_base);
struct vhost_net_ubuf_ref *ubufs = ubuf->ctx;
struct vhost_virtqueue *vq = ubufs->vq;
struct ubuf_info_msgzc *ubuf;
struct vhost_net_ubuf_ref *ubufs;
struct vhost_virtqueue *vq;
int cnt;
rcu_read_lock_bh();
/* Only the final cloned skb reference completes the vhost descriptor. */
if (!refcount_dec_and_test(&ubuf_base->refcnt))
return;
ubuf = uarg_to_msgzc(ubuf_base);
ubufs = ubuf->ctx;
vq = ubufs->vq;
rcu_read_lock_bh();
/* set len to mark this desc buffers done DMA */
vq->heads[ubuf->desc].len = success ?
VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN;
+22 -7
View File
@@ -1482,16 +1482,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
}
#ifdef CONFIG_MMU
static int
vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index,
struct vdpa_notification_area *notify)
{
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
if (index > 65535 || index >= v->nvqs)
return -EINVAL;
index = array_index_nospec(index, v->nvqs);
*notify = ops->get_vq_notification(vdpa, index);
return 0;
}
static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
{
struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
struct vdpa_notification_area notify;
struct vm_area_struct *vma = vmf->vma;
u16 index = vma->vm_pgoff;
unsigned long index = vma->vm_pgoff;
notify = ops->get_vq_notification(vdpa, index);
if (vhost_vdpa_get_vq_notification(v, index, &notify))
return VM_FAULT_SIGBUS;
return vmf_insert_pfn(vma, vmf->address & PAGE_MASK, PFN_DOWN(notify.addr));
}
@@ -1514,8 +1530,6 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
if (vma->vm_flags & VM_READ)
return -EINVAL;
if (index > 65535)
return -EINVAL;
if (!ops->get_vq_notification)
return -ENOTSUPP;
@@ -1523,7 +1537,8 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
* support the doorbell which sits on the page boundary and
* does not share the page with other registers.
*/
notify = ops->get_vq_notification(vdpa, index);
if (vhost_vdpa_get_vq_notification(v, index, &notify))
return -EINVAL;
if (notify.addr & (PAGE_SIZE - 1))
return -EINVAL;
if (vma->vm_end - vma->vm_start != notify.size)
+7 -16
View File
@@ -1522,6 +1522,7 @@ static void vhost_dev_unlock_vqs(struct vhost_dev *d)
static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq)
{
__virtio16 idx;
u16 avail_idx;
int r;
r = vhost_get_avail(vq, idx, &vq->avail->idx);
@@ -1532,17 +1533,19 @@ static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq)
}
/* Check it isn't doing very strange thing with available indexes */
vq->avail_idx = vhost16_to_cpu(vq, idx);
if (unlikely((u16)(vq->avail_idx - vq->last_avail_idx) > vq->num)) {
avail_idx = vhost16_to_cpu(vq, idx);
if (unlikely((u16)(avail_idx - vq->last_avail_idx) > vq->num)) {
vq_err(vq, "Invalid available index change from %u to %u",
vq->last_avail_idx, vq->avail_idx);
vq->last_avail_idx, avail_idx);
return -EINVAL;
}
/* We're done if there is nothing new */
if (vq->avail_idx == vq->last_avail_idx)
if (avail_idx == vq->avail_idx)
return 0;
vq->avail_idx = avail_idx;
/*
* We updated vq->avail_idx so we need a memory barrier between
* the index read above and the caller reading avail ring entries.
@@ -3321,18 +3324,6 @@ void vhost_set_backend_features(struct vhost_dev *dev, u64 features)
}
EXPORT_SYMBOL_GPL(vhost_set_backend_features);
static int __init vhost_init(void)
{
return 0;
}
static void __exit vhost_exit(void)
{
}
module_init(vhost_init);
module_exit(vhost_exit);
MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Michael S. Tsirkin");
+16
View File
@@ -302,6 +302,22 @@ vhost_transport_send_pkt(struct sk_buff *skb, struct net *net)
return -ENODEV;
}
/* Fast-fail if the guest hasn't enabled the RX vq yet. Queuing the packet
* and making the caller wait is pointless: even if the guest manages to init
* within the timeout, it'll immediately reply with RST, because there's no
* listener on the port yet.
*
* vhost_vq_get_backend() without vq->mutex is acceptable here: locking
* the mutex would be too expensive in this hot path, and we already have
* all the outcomes covered: if the backend becomes NULL right after the check,
* vhost_transport_do_send_pkt() will check it under the mutex anyway.
*/
if (unlikely(!data_race(vhost_vq_get_backend(&vsock->vqs[VSOCK_VQ_RX])))) {
rcu_read_unlock();
kfree_skb(skb);
return -EHOSTUNREACH;
}
if (virtio_vsock_skb_reply(skb))
atomic_inc(&vsock->queued_replies);
+9
View File
@@ -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)
+2
View File
@@ -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);
}
+2
View File
@@ -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;
}
+13 -13
View File
@@ -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;
}
}
+6 -4
View File
@@ -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;
+60 -17
View File
@@ -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);
+20 -8
View File
@@ -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;
}
+2
View File
@@ -157,11 +157,13 @@ struct virtio_admin_cmd {
* @id: the device type identification (used to match it with a driver).
* @config: the configuration ops for this device.
* @vringh_config: configuration ops for host vrings.
* @map: the map operations for mapping virtio device memory.
* @vqs: the list of virtqueues for this device.
* @features: the 64 lower features supported by both driver and device.
* @features_array: the full features space supported by both driver and
* device.
* @priv: private pointer for the driver's use.
* @vmap: the map container with transport- or device-specific metadata.
* @debugfs_dir: debugfs directory entry.
* @debugfs_filter_features: features to be filtered set by debugfs.
*/
+78
View File
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (C) 2021-2023 OpenSynergy GmbH
* Copyright Red Hat, Inc. 2025
*/
#ifndef _LINUX_VIRTIO_VIRTIO_CAN_H
#define _LINUX_VIRTIO_VIRTIO_CAN_H
#include <linux/types.h>
#include <linux/virtio_types.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
/* Feature bit numbers */
#define VIRTIO_CAN_F_CAN_CLASSIC 0
#define VIRTIO_CAN_F_CAN_FD 1
#define VIRTIO_CAN_F_RTR_FRAMES 2
#define VIRTIO_CAN_F_LATE_TX_ACK 3
/* CAN Result Types */
#define VIRTIO_CAN_RESULT_OK 0
#define VIRTIO_CAN_RESULT_NOT_OK 1
/* CAN flags to determine type of CAN Id */
#define VIRTIO_CAN_FLAGS_EXTENDED 0x8000
#define VIRTIO_CAN_FLAGS_FD 0x4000
#define VIRTIO_CAN_FLAGS_RTR 0x2000
#define VIRTIO_CAN_MAX_DLEN 64 /* this is like CANFD_MAX_DLEN */
struct virtio_can_config {
#define VIRTIO_CAN_S_CTRL_BUSOFF (1u << 0) /* Controller BusOff */
/* CAN controller status */
__le16 status;
};
/* TX queue message types */
struct virtio_can_tx_out {
#define VIRTIO_CAN_TX 0x0001
__le16 msg_type;
__le16 length; /* 0..8 CC, 0..64 CAN-FD, 0..2048 CAN-XL, 12 bits */
__u8 reserved_classic_dlc; /* If CAN classic length = 8 then DLC can be 8..15 */
__u8 padding;
__le16 reserved_xl_priority; /* May be needed for CAN XL priority */
__le32 flags;
__le32 can_id;
__u8 sdu[] __counted_by_le(length);
};
struct virtio_can_tx_in {
__u8 result;
};
/* RX queue message types */
struct virtio_can_rx {
#define VIRTIO_CAN_RX 0x0101
__le16 msg_type;
__le16 length; /* 0..8 CC, 0..64 CAN-FD, 0..2048 CAN-XL, 12 bits */
__u8 reserved_classic_dlc; /* If CAN classic length = 8 then DLC can be 8..15 */
__u8 padding;
__le16 reserved_xl_priority; /* May be needed for CAN XL priority */
__le32 flags;
__le32 can_id;
__u8 sdu[] __counted_by_le(length);
};
/* Control queue message types */
struct virtio_can_control_out {
#define VIRTIO_CAN_SET_CTRL_MODE_START 0x0201
#define VIRTIO_CAN_SET_CTRL_MODE_STOP 0x0202
__le16 msg_type;
};
struct virtio_can_control_in {
__u8 result;
};
#endif /* #ifndef _LINUX_VIRTIO_VIRTIO_CAN_H */
+1 -1
View File
@@ -44,7 +44,7 @@
#define VIRTIO_CONSOLE_BAD_ID (~(__u32)0)
struct virtio_console_config {
/* colums of the screens */
/* columns of the screens */
__virtio16 cols;
/* rows of the screens */
__virtio16 rows;
-1
View File
@@ -123,7 +123,6 @@ struct vhost_task *vhost_task_create(bool (*fn)(void *),
struct kernel_clone_args args = {
.flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM |
CLONE_THREAD | CLONE_SIGHAND,
.exit_signal = 0,
.fn = vhost_task_fn,
.name = name,
.user_worker = 1,
+2
View File
@@ -60,4 +60,6 @@ enum dma_data_direction {
*/
#define DMA_MAPPING_ERROR (~(dma_addr_t)0)
#define DMA_ATTR_CPU_CACHE_CLEAN (1UL << 11)
#endif
+1
View File
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ERR_H
#define ERR_H
#include <linux/kernel.h>
#define MAX_ERRNO 4095
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
+6
View File
@@ -65,6 +65,12 @@ static inline void *kmalloc_array(unsigned n, size_t s, gfp_t gfp)
return kmalloc(n * s, gfp);
}
#define kmalloc_obj(VAR_OR_TYPE, ...) \
((typeof(VAR_OR_TYPE) *)kmalloc(sizeof(typeof(VAR_OR_TYPE)), 0))
#define kmalloc_objs(VAR_OR_TYPE, COUNT, ...) \
((typeof(VAR_OR_TYPE) *)kmalloc(sizeof(typeof(VAR_OR_TYPE)) * (COUNT), 0))
static inline void *kzalloc(size_t s, gfp_t gfp)
{
void *p = kmalloc(s, gfp);
+5
View File
@@ -159,7 +159,12 @@ static int parallel_test(u64 features,
/* Parent and child use separate addresses, to check our mapping logic! */
host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (host_map == MAP_FAILED)
err(1, "mmap host_map");
guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (guest_map == MAP_FAILED)
err(1, "mmap guest_map");
pipe_ret = pipe(to_guest);
assert(!pipe_ret);