mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-06-21 15:43:21 +02:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
@@ -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, ¬ify))
|
||||
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, ¬ify))
|
||||
return -EINVAL;
|
||||
if (notify.addr & (PAGE_SIZE - 1))
|
||||
return -EINVAL;
|
||||
if (vma->vm_end - vma->vm_start != notify.size)
|
||||
|
||||
+7
-16
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user