diff options
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, ¬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) 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); |
