summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/char/hw_random/virtio-rng.c23
-rw-r--r--drivers/char/virtio_console.c52
-rw-r--r--drivers/firmware/Kconfig2
-rw-r--r--drivers/firmware/qemu_fw_cfg.c2
-rw-r--r--drivers/net/can/Kconfig12
-rw-r--r--drivers/net/can/Makefile1
-rw-r--r--drivers/net/can/virtio_can.c1022
-rw-r--r--drivers/vdpa/ifcvf/ifcvf_main.c11
-rw-r--r--drivers/vdpa/mlx5/core/mr.c7
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa.h22
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa_main.c131
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_blk.c24
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_net.c23
-rw-r--r--drivers/vdpa/vdpa_user/iova_domain.c2
-rw-r--r--drivers/vdpa/vdpa_user/vduse_dev.c189
-rw-r--r--drivers/vhost/net.c15
-rw-r--r--drivers/vhost/vdpa.c29
-rw-r--r--drivers/vhost/vhost.c23
-rw-r--r--drivers/vhost/vsock.c16
-rw-r--r--drivers/virtio/virtio.c9
-rw-r--r--drivers/virtio/virtio_balloon.c2
-rw-r--r--drivers/virtio/virtio_mem.c2
-rw-r--r--drivers/virtio/virtio_mmio.c26
-rw-r--r--drivers/virtio/virtio_pci_common.c10
-rw-r--r--drivers/virtio/virtio_ring.c77
-rw-r--r--drivers/virtio/virtio_rtc_driver.c28
-rw-r--r--include/linux/virtio.h2
-rw-r--r--include/uapi/linux/virtio_can.h78
-rw-r--r--include/uapi/linux/virtio_console.h2
-rw-r--r--kernel/vhost_task.c1
-rw-r--r--tools/virtio/linux/dma-mapping.h2
-rw-r--r--tools/virtio/linux/err.h1
-rw-r--r--tools/virtio/linux/kernel.h6
-rw-r--r--tools/virtio/vringh_test.c5
35 files changed, 1686 insertions, 180 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index c30099d515fe..febef9aa94d2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 0ce02d7e5048..5e83ffa105e4 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -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)
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 9a33217c68d9..198b97314168 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1771,32 +1771,40 @@ static void config_intr(struct virtio_device *vdev)
schedule_work(&portdev->config_work);
}
-static void config_work_handler(struct work_struct *work)
+static void update_size_from_config(struct ports_device *portdev)
{
- struct ports_device *portdev;
+ struct virtio_device *vdev;
+ struct port *port;
+ u16 rows, cols;
- 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;
- vdev = portdev->vdev;
- virtio_cread(vdev, struct virtio_console_config, cols, &cols);
- virtio_cread(vdev, struct virtio_console_config, rows, &rows);
+ /*
+ * 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;
- port = find_port_by_id(portdev, 0);
- set_console_size(port, rows, cols);
+ virtio_cread(vdev, struct virtio_console_config, cols, &cols);
+ virtio_cread(vdev, struct virtio_console_config, rows, &rows);
- /*
- * 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);
- }
+ 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);
+ 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:
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bcfda01c2ab8..12dc70254842 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -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
diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c
index 87a5421bc7d5..0c51a9df589f 100644
--- a/drivers/firmware/qemu_fw_cfg.c
+++ b/drivers/firmware/qemu_fw_cfg.c
@@ -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
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index e15e320db476..e4058708ae68 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -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
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index d7bc10a6b8ea..4010d17f8583 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -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
diff --git a/drivers/net/can/virtio_can.c b/drivers/net/can/virtio_can.c
new file mode 100644
index 000000000000..f67d0bf09681
--- /dev/null
+++ b/drivers/net/can/virtio_can.c
@@ -0,0 +1,1022 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CAN bus driver for the Virtio CAN controller
+ *
+ * Copyright (C) 2021-2023 OpenSynergy GmbH
+ * Copyright Red Hat, Inc. 2025
+ */
+
+#include <linux/atomic.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/stddef.h>
+#include <linux/can/dev.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_can.h>
+
+/* CAN device queues */
+#define VIRTIO_CAN_QUEUE_TX 0
+#define VIRTIO_CAN_QUEUE_RX 1
+#define VIRTIO_CAN_QUEUE_CONTROL 2
+#define VIRTIO_CAN_QUEUE_COUNT 3
+
+#define CAN_KNOWN_FLAGS \
+ (VIRTIO_CAN_FLAGS_EXTENDED |\
+ VIRTIO_CAN_FLAGS_FD |\
+ VIRTIO_CAN_FLAGS_RTR)
+
+/* Max. number of in flight TX messages */
+#define VIRTIO_CAN_ECHO_SKB_MAX 128
+
+struct virtio_can_tx {
+ unsigned int putidx;
+ struct virtio_can_tx_in tx_in;
+ /* Keep virtio_can_tx_out at the end of the structure due to flex array */
+ struct virtio_can_tx_out tx_out;
+};
+
+struct virtio_can_control {
+ struct virtio_can_control_out cpkt_out;
+ struct virtio_can_control_in cpkt_in;
+};
+
+/* virtio_can private data structure */
+struct virtio_can_priv {
+ struct can_priv can; /* must be the first member */
+ /* NAPI for RX messages */
+ struct napi_struct napi;
+ /* NAPI for TX messages */
+ struct napi_struct napi_tx;
+ /* The network device we're associated with */
+ struct net_device *dev;
+ /* The virtio device we're associated with */
+ struct virtio_device *vdev;
+ /* The virtqueues */
+ struct virtqueue *vqs[VIRTIO_CAN_QUEUE_COUNT];
+ /* Lock for TX operations */
+ spinlock_t tx_lock;
+ /* Control queue lock */
+ struct mutex ctrl_lock;
+ /* Wait for control queue processing without polling */
+ struct completion ctrl_done;
+ /* Array of receive queue messages */
+ struct virtio_can_rx *rpkt;
+ struct virtio_can_control can_ctr_msg;
+ /* Data to get and maintain the putidx for local TX echo */
+ struct ida tx_putidx_ida;
+ /* In flight TX messages */
+ atomic_t tx_inflight;
+ /* Packet length */
+ int rpkt_len;
+ /* BusOff pending. Reset after successful indication to upper layer */
+ bool busoff_pending;
+ /* Tracks whether NAPI instances are currently enabled */
+ bool napi_active;
+};
+
+static void virtqueue_napi_schedule(struct napi_struct *napi,
+ struct virtqueue *vq)
+{
+ if (napi_schedule_prep(napi)) {
+ virtqueue_disable_cb(vq);
+ __napi_schedule(napi);
+ }
+}
+
+static void virtqueue_napi_complete(struct napi_struct *napi,
+ struct virtqueue *vq, int processed)
+{
+ int opaque;
+
+ opaque = virtqueue_enable_cb_prepare(vq);
+ if (napi_complete_done(napi, processed)) {
+ if (unlikely(virtqueue_poll(vq, opaque)))
+ virtqueue_napi_schedule(napi, vq);
+ } else {
+ virtqueue_disable_cb(vq);
+ }
+}
+
+static void virtio_can_free_candev(struct net_device *ndev)
+{
+ struct virtio_can_priv *priv = netdev_priv(ndev);
+
+ ida_destroy(&priv->tx_putidx_ida);
+ free_candev(ndev);
+}
+
+static void virtio_can_napi_enable(struct virtio_can_priv *priv)
+{
+ if (!priv->napi_active) {
+ napi_enable(&priv->napi);
+ napi_enable(&priv->napi_tx);
+ priv->napi_active = true;
+ }
+}
+
+static void virtio_can_napi_disable(struct virtio_can_priv *priv)
+{
+ if (priv->napi_active) {
+ napi_disable(&priv->napi_tx);
+ napi_disable(&priv->napi);
+ priv->napi_active = false;
+ }
+}
+
+static int virtio_can_alloc_tx_idx(struct virtio_can_priv *priv)
+{
+ int tx_idx;
+
+ tx_idx = ida_alloc_max(&priv->tx_putidx_ida,
+ priv->can.echo_skb_max - 1, GFP_ATOMIC);
+ if (tx_idx >= 0)
+ atomic_inc(&priv->tx_inflight);
+
+ return tx_idx;
+}
+
+static void virtio_can_free_tx_idx(struct virtio_can_priv *priv,
+ unsigned int idx)
+{
+ ida_free(&priv->tx_putidx_ida, idx);
+ atomic_dec(&priv->tx_inflight);
+}
+
+/* Create a scatter-gather list representing our input buffer and put
+ * it in the queue.
+ *
+ * Callers should take appropriate locks.
+ */
+static int virtio_can_add_inbuf(struct virtqueue *vq, void *buf,
+ unsigned int size)
+{
+ struct scatterlist sg[1];
+ int ret;
+
+ sg_init_one(sg, buf, size);
+
+ ret = virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC);
+
+ return ret;
+}
+
+/* Send a control message with message type either
+ *
+ * - VIRTIO_CAN_SET_CTRL_MODE_START or
+ * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
+ *
+ */
+static u8 virtio_can_send_ctrl_msg(struct net_device *ndev, u16 msg_type)
+{
+ struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
+ struct virtio_can_priv *priv = netdev_priv(ndev);
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
+ struct device *dev = &priv->vdev->dev;
+ unsigned int len;
+ int err;
+
+ if (!vq)
+ return VIRTIO_CAN_RESULT_NOT_OK;
+
+ guard(mutex)(&priv->ctrl_lock);
+
+ priv->can_ctr_msg.cpkt_out.msg_type = cpu_to_le16(msg_type);
+ sg_init_one(&sg_out, &priv->can_ctr_msg.cpkt_out,
+ sizeof(priv->can_ctr_msg.cpkt_out));
+ sg_init_one(&sg_in, &priv->can_ctr_msg.cpkt_in, sizeof(priv->can_ctr_msg.cpkt_in));
+
+ reinit_completion(&priv->ctrl_done);
+
+ err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
+ if (err != 0) {
+ dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
+ return VIRTIO_CAN_RESULT_NOT_OK;
+ }
+
+ if (!virtqueue_kick(vq)) {
+ dev_err(dev, "%s(): Kick failed\n", __func__);
+ return VIRTIO_CAN_RESULT_NOT_OK;
+ }
+
+ while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
+ wait_for_completion(&priv->ctrl_done);
+
+ return priv->can_ctr_msg.cpkt_in.result;
+}
+
+static int virtio_can_start(struct net_device *ndev)
+{
+ struct virtio_can_priv *priv = netdev_priv(ndev);
+ u8 result;
+
+ result = virtio_can_send_ctrl_msg(ndev, VIRTIO_CAN_SET_CTRL_MODE_START);
+ if (result != VIRTIO_CAN_RESULT_OK) {
+ netdev_err(ndev, "CAN controller start failed\n");
+ return -EIO;
+ }
+
+ priv->busoff_pending = false;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+}
+
+static int virtio_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = virtio_can_start(dev);
+ if (err)
+ return err;
+ netif_wake_queue(dev);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int virtio_can_open(struct net_device *ndev)
+{
+ struct virtio_can_priv *priv = netdev_priv(ndev);
+ int err;
+
+ err = open_candev(ndev);
+ if (err)
+ return err;
+
+ err = virtio_can_start(ndev);
+ if (err) {
+ close_candev(ndev);
+ return err;
+ }
+
+ virtio_can_napi_enable(priv);
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+static int virtio_can_stop(struct net_device *ndev)
+{
+ struct virtio_can_priv *priv = netdev_priv(ndev);
+ struct device *dev = &priv->vdev->dev;
+ u8 result;
+
+ result = virtio_can_send_ctrl_msg(ndev, VIRTIO_CAN_SET_CTRL_MODE_STOP);
+ if (result != VIRTIO_CAN_RESULT_OK) {
+ dev_err(dev, "CAN controller stop failed\n");
+ return -EIO;
+ }
+
+ priv->busoff_pending = false;
+ priv->can.state = CAN_STATE_STOPPED;
+
+ /* Switch carrier off if device was connected to the bus */
+ if (netif_carrier_ok(ndev))
+ netif_carrier_off(ndev);
+
+ return 0;
+}
+
+static int virtio_can_close(struct net_device *dev)
+{
+ struct virtio_can_priv *priv = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ /* Ignore stop error: ndo_stop must always complete cleanup regardless.
+ * virtio_can_stop() already logs the error if it fails.
+ */
+ virtio_can_stop(dev);
+ virtio_can_napi_disable(priv);
+ close_candev(dev);
+
+ return 0;
+}
+
+static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
+ const unsigned int hdr_size = sizeof(struct virtio_can_tx_out);
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ struct virtio_can_priv *priv = netdev_priv(dev);
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+ netdev_tx_t xmit_ret = NETDEV_TX_OK;
+ struct virtio_can_tx *can_tx_msg;
+ u32 can_flags;
+ int putidx;
+ int err;
+
+ if (can_dev_dropped_skb(dev, skb))
+ goto kick; /* No way to return NET_XMIT_DROP here */
+
+ /* No local check for CAN_RTR_FLAG or FD frame against negotiated
+ * features. The device will reject those anyway if not supported.
+ */
+
+ can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
+ if (!can_tx_msg) {
+ kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ goto kick; /* No way to return NET_XMIT_DROP here */
+ }
+
+ can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
+ can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
+ can_flags = 0;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ can_flags |= VIRTIO_CAN_FLAGS_EXTENDED;
+ can_tx_msg->tx_out.can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK);
+ } else {
+ can_tx_msg->tx_out.can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK);
+ }
+ if (cf->can_id & CAN_RTR_FLAG)
+ can_flags |= VIRTIO_CAN_FLAGS_RTR;
+ else
+ memcpy(can_tx_msg->tx_out.sdu, cf->data, cf->len);
+ if (can_is_canfd_skb(skb))
+ can_flags |= VIRTIO_CAN_FLAGS_FD;
+
+ can_tx_msg->tx_out.flags = cpu_to_le32(can_flags);
+
+ sg_init_one(&sg_out, &can_tx_msg->tx_out, hdr_size + cf->len);
+ sg_init_one(&sg_in, &can_tx_msg->tx_in, sizeof(can_tx_msg->tx_in));
+
+ putidx = virtio_can_alloc_tx_idx(priv);
+
+ if (unlikely(putidx < 0)) {
+ /* -ENOMEM or -ENOSPC here. -ENOSPC should not be possible as
+ * tx_inflight >= can.echo_skb_max is checked in flow control
+ */
+ WARN_ON_ONCE(putidx == -ENOSPC);
+ kfree(can_tx_msg);
+ kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ goto kick; /* No way to return NET_XMIT_DROP here */
+ }
+
+ can_tx_msg->putidx = (unsigned int)putidx;
+
+ /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
+ err = can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
+ if (unlikely(err)) {
+ /* skb was already freed by can_put_echo_skb() on error */
+ virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
+ kfree(can_tx_msg);
+ dev->stats.tx_dropped++;
+ goto kick;
+ }
+
+ /* Protect queue and list operations */
+ scoped_guard(spinlock_irqsave, &priv->tx_lock)
+ err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
+
+ if (unlikely(err)) {
+ /*
+ * can_put_echo_skb() already consumed skb via consume_skb(),
+ * so returning NETDEV_TX_BUSY would cause the stack to requeue
+ * a freed pointer. Drop the frame and return OK instead.
+ */
+ can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
+ virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
+ netif_stop_queue(dev);
+ kfree(can_tx_msg);
+ dev->stats.tx_dropped++;
+ /* Expected never to be seen */
+ netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
+ goto kick;
+ }
+
+ /* Normal flow control: stop queue when no transmission slots left */
+ if (atomic_read(&priv->tx_inflight) >= priv->can.echo_skb_max ||
+ vq->num_free == 0 || (vq->num_free < ARRAY_SIZE(sgs) &&
+ !virtio_has_feature(vq->vdev, VIRTIO_RING_F_INDIRECT_DESC))) {
+ netif_stop_queue(dev);
+ netdev_dbg(dev, "TX: Normal stop queue\n");
+ }
+
+kick:
+ if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
+ scoped_guard(spinlock_irqsave, &priv->tx_lock) {
+ if (!virtqueue_kick(vq))
+ netdev_err(dev, "%s(): Kick failed\n", __func__);
+ }
+ }
+
+ return xmit_ret;
+}
+
+static const struct net_device_ops virtio_can_netdev_ops = {
+ .ndo_open = virtio_can_open,
+ .ndo_stop = virtio_can_close,
+ .ndo_start_xmit = virtio_can_start_xmit,
+};
+
+static int register_virtio_can_dev(struct net_device *dev)
+{
+ dev->flags |= IFF_ECHO; /* we support local echo */
+ dev->netdev_ops = &virtio_can_netdev_ops;
+
+ return register_candev(dev);
+}
+
+static int virtio_can_read_tx_queue(struct virtqueue *vq)
+{
+ struct virtio_can_priv *can_priv = vq->vdev->priv;
+ struct net_device *dev = can_priv->dev;
+ struct virtio_can_tx *can_tx_msg;
+ struct net_device_stats *stats;
+ unsigned int len;
+ u8 result;
+
+ stats = &dev->stats;
+
+ scoped_guard(spinlock_irqsave, &can_priv->tx_lock)
+ can_tx_msg = virtqueue_get_buf(vq, &len);
+
+ if (!can_tx_msg)
+ return 0;
+
+ if (unlikely(len < sizeof(struct virtio_can_tx_in))) {
+ netdev_err(dev, "TX ACK: Device sent no result code\n");
+ result = VIRTIO_CAN_RESULT_NOT_OK; /* Keep things going */
+ } else {
+ result = can_tx_msg->tx_in.result;
+ }
+
+ if (can_priv->can.state < CAN_STATE_BUS_OFF) {
+ if (result != VIRTIO_CAN_RESULT_OK) {
+ struct can_frame *skb_cf;
+ struct sk_buff *skb = alloc_can_err_skb(dev, &skb_cf);
+
+ if (skb) {
+ skb_cf->can_id |= CAN_ERR_CRTL;
+ skb_cf->data[1] |= CAN_ERR_CRTL_UNSPEC;
+ netif_rx(skb);
+ }
+ netdev_warn(dev, "TX ACK: Result = %u\n", result);
+ can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
+ stats->tx_dropped++;
+ } else {
+ stats->tx_bytes += can_get_echo_skb(dev, can_tx_msg->putidx,
+ NULL);
+ stats->tx_packets++;
+ }
+ } else {
+ netdev_dbg(dev, "TX ACK: Controller inactive, drop echo\n");
+ can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
+ stats->tx_dropped++;
+ }
+
+ virtio_can_free_tx_idx(can_priv, can_tx_msg->putidx);
+
+ /* Flow control */
+ if (netif_queue_stopped(dev)) {
+ netdev_dbg(dev, "TX ACK: Wake up stopped queue\n");
+ netif_wake_queue(dev);
+ }
+
+ kfree(can_tx_msg);
+
+ return 1; /* Queue was not empty so there may be more data */
+}
+
+static int virtio_can_tx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *dev = napi->dev;
+ struct virtio_can_priv *priv = netdev_priv(dev);
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+ int work_done = 0;
+
+ while (work_done < quota && virtio_can_read_tx_queue(vq) != 0)
+ work_done++;
+
+ if (work_done < quota)
+ virtqueue_napi_complete(napi, vq, work_done);
+
+ return work_done;
+}
+
+static void virtio_can_tx_intr(struct virtqueue *vq)
+{
+ struct virtio_can_priv *can_priv = vq->vdev->priv;
+
+ virtqueue_disable_cb(vq);
+ napi_schedule(&can_priv->napi_tx);
+}
+
+/* This function is the NAPI RX poll function and NAPI guarantees that this
+ * function is not invoked simultaneously on multiple processors.
+ * Read a RX message from the used queue and sends it to the upper layer.
+ */
+static int virtio_can_read_rx_queue(struct virtqueue *vq)
+{
+ const unsigned int header_size = sizeof(struct virtio_can_rx);
+ struct virtio_can_priv *priv = vq->vdev->priv;
+ struct net_device *dev = priv->dev;
+ struct net_device_stats *stats;
+ struct virtio_can_rx *can_rx;
+ unsigned int transport_len;
+ struct canfd_frame *cf;
+ struct sk_buff *skb;
+ unsigned int len;
+ u32 can_flags;
+ u16 msg_type;
+ u32 can_id;
+ int ret;
+
+ stats = &dev->stats;
+
+ can_rx = virtqueue_get_buf(vq, &transport_len);
+ if (!can_rx)
+ return 0; /* No more data */
+
+ if (transport_len < header_size) {
+ netdev_warn(dev, "RX: Message too small\n");
+ goto putback;
+ }
+
+ if (priv->can.state >= CAN_STATE_ERROR_PASSIVE) {
+ netdev_dbg(dev, "%s(): Controller not active\n", __func__);
+ goto putback;
+ }
+
+ msg_type = le16_to_cpu(can_rx->msg_type);
+ if (msg_type != VIRTIO_CAN_RX) {
+ netdev_warn(dev, "RX: Got unknown msg_type %04x\n", msg_type);
+ goto putback;
+ }
+
+ len = le16_to_cpu(can_rx->length);
+ can_flags = le32_to_cpu(can_rx->flags);
+ can_id = le32_to_cpu(can_rx->can_id);
+
+ if (can_flags & ~CAN_KNOWN_FLAGS) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: Invalid flags 0x%x\n",
+ can_id, can_flags);
+ goto putback;
+ }
+
+ if (can_flags & VIRTIO_CAN_FLAGS_EXTENDED) {
+ can_id &= CAN_EFF_MASK;
+ can_id |= CAN_EFF_FLAG;
+ } else {
+ can_id &= CAN_SFF_MASK;
+ }
+
+ if (can_flags & VIRTIO_CAN_FLAGS_RTR) {
+ if (!virtio_has_feature(vq->vdev, VIRTIO_CAN_F_RTR_FRAMES)) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: RTR not negotiated\n",
+ can_id);
+ goto putback;
+ }
+ if (can_flags & VIRTIO_CAN_FLAGS_FD) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: RTR with FD not possible\n",
+ can_id);
+ goto putback;
+ }
+
+ if (len > 0xF) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: RTR with DLC > 0xF\n",
+ can_id);
+ goto putback;
+ }
+
+ if (len > 0x8)
+ len = 0x8;
+
+ can_id |= CAN_RTR_FLAG;
+ }
+
+ if (transport_len < header_size + len) {
+ netdev_warn(dev, "RX: Message too small for payload\n");
+ goto putback;
+ }
+
+ if (can_flags & VIRTIO_CAN_FLAGS_FD) {
+ if (!virtio_has_feature(vq->vdev, VIRTIO_CAN_F_CAN_FD)) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: FD not negotiated\n",
+ can_id);
+ goto putback;
+ }
+
+ if (len > CANFD_MAX_DLEN)
+ len = CANFD_MAX_DLEN;
+
+ skb = alloc_canfd_skb(priv->dev, &cf);
+ } else {
+ if (!virtio_has_feature(vq->vdev, VIRTIO_CAN_F_CAN_CLASSIC)) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: CAN Id 0x%08x: classic not negotiated\n",
+ can_id);
+ goto putback;
+ }
+
+ if (len > CAN_MAX_DLEN)
+ len = CAN_MAX_DLEN;
+
+ skb = alloc_can_skb(priv->dev, (struct can_frame **)&cf);
+ }
+ if (!skb) {
+ stats->rx_dropped++;
+ netdev_warn(dev, "RX: No skb available\n");
+ goto putback;
+ }
+
+ cf->can_id = can_id;
+ cf->len = len;
+ if (!(can_flags & VIRTIO_CAN_FLAGS_RTR)) {
+ /* RTR frames have a DLC but no payload */
+ memcpy(cf->data, can_rx->sdu, len);
+ }
+
+ if (netif_receive_skb(skb) == NET_RX_SUCCESS) {
+ stats->rx_packets++;
+ if (!(can_flags & VIRTIO_CAN_FLAGS_RTR))
+ stats->rx_bytes += len;
+ }
+
+putback:
+ /* Put processed RX buffer back into avail queue */
+ ret = virtio_can_add_inbuf(vq, can_rx,
+ priv->rpkt_len);
+ if (!ret)
+ virtqueue_kick(vq);
+ return 1; /* Queue was not empty so there may be more data */
+}
+
+static int virtio_can_handle_busoff(struct net_device *dev)
+{
+ struct virtio_can_priv *priv = netdev_priv(dev);
+ struct can_frame *cf;
+ struct sk_buff *skb;
+
+ if (!priv->busoff_pending)
+ return 0;
+
+ if (priv->can.state < CAN_STATE_BUS_OFF) {
+ netdev_dbg(dev, "entered error bus off state\n");
+
+ /* bus-off state */
+ priv->can.state = CAN_STATE_BUS_OFF;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(dev);
+ }
+
+ /* propagate the error condition to the CAN stack */
+ skb = alloc_can_err_skb(dev, &cf);
+ if (unlikely(!skb))
+ return 0;
+
+ /* bus-off state */
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ /* Ensure that the BusOff indication does not get lost */
+ if (netif_receive_skb(skb) == NET_RX_SUCCESS)
+ priv->busoff_pending = false;
+
+ return 1;
+}
+
+static int virtio_can_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *dev = napi->dev;
+ struct virtio_can_priv *priv = netdev_priv(dev);
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
+ int work_done = 0;
+
+ work_done += virtio_can_handle_busoff(dev);
+
+ while (work_done < quota && virtio_can_read_rx_queue(vq) != 0)
+ work_done++;
+
+ if (work_done < quota)
+ virtqueue_napi_complete(napi, vq, work_done);
+
+ return work_done;
+}
+
+static void virtio_can_rx_intr(struct virtqueue *vq)
+{
+ struct virtio_can_priv *can_priv = vq->vdev->priv;
+
+ virtqueue_disable_cb(vq);
+ napi_schedule(&can_priv->napi);
+}
+
+static void virtio_can_control_intr(struct virtqueue *vq)
+{
+ struct virtio_can_priv *can_priv = vq->vdev->priv;
+
+ complete(&can_priv->ctrl_done);
+}
+
+static void virtio_can_config_changed(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *can_priv = vdev->priv;
+ u16 status;
+
+ status = virtio_cread16(vdev, offsetof(struct virtio_can_config,
+ status));
+
+ if (!(status & VIRTIO_CAN_S_CTRL_BUSOFF))
+ return;
+
+ if (!can_priv->busoff_pending &&
+ can_priv->can.state < CAN_STATE_BUS_OFF) {
+ can_priv->busoff_pending = true;
+ napi_schedule(&can_priv->napi);
+ }
+}
+
+static void virtio_can_populate_rx_vq(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
+ unsigned int buf_size = priv->rpkt_len;
+ int num_elements = vq->num_free;
+ u8 *buf = (u8 *)priv->rpkt;
+ unsigned int idx;
+ int ret = 0;
+
+ for (idx = 0; idx < num_elements; idx++) {
+ ret = virtio_can_add_inbuf(vq, buf, buf_size);
+ if (ret < 0) {
+ dev_dbg(&vdev->dev, "rpkt fill: ret=%d, idx=%u, size=%u\n",
+ ret, idx, buf_size);
+ break;
+ }
+ buf += buf_size;
+ }
+
+ if (idx > 0)
+ virtqueue_kick(vq);
+
+ dev_dbg(&vdev->dev, "%u rpkt added\n", idx);
+}
+
+static int virtio_can_find_vqs(struct virtio_can_priv *priv)
+{
+ struct virtqueue_info vqs_info[] = {
+ { "can-tx", virtio_can_tx_intr },
+ { "can-rx", virtio_can_rx_intr },
+ { "can-state-ctrl", virtio_can_control_intr },
+ };
+
+ /* Find the queues. */
+ return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
+ vqs_info, NULL);
+}
+
+/* Function must not be called before virtio_can_find_vqs() has been run */
+static void virtio_can_del_vq(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct virtqueue *vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+ struct virtio_can_tx *can_tx_msg;
+ int q;
+
+ if (!vq)
+ return;
+
+ /* Reset the device */
+ virtio_reset_device(vdev);
+
+ /* From here we have dead silence from the device side so no locks
+ * are needed to protect against device side events.
+ */
+
+ /* Free pending TX buffers which were allocated in virtio_can_start_xmit() */
+ while ((can_tx_msg = virtqueue_detach_unused_buf(vq))) {
+ can_free_echo_skb(priv->dev, can_tx_msg->putidx, NULL);
+ virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
+ kfree(can_tx_msg);
+ }
+
+ /* RX and control queue buffers are managed elsewhere, just detach */
+ for (q = VIRTIO_CAN_QUEUE_RX; q < VIRTIO_CAN_QUEUE_COUNT; q++)
+ while (virtqueue_detach_unused_buf(priv->vqs[q]))
+ ;
+
+ if (vdev->config->del_vqs)
+ vdev->config->del_vqs(vdev);
+
+ memset(priv->vqs, 0, sizeof(priv->vqs));
+}
+
+static void virtio_can_remove(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct net_device *dev = priv->dev;
+
+ unregister_candev(dev);
+
+ virtio_can_del_vq(vdev);
+
+ virtio_can_free_candev(dev);
+}
+
+static int virtio_can_validate(struct virtio_device *vdev)
+{
+ /* CAN needs always access to the config space.
+ * Check that the driver can access the config space
+ */
+ if (!vdev->config->get) {
+ dev_err(&vdev->dev, "%s failure: config access disabled\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ dev_err(&vdev->dev,
+ "device does not comply with spec version 1.x\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int virtio_can_probe(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv;
+ struct net_device *dev;
+ size_t size;
+ int err;
+
+ dev = alloc_candev(sizeof(struct virtio_can_priv),
+ VIRTIO_CAN_ECHO_SKB_MAX);
+ if (!dev)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+
+ ida_init(&priv->tx_putidx_ida);
+
+ netif_napi_add(dev, &priv->napi, virtio_can_rx_poll);
+ netif_napi_add(dev, &priv->napi_tx, virtio_can_tx_poll);
+
+ SET_NETDEV_DEV(dev, &vdev->dev);
+
+ priv->dev = dev;
+ priv->vdev = vdev;
+ vdev->priv = priv;
+
+ priv->can.do_set_mode = virtio_can_set_mode;
+ priv->can.bittiming.bitrate = CAN_BITRATE_UNKNOWN;
+ /* Set Virtio CAN supported operations */
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
+ if (virtio_has_feature(vdev, VIRTIO_CAN_F_CAN_FD)) {
+ priv->can.fd.data_bittiming.bitrate = CAN_BITRATE_UNKNOWN;
+ err = can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD);
+ if (err != 0)
+ goto on_failure;
+ }
+
+ /* Initialize virtqueues */
+ err = virtio_can_find_vqs(priv);
+ if (err != 0)
+ goto on_failure;
+
+ spin_lock_init(&priv->tx_lock);
+ mutex_init(&priv->ctrl_lock);
+
+ init_completion(&priv->ctrl_done);
+
+ priv->rpkt_len = sizeof(struct virtio_can_rx);
+
+ if (virtio_has_feature(vdev, VIRTIO_CAN_F_CAN_FD))
+ priv->rpkt_len += CANFD_MAX_DLEN;
+ else
+ priv->rpkt_len += CAN_MAX_DLEN;
+
+ size = priv->rpkt_len * priv->vqs[VIRTIO_CAN_QUEUE_RX]->num_free;
+ priv->rpkt = devm_kzalloc(&vdev->dev, size, GFP_KERNEL);
+ if (!priv->rpkt) {
+ virtio_can_del_vq(vdev);
+ err = -ENOMEM;
+ goto on_failure;
+ }
+ virtio_can_populate_rx_vq(vdev);
+
+ err = register_virtio_can_dev(dev);
+ if (err) {
+ virtio_can_del_vq(vdev);
+ goto on_failure;
+ }
+
+ return 0;
+
+on_failure:
+ virtio_can_free_candev(dev);
+ return err;
+}
+
+static int __maybe_unused virtio_can_freeze(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct net_device *ndev = priv->dev;
+
+ if (netif_running(ndev)) {
+ /* virtio_can_close() calls netif_stop_queue(), virtio_can_stop(),
+ * napi_disable() and close_candev(). Call it directly (not via
+ * dev_close()) to preserve IFF_UP so that netif_running() returns
+ * true in virtio_can_restore() and the device is brought back up.
+ */
+ virtio_can_close(ndev);
+ netif_device_detach(ndev);
+ }
+
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ virtio_can_del_vq(vdev);
+
+ return 0;
+}
+
+static int __maybe_unused virtio_can_restore(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct net_device *ndev = priv->dev;
+ size_t size;
+ int err;
+
+ err = virtio_can_find_vqs(priv);
+ if (err != 0)
+ return err;
+
+ size = priv->rpkt_len * priv->vqs[VIRTIO_CAN_QUEUE_RX]->num_free;
+ priv->rpkt = devm_krealloc(&vdev->dev, priv->rpkt, size, GFP_KERNEL | __GFP_ZERO);
+ if (!priv->rpkt) {
+ virtio_can_del_vq(vdev);
+ return -ENOMEM;
+ }
+ virtio_can_populate_rx_vq(vdev);
+
+ if (netif_running(ndev)) {
+ /* virtio_can_open() calls open_candev(), virtio_can_start(),
+ * napi_enable() and netif_start_queue(). Call it directly (not
+ * via dev_open()) since IFF_UP is still set from before freeze.
+ */
+ err = virtio_can_open(ndev);
+ if (err) {
+ virtio_can_del_vq(vdev);
+ return err;
+ }
+ netif_device_attach(ndev);
+ } else {
+ priv->can.state = CAN_STATE_STOPPED;
+ }
+
+ return 0;
+}
+
+static struct virtio_device_id virtio_can_id_table[] = {
+ { VIRTIO_ID_CAN, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+ VIRTIO_CAN_F_CAN_CLASSIC,
+ VIRTIO_CAN_F_CAN_FD,
+ VIRTIO_CAN_F_LATE_TX_ACK,
+ VIRTIO_CAN_F_RTR_FRAMES,
+};
+
+static struct virtio_driver virtio_can_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = virtio_can_id_table,
+ .validate = virtio_can_validate,
+ .probe = virtio_can_probe,
+ .remove = virtio_can_remove,
+ .config_changed = virtio_can_config_changed,
+#ifdef CONFIG_PM_SLEEP
+ .freeze = virtio_can_freeze,
+ .restore = virtio_can_restore,
+#endif
+};
+
+module_virtio_driver(virtio_can_driver);
+MODULE_DEVICE_TABLE(virtio, virtio_can_id_table);
+
+MODULE_AUTHOR("OpenSynergy GmbH");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CAN bus driver for Virtio CAN controller");
diff --git a/drivers/vdpa/ifcvf/ifcvf_main.c b/drivers/vdpa/ifcvf/ifcvf_main.c
index d46c1606c97a..ab6d6ab3b3d8 100644
--- a/drivers/vdpa/ifcvf/ifcvf_main.c
+++ b/drivers/vdpa/ifcvf/ifcvf_main.c
@@ -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)
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index 42c2705077a6..6d02ccf9eb91 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -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;
diff --git a/drivers/vdpa/octeon_ep/octep_vdpa.h b/drivers/vdpa/octeon_ep/octep_vdpa.h
index 53b020b019f7..a67bf50e4075 100644
--- a/drivers/vdpa/octeon_ep/octep_vdpa.h
+++ b/drivers/vdpa/octeon_ep/octep_vdpa.h
@@ -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;
diff --git a/drivers/vdpa/octeon_ep/octep_vdpa_main.c b/drivers/vdpa/octeon_ep/octep_vdpa_main.c
index 31a02e7fd7f2..5b35993750f5 100644
--- a/drivers/vdpa/octeon_ep/octep_vdpa_main.c
+++ b/drivers/vdpa/octeon_ep/octep_vdpa_main.c
@@ -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;
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
index b137f3679343..f70f454dde8e 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
@@ -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)
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
index 6caf09a1907b..29fd14ce5860 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
@@ -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);
diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c
index 806cec32c4bc..4dc76c0d0d13 100644
--- a/drivers/vdpa/vdpa_user/iova_domain.c
+++ b/drivers/vdpa/vdpa_user/iova_domain.c
@@ -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))
diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c
index 6202f6902fcd..f15ad425e01f 100644
--- a/drivers/vdpa/vdpa_user/vduse_dev.c
+++ b/drivers/vdpa/vdpa_user/vduse_dev.c
@@ -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;
+ spin_unlock(&dev->msg_lock);
- ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
- goto unlock;
+ return -EAGAIN;
- spin_unlock(&dev->msg_lock);
ret = wait_event_interruptible_exclusive(dev->waitq,
!list_empty(&dev->send_list));
if (ret)
@@ -382,17 +388,34 @@ static ssize_t vduse_dev_read_iter(struct kiocb *iocb, struct iov_iter *to)
spin_lock(&dev->msg_lock);
}
+
+ 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);
spin_unlock(&dev->msg_lock);
- ret = copy_to_iter(&msg->req, size, to);
- spin_lock(&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;
- vduse_enqueue_msg(&dev->send_list, msg);
- goto unlock;
}
- vduse_enqueue_msg(&dev->recv_list, msg);
-unlock:
- spin_unlock(&dev->msg_lock);
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,
};
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index db341c922673..77b59f49bddb 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -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;
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index 692564b1bcbb..ac55275fa0d0 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1482,16 +1482,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
}
#ifdef CONFIG_MMU
-static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
+static int
+vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index,
+ struct vdpa_notification_area *notify)
{
- struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
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_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)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 2f2c45d20883..4c525b3e16ea 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -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");
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index 1d8ec6bed53e..9aaab6bb8061 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -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);
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 5bdc6b82b30b..299fa83be1d5 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -435,6 +435,14 @@ static void virtio_dev_shutdown(struct device *_d)
dev->config->reset(dev);
}
+static int virtio_dev_num_vf(struct device *dev)
+{
+ struct virtio_device *vdev = dev_to_virtio(dev);
+
+ return dev_num_vf(vdev->dev.parent);
+}
+
+
static const struct bus_type virtio_bus = {
.name = "virtio",
.match = virtio_dev_match,
@@ -444,6 +452,7 @@ static const struct bus_type virtio_bus = {
.remove = virtio_dev_remove,
.irq_get_affinity = virtio_irq_get_affinity,
.shutdown = virtio_dev_shutdown,
+ .num_vf = virtio_dev_num_vf,
};
int __register_virtio_driver(struct virtio_driver *driver, struct module *owner)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f6c2dff33f8a..088b3a0e6ce6 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1075,6 +1075,7 @@ out_del_balloon_wq:
out_del_vqs:
vdev->config->del_vqs(vdev);
out_free_vb:
+ mutex_destroy(&vb->balloon_lock);
kfree(vb);
out:
return err;
@@ -1119,6 +1120,7 @@ static void virtballoon_remove(struct virtio_device *vdev)
}
remove_common(vb);
+ mutex_destroy(&vb->balloon_lock);
kfree(vb);
}
diff --git a/drivers/virtio/virtio_mem.c b/drivers/virtio/virtio_mem.c
index 48051e9e98ab..11c441501582 100644
--- a/drivers/virtio/virtio_mem.c
+++ b/drivers/virtio/virtio_mem.c
@@ -2975,6 +2975,7 @@ static int virtio_mem_probe(struct virtio_device *vdev)
out_del_vq:
vdev->config->del_vqs(vdev);
out_free_vm:
+ mutex_destroy(&vm->hotplug_mutex);
kfree(vm);
vdev->priv = NULL;
@@ -3067,6 +3068,7 @@ static void virtio_mem_remove(struct virtio_device *vdev)
virtio_reset_device(vdev);
vdev->config->del_vqs(vdev);
+ mutex_destroy(&vm->hotplug_mutex);
kfree(vm);
vdev->priv = NULL;
}
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 595c2274fbb5..510b7c4efdff 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -662,9 +662,7 @@ static void virtio_mmio_remove(struct platform_device *pdev)
#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES)
-static struct device vm_cmdline_parent = {
- .init_name = "virtio-mmio-cmdline",
-};
+static struct device *vm_cmdline_parent;
static int vm_cmdline_parent_registered;
static int vm_cmdline_id;
@@ -672,7 +670,6 @@ static int vm_cmdline_id;
static int vm_cmdline_set(const char *device,
const struct kernel_param *kp)
{
- int err;
struct resource resources[2] = {};
char *str;
long long base, size;
@@ -704,11 +701,10 @@ static int vm_cmdline_set(const char *device,
resources[1].start = resources[1].end = irq;
if (!vm_cmdline_parent_registered) {
- err = device_register(&vm_cmdline_parent);
- if (err) {
- put_device(&vm_cmdline_parent);
+ vm_cmdline_parent = __root_device_register("virtio-mmio-cmdline", NULL);
+ if (IS_ERR(vm_cmdline_parent)) {
pr_err("Failed to register parent device!\n");
- return err;
+ return PTR_ERR(vm_cmdline_parent);
}
vm_cmdline_parent_registered = 1;
}
@@ -719,7 +715,7 @@ static int vm_cmdline_set(const char *device,
(unsigned long long)resources[0].end,
(int)resources[1].start);
- pdev = platform_device_register_resndata(&vm_cmdline_parent,
+ pdev = platform_device_register_resndata(vm_cmdline_parent,
"virtio-mmio", vm_cmdline_id++,
resources, ARRAY_SIZE(resources), NULL, 0);
@@ -743,8 +739,12 @@ static int vm_cmdline_get_device(struct device *dev, void *data)
static int vm_cmdline_get(char *buffer, const struct kernel_param *kp)
{
buffer[0] = '\0';
- device_for_each_child(&vm_cmdline_parent, buffer,
- vm_cmdline_get_device);
+
+ if (vm_cmdline_parent_registered) {
+ device_for_each_child(vm_cmdline_parent, buffer,
+ vm_cmdline_get_device);
+ }
+
return strlen(buffer) + 1;
}
@@ -766,9 +766,9 @@ static int vm_unregister_cmdline_device(struct device *dev,
static void vm_unregister_cmdline_devices(void)
{
if (vm_cmdline_parent_registered) {
- device_for_each_child(&vm_cmdline_parent, NULL,
+ device_for_each_child(vm_cmdline_parent, NULL,
vm_unregister_cmdline_device);
- device_unregister(&vm_cmdline_parent);
+ root_device_unregister(vm_cmdline_parent);
vm_cmdline_parent_registered = 0;
}
}
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index da97b6a988de..164f480b18a6 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -423,10 +423,11 @@ static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs,
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_find_one_vq_msix(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_find_one_vq_msix(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx, false,
&allocated_vectors, vector_policy,
- &vp_dev->vqs[i]);
+ &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto error_find;
@@ -485,9 +486,10 @@ static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs,
vqs[i] = NULL;
continue;
}
- vqs[i] = vp_setup_vq(vdev, queue_idx++, vqi->callback,
+ vqs[i] = vp_setup_vq(vdev, queue_idx, vqi->callback,
vqi->name, vqi->ctx,
- VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[i]);
+ VIRTIO_MSI_NO_VECTOR, &vp_dev->vqs[queue_idx]);
+ queue_idx++;
if (IS_ERR(vqs[i])) {
err = PTR_ERR(vqs[i]);
goto out_del_vqs;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index fbca7ce1c6bf..b438dc2ce1b8 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -272,6 +272,55 @@ struct vring_virtqueue {
#endif
};
+/*
+ * Accessors for device-writable fields in virtio rings.
+ * These fields are concurrently written by the device and read by the driver.
+ * Use READ_ONCE() to prevent compiler optimizations, document the
+ * intentional data race and prevent KCSAN warnings.
+ */
+static inline u16 vring_read_split_used_idx(const struct vring_virtqueue *vq)
+{
+ return virtio16_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->idx));
+}
+
+static inline u32 vring_read_split_used_id(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return virtio32_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->ring[idx].id));
+}
+
+static inline u32 vring_read_split_used_len(const struct vring_virtqueue *vq, u16 idx)
+{
+ return virtio32_to_cpu(vq->vq.vdev,
+ READ_ONCE(vq->split.vring.used->ring[idx].len));
+}
+
+static inline u16 vring_read_split_avail_event(const struct vring_virtqueue *vq)
+{
+ return virtio16_to_cpu(vq->vq.vdev,
+ READ_ONCE(vring_avail_event(&vq->split.vring)));
+}
+
+static inline u16 vring_read_packed_desc_flags(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le16_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].flags));
+}
+
+static inline u16 vring_read_packed_desc_id(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le16_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].id));
+}
+
+static inline u32 vring_read_packed_desc_len(const struct vring_virtqueue *vq,
+ u16 idx)
+{
+ return le32_to_cpu(READ_ONCE(vq->packed.vring.desc[idx].len));
+}
+
static struct vring_desc_extra *vring_alloc_desc_extra(unsigned int num);
static void vring_free(struct virtqueue *_vq);
@@ -809,8 +858,7 @@ static bool virtqueue_kick_prepare_split(struct vring_virtqueue *vq)
LAST_ADD_TIME_INVALID(vq);
if (vq->event) {
- needs_kick = vring_need_event(virtio16_to_cpu(vq->vq.vdev,
- vring_avail_event(&vq->split.vring)),
+ needs_kick = vring_need_event(vring_read_split_avail_event(vq),
new, old);
} else {
needs_kick = !(vq->split.vring.used->flags &
@@ -897,8 +945,7 @@ static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head,
static bool virtqueue_poll_split(const struct vring_virtqueue *vq,
unsigned int last_used_idx)
{
- return (u16)last_used_idx != virtio16_to_cpu(vq->vq.vdev,
- vq->split.vring.used->idx);
+ return (u16)last_used_idx != vring_read_split_used_idx(vq);
}
static bool more_used_split(const struct vring_virtqueue *vq)
@@ -939,10 +986,8 @@ static void *virtqueue_get_buf_ctx_split(struct vring_virtqueue *vq,
virtio_rmb(vq->weak_barriers);
last_used = (vq->last_used_idx & (vq->split.vring.num - 1));
- i = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used].id);
- *len = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used].len);
+ i = vring_read_split_used_id(vq, last_used);
+ *len = vring_read_split_used_len(vq, last_used);
if (unlikely(i >= vq->split.vring.num)) {
BAD_RING(vq, "id %u out of range\n", i);
@@ -1003,10 +1048,8 @@ static void *virtqueue_get_buf_ctx_split_in_order(struct vring_virtqueue *vq,
*/
virtio_rmb(vq->weak_barriers);
- vq->batch_last.id = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used_idx].id);
- vq->batch_last.len = virtio32_to_cpu(vq->vq.vdev,
- vq->split.vring.used->ring[last_used_idx].len);
+ vq->batch_last.id = vring_read_split_used_id(vq, last_used_idx);
+ vq->batch_last.len = vring_read_split_used_len(vq, last_used_idx);
}
if (vq->batch_last.id == last_used) {
@@ -1112,7 +1155,7 @@ static bool virtqueue_enable_cb_delayed_split(struct vring_virtqueue *vq)
&vring_used_event(&vq->split.vring),
cpu_to_virtio16(vq->vq.vdev, vq->last_used_idx + bufs));
- if (unlikely((u16)(virtio16_to_cpu(vq->vq.vdev, vq->split.vring.used->idx)
+ if (unlikely((u16)(vring_read_split_used_idx(vq)
- vq->last_used_idx) > bufs)) {
END_USE(vq);
return false;
@@ -2036,10 +2079,10 @@ static void detach_buf_packed(struct vring_virtqueue *vq,
static inline bool is_used_desc_packed(const struct vring_virtqueue *vq,
u16 idx, bool used_wrap_counter)
{
- bool avail, used;
u16 flags;
+ bool avail, used;
- flags = le16_to_cpu(vq->packed.vring.desc[idx].flags);
+ flags = vring_read_packed_desc_flags(vq, idx);
avail = !!(flags & (1 << VRING_PACKED_DESC_F_AVAIL));
used = !!(flags & (1 << VRING_PACKED_DESC_F_USED));
@@ -2186,8 +2229,8 @@ static void *virtqueue_get_buf_ctx_packed(struct vring_virtqueue *vq,
last_used_idx = READ_ONCE(vq->last_used_idx);
used_wrap_counter = packed_used_wrap_counter(last_used_idx);
last_used = packed_last_used(last_used_idx);
- id = le16_to_cpu(vq->packed.vring.desc[last_used].id);
- *len = le32_to_cpu(vq->packed.vring.desc[last_used].len);
+ id = vring_read_packed_desc_id(vq, last_used);
+ *len = vring_read_packed_desc_len(vq, last_used);
if (unlikely(id >= num)) {
BAD_RING(vq, "id %u out of range\n", id);
diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc_driver.c
index a57d5e06e19d..4419735b0f0d 100644
--- a/drivers/virtio/virtio_rtc_driver.c
+++ b/drivers/virtio/virtio_rtc_driver.c
@@ -1257,6 +1257,15 @@ static int viortc_init_vqs(struct viortc_dev *viortc)
return 0;
}
+static void __viortc_remove(struct viortc_dev *viortc)
+{
+ struct virtio_device *vdev = viortc->vdev;
+
+ viortc_clocks_deinit(viortc);
+ virtio_reset_device(vdev);
+ vdev->config->del_vqs(vdev);
+}
+
/**
* viortc_probe() - probe a virtio_rtc virtio device
* @vdev: virtio device
@@ -1282,7 +1291,7 @@ static int viortc_probe(struct virtio_device *vdev)
ret = viortc_init_vqs(viortc);
if (ret)
- return ret;
+ goto err_reset_vdev;
virtio_device_ready(vdev);
@@ -1329,10 +1338,7 @@ static void viortc_remove(struct virtio_device *vdev)
{
struct viortc_dev *viortc = vdev->priv;
- viortc_clocks_deinit(viortc);
-
- virtio_reset_device(vdev);
- vdev->config->del_vqs(vdev);
+ __viortc_remove(viortc);
}
static int viortc_freeze(struct virtio_device *dev)
@@ -1353,9 +1359,11 @@ static int viortc_restore(struct virtio_device *dev)
bool notify = false;
int ret;
+ dev->config->del_vqs(dev);
+
ret = viortc_init_vqs(viortc);
if (ret)
- return ret;
+ goto err_remove;
alarm_viortc_vq = &viortc->vqs[VIORTC_ALARMQ];
alarm_vq = alarm_viortc_vq->vq;
@@ -1364,7 +1372,7 @@ static int viortc_restore(struct virtio_device *dev)
ret = viortc_populate_vq(viortc, alarm_viortc_vq,
VIORTC_ALARMQ_BUF_CAP, false);
if (ret)
- return ret;
+ goto err_remove;
notify = virtqueue_kick_prepare(alarm_vq);
}
@@ -1372,8 +1380,12 @@ static int viortc_restore(struct virtio_device *dev)
virtio_device_ready(dev);
if (notify && !virtqueue_notify(alarm_vq))
- ret = -EIO;
+ return -EIO;
+
+ return 0;
+err_remove:
+ __viortc_remove(viortc);
return ret;
}
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index 3bbc4cb6a672..bf089e51970e 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -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.
*/
diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
new file mode 100644
index 000000000000..e054d5099241
--- /dev/null
+++ b/include/uapi/linux/virtio_can.h
@@ -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 */
diff --git a/include/uapi/linux/virtio_console.h b/include/uapi/linux/virtio_console.h
index 7e6ec2ff0560..0506539e6553 100644
--- a/include/uapi/linux/virtio_console.h
+++ b/include/uapi/linux/virtio_console.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;
diff --git a/kernel/vhost_task.c b/kernel/vhost_task.c
index 3f1ed7ef0582..3717885a5992 100644
--- a/kernel/vhost_task.c
+++ b/kernel/vhost_task.c
@@ -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,
diff --git a/tools/virtio/linux/dma-mapping.h b/tools/virtio/linux/dma-mapping.h
index fddfa2fbb276..8d1a16cb20db 100644
--- a/tools/virtio/linux/dma-mapping.h
+++ b/tools/virtio/linux/dma-mapping.h
@@ -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
diff --git a/tools/virtio/linux/err.h b/tools/virtio/linux/err.h
index 0943c644a701..b7b4cb516dc9 100644
--- a/tools/virtio/linux/err.h
+++ b/tools/virtio/linux/err.h
@@ -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)
diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h
index 416d02703f61..104abf9d1aee 100644
--- a/tools/virtio/linux/kernel.h
+++ b/tools/virtio/linux/kernel.h
@@ -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);
diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c
index b9591223437a..5ea6d29bc992 100644
--- a/tools/virtio/vringh_test.c
+++ b/tools/virtio/vringh_test.c
@@ -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);