* [PATCH v5] can: virtio: Initial virtio CAN driver.
@ 2024-01-08 13:10 Mikhail Golubev-Ciuchea
2024-01-08 19:34 ` Christophe JAILLET
2025-09-11 20:59 ` Francesco Valla
0 siblings, 2 replies; 30+ messages in thread
From: Mikhail Golubev-Ciuchea @ 2024-01-08 13:10 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Harald Mommer,
Michael S. Tsirkin, Jason Wang, Xuan Zhuo
Cc: Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
From: Harald Mommer <harald.mommer@opensynergy.com>
- CAN Control
- "ip link set up can0" starts the virtual CAN controller,
- "ip link set up can0" stops the virtual CAN controller
- CAN RX
Receive CAN frames. CAN frames can be standard or extended, classic or
CAN FD. Classic CAN RTR frames are supported.
- CAN TX
Send CAN frames. CAN frames can be standard or extended, classic or
CAN FD. Classic CAN RTR frames are supported.
- CAN BusOff indication
CAN BusOff is handled by a bit in the configuration space.
Signed-off-by: Harald Mommer <Harald.Mommer@opensynergy.com>
Signed-off-by: Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com>
Co-developed-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Cc: Damir Shaikhutdinov <Damir.Shaikhutdinov@opensynergy.com>
---
V5:
* Re-base on top of linux-next (next-20240103)
* Tested with https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3
RFC V4:
* Apply reverse Christmas tree style
* Add member *classic_dlc to RX and TX CAN frames
* Fix race causing a NETDEV_TX_BUSY return
* Fix TX queue going stuck on -ENOMEM
* Update stats.tx_dropped on kzalloc() failure
* Replace "(err != 0)" with "(unlikely(err))"
* Use "ARRAY_SIZE(sgs)"
* Refactor SGs in virtio_can_send_ctrl_msg()
* Tested with https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3
RFC V3:
* Incorporate patch "[PATCH] can: virtio-can: cleanups" from
https://lore.kernel.org/all/20230424-footwear-daily-9339bd0ec428-mkl@pengutronix.de/
* Add missing can_free_echo_skb()
* Replace home-brewed ID allocator with the standard one from kernel
* Simplify flow control
* Tested with https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3
RFC V2:
* Remove the event indication queue and use the config space instead, to
indicate a bus off condition
* Rework RX and TX messages having a length field and some more fields for CAN
EXT
* Fix CAN_EFF_MASK comparison
* Remove MISRA style code (e.g. '! = 0u')
* Remove priorities leftovers
* Remove BUGONs
* Based on virtio can spec RFCv3
* Tested with https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3
MAINTAINERS | 7 +
drivers/net/can/Kconfig | 12 +
drivers/net/can/Makefile | 1 +
drivers/net/can/virtio_can.c | 1008 +++++++++++++++++++++++++++++++
include/uapi/linux/virtio_can.h | 75 +++
5 files changed, 1103 insertions(+)
create mode 100644 drivers/net/can/virtio_can.c
create mode 100644 include/uapi/linux/virtio_can.h
diff --git a/MAINTAINERS b/MAINTAINERS
index cef9597b90ac..d3892370db2b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23214,6 +23214,13 @@ 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@opensynergy.com>
+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/net/can/Kconfig b/drivers/net/can/Kconfig
index eb410714afc2..f966bab19ed8 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -215,6 +215,18 @@ config CAN_XILINXCAN
Xilinx CAN driver. This driver supports both soft AXI CAN IP and
Zynq CANPS IP.
+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.
+
source "drivers/net/can/c_can/Kconfig"
source "drivers/net/can/cc770/Kconfig"
source "drivers/net/can/ctucanfd/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index ff8f76295d13..4488e591736e 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -30,6 +30,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..924a7c19e438
--- /dev/null
+++ b/drivers/net/can/virtio_can.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CAN bus driver for the Virtio CAN controller
+ * Copyright (C) 2021-2023 OpenSynergy GmbH
+ */
+
+#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 /* Driver side view! The device receives here */
+#define VIRTIO_CAN_QUEUE_RX 1 /* Driver side view! The device transmits here */
+#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 {
+ struct list_head list;
+ unsigned int putidx;
+ struct virtio_can_tx_out tx_out;
+ struct virtio_can_tx_in tx_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];
+ /* I/O callback function pointers for the virtqueues */
+ vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
+ /* Lock for TX operations */
+ spinlock_t tx_lock;
+ /* Control queue lock. Defensive programming, may be not needed */
+ struct mutex ctrl_lock;
+ /* Wait for control queue processing without polling */
+ struct completion ctrl_done;
+ /* List of virtio CAN TX message */
+ struct list_head tx_list;
+ /* Array of receive queue messages */
+ struct virtio_can_rx rpkt[128];
+ /* Those control queue messages cannot live on the stack! */
+ struct virtio_can_control_out cpkt_out;
+ struct virtio_can_control_in cpkt_in;
+ /* Data to get and maintain the putidx for local TX echo */
+ struct ida tx_putidx_ida;
+ /* In flight TX messages */
+ atomic_t tx_inflight;
+ /* BusOff pending. Reset after successful indication to upper layer */
+ bool busoff_pending;
+};
+
+/* Function copied from virtio_net.c */
+static void virtqueue_napi_schedule(struct napi_struct *napi,
+ struct virtqueue *vq)
+{
+ if (napi_schedule_prep(napi)) {
+ virtqueue_disable_cb(vq);
+ __napi_schedule(napi);
+ }
+}
+
+/* Function copied from virtio_net.c */
+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 int virtio_can_alloc_tx_idx(struct virtio_can_priv *priv)
+{
+ int tx_idx;
+
+ tx_idx = ida_alloc_range(&priv->tx_putidx_ida, 0,
+ priv->can.echo_skb_max - 1, GFP_KERNEL);
+ if (tx_idx >= 0)
+ atomic_add(1, &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_sub(1, &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);
+ virtqueue_kick(vq);
+ if (ret == 0)
+ ret = vq->num_free;
+ return ret;
+}
+
+/* Send a control message with message type either
+ *
+ * - VIRTIO_CAN_SET_CTRL_MODE_START or
+ * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
+ *
+ * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
+ * for this Linux driver to have an asynchronous implementation of the mode
+ * setting function so in order to keep things simple the function is
+ * implemented as synchronous function. Design pattern is
+ * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
+ */
+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 device *dev = &priv->vdev->dev;
+ struct virtqueue *vq;
+ unsigned int len;
+ int err;
+
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
+
+ /* The function may be serialized by rtnl lock. Not sure.
+ * Better safe than sorry.
+ */
+ mutex_lock(&priv->ctrl_lock);
+
+ priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
+ sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
+ sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
+
+ err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
+ if (err != 0) {
+ /* Not expected to happen */
+ dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
+ }
+
+ if (!virtqueue_kick(vq)) {
+ /* Not expected to happen */
+ dev_err(dev, "%s(): Kick failed\n", __func__);
+ }
+
+ while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
+ wait_for_completion(&priv->ctrl_done);
+
+ mutex_unlock(&priv->ctrl_lock);
+
+ return priv->cpkt_in.result;
+}
+
+static void 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) {
+ /* Not expected to happen */
+ netdev_err(ndev, "CAN controller start failed\n");
+ }
+
+ priv->busoff_pending = false;
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* Switch carrier on if device was not connected to the bus */
+ if (!netif_carrier_ok(ndev))
+ netif_carrier_on(ndev);
+}
+
+/* See also m_can.c/m_can_set_mode()
+ *
+ * It is interesting that not only the M-CAN implementation but also all other
+ * implementations I looked into only support CAN_MODE_START.
+ * That CAN_MODE_SLEEP is frequently not found to be supported anywhere did not
+ * come not as surprise but that CAN_MODE_STOP is also never supported was one.
+ * The function is accessible via the method pointer do_set_mode in
+ * struct can_priv. As usual no documentation there.
+ * May not play any role as grepping through the code did not reveal any place
+ * from where the method is actually called.
+ */
+static int virtio_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ virtio_can_start(dev);
+ netif_wake_queue(dev);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/* Called by issuing "ip link set up can0" */
+static int virtio_can_open(struct net_device *dev)
+{
+ /* start the virtio_can controller */
+ virtio_can_start(dev);
+
+ /* RX and TX napi were already enabled in virtio_can_probe() */
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void 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");
+
+ 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);
+}
+
+static int virtio_can_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ /* Keep RX napi active to allow dropping of pending RX CAN messages,
+ * keep TX napi active to allow processing of cancelled CAN messages
+ */
+ virtio_can_stop(dev);
+ close_candev(dev);
+
+ return 0;
+}
+
+static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
+ struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ struct virtio_can_priv *priv = netdev_priv(dev);
+ netdev_tx_t xmit_ret = NETDEV_TX_OK;
+ struct virtio_can_tx *can_tx_msg;
+ struct virtqueue *vq;
+ unsigned long flags;
+ u32 can_flags;
+ int putidx;
+ int err;
+
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+
+ 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), GFP_ATOMIC);
+ if (!can_tx_msg) {
+ 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_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);
+ can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
+
+ /* Prepare sending of virtio message */
+ 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);
+ dev->stats.tx_dropped++;
+ goto kick; /* No way to return NET_XMIT_DROP here */
+ }
+
+ can_tx_msg->putidx = (unsigned int)putidx;
+
+ /* Protect list operation */
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ list_add_tail(&can_tx_msg->list, &priv->tx_list);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
+ can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
+
+ /* Protect queue and list operations */
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
+ if (unlikely(err)) { /* checking vq->num_free in flow control */
+ list_del(&can_tx_msg->list);
+ can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
+ virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ netif_stop_queue(dev);
+ kfree(can_tx_msg);
+ /* Expected never to be seen */
+ netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
+ xmit_ret = NETDEV_TX_BUSY;
+ 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");
+ }
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+kick:
+ if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
+ 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,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+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);
+}
+
+/* Compare with m_can.c/m_can_echo_tx_event() */
+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 long flags;
+ unsigned int len;
+ u8 result;
+
+ stats = &dev->stats;
+
+ /* Protect list and virtio queue operations */
+ spin_lock_irqsave(&can_priv->tx_lock, flags);
+
+ can_tx_msg = virtqueue_get_buf(vq, &len);
+ if (!can_tx_msg) {
+ spin_unlock_irqrestore(&can_priv->tx_lock, flags);
+ return 0; /* No more data */
+ }
+
+ 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) {
+ /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
+ * echoed. Intentional to bring a waiting process in an upper
+ * layer to an end.
+ * TODO: Any better means to indicate a problem here?
+ */
+ if (result != VIRTIO_CAN_RESULT_OK)
+ netdev_warn(dev, "TX ACK: Result = %u\n", result);
+
+ 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);
+ }
+
+ list_del(&can_tx_msg->list);
+ 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);
+ }
+
+ spin_unlock_irqrestore(&can_priv->tx_lock, flags);
+
+ kfree(can_tx_msg);
+
+ return 1; /* Queue was not empty so there may be more data */
+}
+
+/* Poll TX used queue for sent CAN messages
+ * See https://wiki.linuxfoundation.org/networking/napi function
+ * int (*poll)(struct napi_struct *napi, int budget);
+ */
+static int virtio_can_tx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *dev = napi->dev;
+ struct virtio_can_priv *priv;
+ struct virtqueue *vq;
+ int work_done = 0;
+
+ priv = netdev_priv(dev);
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+
+ 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.
+ * (See also m_can.c / m_can_read_fifo()).
+ */
+static int virtio_can_read_rx_queue(struct virtqueue *vq)
+{
+ const unsigned int header_size = offsetof(struct virtio_can_rx, sdu);
+ 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;
+
+ 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 += cf->len;
+ }
+
+putback:
+ /* Put processed RX buffer back into avail queue */
+ virtio_can_add_inbuf(vq, can_rx, sizeof(struct virtio_can_rx));
+
+ return 1; /* Queue was not empty so there may be more data */
+}
+
+/* See m_can_poll() / m_can_handle_state_errors() m_can_handle_state_change() */
+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;
+}
+
+/* Poll RX used queue for received CAN messages
+ * See https://wiki.linuxfoundation.org/networking/napi function
+ * int (*poll)(struct napi_struct *napi, int budget);
+ * Important: "The networking subsystem promises that poll() will not be
+ * invoked simultaneously (for the same napi_struct) on multiple processors"
+ */
+static int virtio_can_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *dev = napi->dev;
+ struct virtio_can_priv *priv;
+ struct virtqueue *vq;
+ int work_done = 0;
+
+ priv = netdev_priv(dev);
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
+
+ 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_vqs(struct virtio_device *vdev)
+
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct virtqueue *vq;
+ unsigned int idx;
+ int ret;
+
+ /* Fill RX queue */
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
+ for (idx = 0; idx < ARRAY_SIZE(priv->rpkt); idx++) {
+ ret = virtio_can_add_inbuf(vq, &priv->rpkt[idx],
+ sizeof(struct virtio_can_rx));
+ if (ret < 0) {
+ dev_dbg(&vdev->dev, "rpkt fill: ret=%d, idx=%u\n",
+ ret, idx);
+ break;
+ }
+ }
+ dev_dbg(&vdev->dev, "%u rpkt added\n", idx);
+}
+
+static int virtio_can_find_vqs(struct virtio_can_priv *priv)
+{
+ /* The order of RX and TX is exactly the opposite as in console and
+ * network. Does not play any role but is a bad trap.
+ */
+ static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
+ "can-tx",
+ "can-rx",
+ "can-state-ctrl"
+ };
+
+ priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
+ priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
+ priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
+
+ /* Find the queues. */
+ return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
+ priv->io_callbacks, io_names, 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 list_head *cursor, *next;
+ struct virtqueue *vq;
+
+ /* Reset the device */
+ if (vdev->config->reset)
+ vdev->config->reset(vdev);
+
+ /* From here we have dead silence from the device side so no locks
+ * are needed to protect against device side events.
+ */
+
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
+ while (virtqueue_detach_unused_buf(vq))
+ ; /* Do nothing, content allocated statically */
+
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
+ while (virtqueue_detach_unused_buf(vq))
+ ; /* Do nothing, content allocated statically */
+
+ vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
+ while (virtqueue_detach_unused_buf(vq))
+ ; /* Do nothing, content to be de-allocated separately */
+
+ /* Is keeping track of allocated elements by an own linked list
+ * really necessary or may this be optimized using only
+ * virtqueue_detach_unused_buf()?
+ */
+ list_for_each_safe(cursor, next, &priv->tx_list) {
+ struct virtio_can_tx *can_tx;
+
+ can_tx = list_entry(cursor, struct virtio_can_tx, list);
+ list_del(cursor);
+ kfree(can_tx);
+ }
+
+ if (vdev->config->del_vqs)
+ vdev->config->del_vqs(vdev);
+}
+
+/* See virtio_net.c/virtnet_remove() and also m_can.c/m_can_plat_remove() */
+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);
+
+ /* No calls of netif_napi_del() needed as free_candev() will do this */
+
+ 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;
+ 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;
+ /* Set Virtio CAN supported operations */
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
+ if (virtio_has_feature(vdev, VIRTIO_CAN_F_CAN_FD)) {
+ 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;
+
+ INIT_LIST_HEAD(&priv->tx_list);
+
+ spin_lock_init(&priv->tx_lock);
+ mutex_init(&priv->ctrl_lock);
+
+ init_completion(&priv->ctrl_done);
+
+ virtio_can_populate_vqs(vdev);
+
+ register_virtio_can_dev(dev);
+
+ napi_enable(&priv->napi);
+ napi_enable(&priv->napi_tx);
+
+ /* Request device going live */
+ virtio_device_ready(vdev); /* Optionally done by virtio_dev_probe() */
+
+ return 0;
+
+on_failure:
+ virtio_can_free_candev(dev);
+ return err;
+}
+
+/* Compare with m_can.c/m_can_suspend(), virtio_net.c/virtnet_freeze() and
+ * virtio_card.c/virtsnd_freeze()
+ */
+static int __maybe_unused virtio_can_freeze(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct net_device *ndev = priv->dev;
+
+ napi_disable(&priv->napi);
+ napi_disable(&priv->napi_tx);
+
+ if (netif_running(ndev)) {
+ netif_stop_queue(ndev);
+ netif_device_detach(ndev);
+ virtio_can_stop(ndev);
+ }
+
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ virtio_can_del_vq(vdev);
+
+ return 0;
+}
+
+/* Compare with m_can.c/m_can_resume(), virtio_net.c/virtnet_restore() and
+ * virtio_card.c/virtsnd_restore()
+ */
+static int __maybe_unused virtio_can_restore(struct virtio_device *vdev)
+{
+ struct virtio_can_priv *priv = vdev->priv;
+ struct net_device *ndev = priv->dev;
+ int err;
+
+ err = virtio_can_find_vqs(priv);
+ if (err != 0)
+ return err;
+ virtio_can_populate_vqs(vdev);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ if (netif_running(ndev)) {
+ virtio_can_start(ndev);
+ netif_device_attach(ndev);
+ netif_start_queue(ndev);
+ }
+
+ napi_enable(&priv->napi);
+ napi_enable(&priv->napi_tx);
+
+ 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/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
new file mode 100644
index 000000000000..7cf613bb3f1a
--- /dev/null
+++ b/include/uapi/linux/virtio_can.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2021-2023 OpenSynergy GmbH
+ */
+#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_LATE_TX_ACK 2
+#define VIRTIO_CAN_F_RTR_FRAMES 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
+
+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[64];
+};
+
+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[64];
+};
+
+/* 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 */
--
2.43.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2024-01-08 13:10 [PATCH v5] can: virtio: Initial virtio CAN driver Mikhail Golubev-Ciuchea
@ 2024-01-08 19:34 ` Christophe JAILLET
2024-02-01 18:57 ` Harald Mommer
2025-09-11 20:59 ` Francesco Valla
1 sibling, 1 reply; 30+ messages in thread
From: Christophe JAILLET @ 2024-01-08 19:34 UTC (permalink / raw)
To: Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Marc Kleine-Budde,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Harald Mommer, Michael S. Tsirkin, Jason Wang, Xuan Zhuo
Cc: Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
Le 08/01/2024 à 14:10, Mikhail Golubev-Ciuchea a écrit :
> From: Harald Mommer <harald.mommer@opensynergy.com>
>
> - CAN Control
>
> - "ip link set up can0" starts the virtual CAN controller,
> - "ip link set up can0" stops the virtual CAN controller
>
> - CAN RX
>
> Receive CAN frames. CAN frames can be standard or extended, classic or
> CAN FD. Classic CAN RTR frames are supported.
>
> - CAN TX
>
> Send CAN frames. CAN frames can be standard or extended, classic or
> CAN FD. Classic CAN RTR frames are supported.
>
> - CAN BusOff indication
>
> CAN BusOff is handled by a bit in the configuration space.
>
> Signed-off-by: Harald Mommer <Harald.Mommer@opensynergy.com>
> Signed-off-by: Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com>
> Co-developed-by: Marc Kleine-Budde <mkl@pengutronix.de>
> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
> Cc: Damir Shaikhutdinov <Damir.Shaikhutdinov@opensynergy.com>
> ---
Hi,
a few nits below, should there be a v6.
> +static int virtio_can_alloc_tx_idx(struct virtio_can_priv *priv)
> +{
> + int tx_idx;
> +
> + tx_idx = ida_alloc_range(&priv->tx_putidx_ida, 0,
> + priv->can.echo_skb_max - 1, GFP_KERNEL);
> + if (tx_idx >= 0)
> + atomic_add(1, &priv->tx_inflight);
atomic_inc() ?
> +
> + 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_sub(1, &priv->tx_inflight);
atomic_dec() ?
> +}
...
> +static int virtio_can_probe(struct virtio_device *vdev)
> +{
> + struct virtio_can_priv *priv;
> + struct net_device *dev;
> + 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;
> + /* Set Virtio CAN supported operations */
> + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
> + if (virtio_has_feature(vdev, VIRTIO_CAN_F_CAN_FD)) {
> + 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;
> +
> + INIT_LIST_HEAD(&priv->tx_list);
> +
> + spin_lock_init(&priv->tx_lock);
> + mutex_init(&priv->ctrl_lock);
> +
> + init_completion(&priv->ctrl_done);
> +
> + virtio_can_populate_vqs(vdev);
> +
> + register_virtio_can_dev(dev);
Check for error?
CJ
> +
> + napi_enable(&priv->napi);
> + napi_enable(&priv->napi_tx);
> +
> + /* Request device going live */
> + virtio_device_ready(vdev); /* Optionally done by virtio_dev_probe() */
> +
> + return 0;
> +
> +on_failure:
> + virtio_can_free_candev(dev);
> + return err;
> +}
...
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2024-01-08 19:34 ` Christophe JAILLET
@ 2024-02-01 18:57 ` Harald Mommer
2025-03-12 10:31 ` Matias Ezequiel Vara Larsen
2025-03-13 18:48 ` Matias Ezequiel Vara Larsen
0 siblings, 2 replies; 30+ messages in thread
From: Harald Mommer @ 2024-02-01 18:57 UTC (permalink / raw)
To: Christophe JAILLET, Mikhail Golubev-Ciuchea, Wolfgang Grandegger,
Marc Kleine-Budde, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Michael S. Tsirkin, Jason Wang, Xuan Zhuo
Cc: Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
Hello,
I thought there would be some more comments coming and I could address
everything in one chunk. Not the case, besides your comments silence.
On 08.01.24 20:34, Christophe JAILLET wrote:
>
> Hi,
> a few nits below, should there be a v6.
>
I'm sure there will be but not so soon. Probably after acceptance of the
virtio CAN specification or after change requests to the specification
are received and the driver has to be adapted to an updated draft.
>
>> +static int virtio_can_alloc_tx_idx(struct virtio_can_priv *priv)
>> +{
>> + int tx_idx;
>> +
>> + tx_idx = ida_alloc_range(&priv->tx_putidx_ida, 0,
>> + priv->can.echo_skb_max - 1, GFP_KERNEL);
>> + if (tx_idx >= 0)
>> + atomic_add(1, &priv->tx_inflight);
>
> atomic_inc() ?
Yes, will be done, already in my local code base.
>
>> +
>> + 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_sub(1, &priv->tx_inflight);
>
> atomic_dec() ?
See above.
>
>> +}
>
> ...
>
>> +static int virtio_can_probe(struct virtio_device *vdev)
>> +{
>> + struct virtio_can_priv *priv;
>> + struct net_device *dev;
>> + 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;
>> + /* Set Virtio CAN supported operations */
>> + priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
>> + if (virtio_has_feature(vdev, VIRTIO_CAN_F_CAN_FD)) {
>> + 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;
>> +
>> + INIT_LIST_HEAD(&priv->tx_list);
>> +
>> + spin_lock_init(&priv->tx_lock);
>> + mutex_init(&priv->ctrl_lock);
>> +
>> + init_completion(&priv->ctrl_done);
>> +
>> + virtio_can_populate_vqs(vdev);
>> +
>> + register_virtio_can_dev(dev);
>
> Check for error?
err = register_virtio_can_dev(dev);
if (err) {
virtio_can_del_vq(vdev);
dev_err(&vdev->dev, "Couldn't register candev (err=%d)\n", err);
goto on_failure;
}
>
> CJ
>
>> +
>> + napi_enable(&priv->napi);
>> + napi_enable(&priv->napi_tx);
>> +
>> + /* Request device going live */
>> + virtio_device_ready(vdev); /* Optionally done by
>> virtio_dev_probe() */
The virtio_device_ready() will also be removed. The caller will make the
device live anyway after return. There are no operations between the
virtio_device_ready() and the return 0 which make it necessary to have
the device live early before the return.
>> +
>> + return 0;
>> +
>> +on_failure:
>> + virtio_can_free_candev(dev);
>> + return err;
>> +}
>
> ...
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2024-02-01 18:57 ` Harald Mommer
@ 2025-03-12 10:31 ` Matias Ezequiel Vara Larsen
2025-03-12 10:41 ` Marc Kleine-Budde
2025-03-13 18:48 ` Matias Ezequiel Vara Larsen
1 sibling, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-03-12 10:31 UTC (permalink / raw)
To: Harald Mommer
Cc: Christophe JAILLET, Mikhail Golubev-Ciuchea, Wolfgang Grandegger,
Marc Kleine-Budde, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
Hello,
On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> Hello,
>
> I thought there would be some more comments coming and I could address
> everything in one chunk. Not the case, besides your comments silence.
>
> On 08.01.24 20:34, Christophe JAILLET wrote:
> >
> > Hi,
> > a few nits below, should there be a v6.
> >
>
> I'm sure there will be but not so soon. Probably after acceptance of the
> virtio CAN specification or after change requests to the specification are
> received and the driver has to be adapted to an updated draft.
>
>
What is the status of this series?
Thanks, Matias.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-03-12 10:31 ` Matias Ezequiel Vara Larsen
@ 2025-03-12 10:41 ` Marc Kleine-Budde
2025-03-12 13:28 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Marc Kleine-Budde @ 2025-03-12 10:41 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Harald Mommer, Christophe JAILLET, Mikhail Golubev-Ciuchea,
Wolfgang Grandegger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Jason Wang,
Xuan Zhuo, Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
[-- Attachment #1: Type: text/plain, Size: 1257 bytes --]
On 12.03.2025 11:31:12, Matias Ezequiel Vara Larsen wrote:
> On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> > Hello,
> >
> > I thought there would be some more comments coming and I could address
> > everything in one chunk. Not the case, besides your comments silence.
> >
> > On 08.01.24 20:34, Christophe JAILLET wrote:
> > >
> > > Hi,
> > > a few nits below, should there be a v6.
> > >
> >
> > I'm sure there will be but not so soon. Probably after acceptance of the
> > virtio CAN specification or after change requests to the specification are
> > received and the driver has to be adapted to an updated draft.
> >
> What is the status of this series?
There has been no movement from the Linux side. The patch series is
quite extensive. To get this mainline, we need not only a proper Linux
CAN driver, but also a proper VirtIO specification. This whole project
is too big for me to do it as a collaborative effort.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-03-12 10:41 ` Marc Kleine-Budde
@ 2025-03-12 13:28 ` Matias Ezequiel Vara Larsen
2025-03-12 13:36 ` Marc Kleine-Budde
0 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-03-12 13:28 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Harald Mommer, Christophe JAILLET, Mikhail Golubev-Ciuchea,
Wolfgang Grandegger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Jason Wang,
Xuan Zhuo, Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
On Wed, Mar 12, 2025 at 11:41:26AM +0100, Marc Kleine-Budde wrote:
> On 12.03.2025 11:31:12, Matias Ezequiel Vara Larsen wrote:
> > On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> > > Hello,
> > >
> > > I thought there would be some more comments coming and I could address
> > > everything in one chunk. Not the case, besides your comments silence.
> > >
> > > On 08.01.24 20:34, Christophe JAILLET wrote:
> > > >
> > > > Hi,
> > > > a few nits below, should there be a v6.
> > > >
> > >
> > > I'm sure there will be but not so soon. Probably after acceptance of the
> > > virtio CAN specification or after change requests to the specification are
> > > received and the driver has to be adapted to an updated draft.
> > >
> > What is the status of this series?
>
> There has been no movement from the Linux side. The patch series is
> quite extensive. To get this mainline, we need not only a proper Linux
> CAN driver, but also a proper VirtIO specification.
Thanks for your answer. AFAIK the spec has been merged (see
https://github.com/oasis-tcs/virtio-spec/tree/virtio-1.4).
> This whole project is too big for me to do it as a collaborative
> effort.
>
What do you mean?
Thanks,
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-03-12 13:28 ` Matias Ezequiel Vara Larsen
@ 2025-03-12 13:36 ` Marc Kleine-Budde
2025-03-12 15:11 ` Matias Ezequiel Vara Larsen
2025-03-13 0:26 ` Jason Wang
0 siblings, 2 replies; 30+ messages in thread
From: Marc Kleine-Budde @ 2025-03-12 13:36 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Harald Mommer, Christophe JAILLET, Mikhail Golubev-Ciuchea,
Wolfgang Grandegger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Jason Wang,
Xuan Zhuo, Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
[-- Attachment #1: Type: text/plain, Size: 1929 bytes --]
On 12.03.2025 14:28:10, Matias Ezequiel Vara Larsen wrote:
> On Wed, Mar 12, 2025 at 11:41:26AM +0100, Marc Kleine-Budde wrote:
> > On 12.03.2025 11:31:12, Matias Ezequiel Vara Larsen wrote:
> > > On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> > > > Hello,
> > > >
> > > > I thought there would be some more comments coming and I could address
> > > > everything in one chunk. Not the case, besides your comments silence.
> > > >
> > > > On 08.01.24 20:34, Christophe JAILLET wrote:
> > > > >
> > > > > Hi,
> > > > > a few nits below, should there be a v6.
> > > > >
> > > >
> > > > I'm sure there will be but not so soon. Probably after acceptance of the
> > > > virtio CAN specification or after change requests to the specification are
> > > > received and the driver has to be adapted to an updated draft.
> > > >
> > > What is the status of this series?
> >
> > There has been no movement from the Linux side. The patch series is
> > quite extensive. To get this mainline, we need not only a proper Linux
> > CAN driver, but also a proper VirtIO specification.
>
> Thanks for your answer. AFAIK the spec has been merged (see
> https://github.com/oasis-tcs/virtio-spec/tree/virtio-1.4).
Yes, the spec was merged. I think it was written with a specific
use-case (IIRC: automotive, Linux on-top of a specific hypervisor) in
mind, in Linux we have other use cases that might not be covered.
> > This whole project is too big for me to do it as a collaborative
> > effort.
>
> What do you mean?
I mean the driver is too big to review on a non-paid community based
effort.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-03-12 13:36 ` Marc Kleine-Budde
@ 2025-03-12 15:11 ` Matias Ezequiel Vara Larsen
2025-03-13 0:26 ` Jason Wang
1 sibling, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-03-12 15:11 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Harald Mommer, Christophe JAILLET, Mikhail Golubev-Ciuchea,
Wolfgang Grandegger, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin, Jason Wang,
Xuan Zhuo, Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
On Wed, Mar 12, 2025 at 02:36:05PM +0100, Marc Kleine-Budde wrote:
> On 12.03.2025 14:28:10, Matias Ezequiel Vara Larsen wrote:
> > On Wed, Mar 12, 2025 at 11:41:26AM +0100, Marc Kleine-Budde wrote:
> > > On 12.03.2025 11:31:12, Matias Ezequiel Vara Larsen wrote:
> > > > On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> > > > > Hello,
> > > > >
> > > > > I thought there would be some more comments coming and I could address
> > > > > everything in one chunk. Not the case, besides your comments silence.
> > > > >
> > > > > On 08.01.24 20:34, Christophe JAILLET wrote:
> > > > > >
> > > > > > Hi,
> > > > > > a few nits below, should there be a v6.
> > > > > >
> > > > >
> > > > > I'm sure there will be but not so soon. Probably after acceptance of the
> > > > > virtio CAN specification or after change requests to the specification are
> > > > > received and the driver has to be adapted to an updated draft.
> > > > >
> > > > What is the status of this series?
> > >
> > > There has been no movement from the Linux side. The patch series is
> > > quite extensive. To get this mainline, we need not only a proper Linux
> > > CAN driver, but also a proper VirtIO specification.
> >
> > Thanks for your answer. AFAIK the spec has been merged (see
> > https://github.com/oasis-tcs/virtio-spec/tree/virtio-1.4).
>
> Yes, the spec was merged. I think it was written with a specific
> use-case (IIRC: automotive, Linux on-top of a specific hypervisor) in
> mind, in Linux we have other use cases that might not be covered.
What use-case you have in Linux?
>
> > > This whole project is too big for me to do it as a collaborative
> > > effort.
> >
> > What do you mean?
>
> I mean the driver is too big to review on a non-paid community based
> effort.
>
I think I can help reviewing it. I will try to spend some time in the next
weeks.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-03-12 13:36 ` Marc Kleine-Budde
2025-03-12 15:11 ` Matias Ezequiel Vara Larsen
@ 2025-03-13 0:26 ` Jason Wang
1 sibling, 0 replies; 30+ messages in thread
From: Jason Wang @ 2025-03-13 0:26 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Matias Ezequiel Vara Larsen, Harald Mommer, Christophe JAILLET,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Michael S. Tsirkin,
Xuan Zhuo, Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
On Wed, Mar 12, 2025 at 9:36 PM Marc Kleine-Budde <mkl@pengutronix.de> wrote:
>
> On 12.03.2025 14:28:10, Matias Ezequiel Vara Larsen wrote:
> > On Wed, Mar 12, 2025 at 11:41:26AM +0100, Marc Kleine-Budde wrote:
> > > On 12.03.2025 11:31:12, Matias Ezequiel Vara Larsen wrote:
> > > > On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> > > > > Hello,
> > > > >
> > > > > I thought there would be some more comments coming and I could address
> > > > > everything in one chunk. Not the case, besides your comments silence.
> > > > >
> > > > > On 08.01.24 20:34, Christophe JAILLET wrote:
> > > > > >
> > > > > > Hi,
> > > > > > a few nits below, should there be a v6.
> > > > > >
> > > > >
> > > > > I'm sure there will be but not so soon. Probably after acceptance of the
> > > > > virtio CAN specification or after change requests to the specification are
> > > > > received and the driver has to be adapted to an updated draft.
> > > > >
> > > > What is the status of this series?
> > >
> > > There has been no movement from the Linux side. The patch series is
> > > quite extensive. To get this mainline, we need not only a proper Linux
> > > CAN driver, but also a proper VirtIO specification.
> >
> > Thanks for your answer. AFAIK the spec has been merged (see
> > https://github.com/oasis-tcs/virtio-spec/tree/virtio-1.4).
>
> Yes, the spec was merged. I think it was written with a specific
> use-case (IIRC: automotive, Linux on-top of a specific hypervisor) in
> mind, in Linux we have other use cases that might not be covered.
>
> > > This whole project is too big for me to do it as a collaborative
> > > effort.
> >
> > What do you mean?
>
> I mean the driver is too big to review on a non-paid community based
> effort.
If you can split the path into smaller ones, I'm happy to review.
Thanks
>
> regards,
> Marc
>
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Embedded Linux | https://www.pengutronix.de |
> Vertretung Nürnberg | Phone: +49-5121-206917-129 |
> Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2024-02-01 18:57 ` Harald Mommer
2025-03-12 10:31 ` Matias Ezequiel Vara Larsen
@ 2025-03-13 18:48 ` Matias Ezequiel Vara Larsen
1 sibling, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-03-13 18:48 UTC (permalink / raw)
To: Harald Mommer
Cc: Christophe JAILLET, Mikhail Golubev-Ciuchea, Wolfgang Grandegger,
Marc Kleine-Budde, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization
On Thu, Feb 01, 2024 at 07:57:45PM +0100, Harald Mommer wrote:
> Hello,
>
> I thought there would be some more comments coming and I could address
> everything in one chunk. Not the case, besides your comments silence.
>
> On 08.01.24 20:34, Christophe JAILLET wrote:
> >
> > Hi,
> > a few nits below, should there be a v6.
> >
>
> I'm sure there will be but not so soon. Probably after acceptance of the
> virtio CAN specification or after change requests to the specification are
> received and the driver has to be adapted to an updated draft.
>
What are the changes in the specification that should be taken into
account for the next series?
Thanks, Matias.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2024-01-08 13:10 [PATCH v5] can: virtio: Initial virtio CAN driver Mikhail Golubev-Ciuchea
2024-01-08 19:34 ` Christophe JAILLET
@ 2025-09-11 20:59 ` Francesco Valla
2025-10-01 15:23 ` Matias Ezequiel Vara Larsen
` (4 more replies)
1 sibling, 5 replies; 30+ messages in thread
From: Francesco Valla @ 2025-09-11 20:59 UTC (permalink / raw)
To: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea
Cc: Wolfgang Grandegger, Eric Dumazet, Jakub Kicinski,
Michael S. Tsirkin, Jason Wang, Xuan Zhuo, Damir Shaikhutdinov,
linux-kernel, linux-can, netdev, virtualization,
Matias Ezequiel Vara Larsen, development
Hello Mikhail, Harald,
hoping there will be a v6 of this patch soon, a few comments:
On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
[...]
> +
> +/* 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];
> + /* I/O callback function pointers for the virtqueues */
> + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> + /* Lock for TX operations */
> + spinlock_t tx_lock;
> + /* Control queue lock. Defensive programming, may be not needed */
> + struct mutex ctrl_lock;
> + /* Wait for control queue processing without polling */
> + struct completion ctrl_done;
> + /* List of virtio CAN TX message */
> + struct list_head tx_list;
> + /* Array of receive queue messages */
> + struct virtio_can_rx rpkt[128];
This array should probably be allocated dynamically at probe - maybe
using a module parameter instead of a hardcoded value as length?
> + /* Those control queue messages cannot live on the stack! */
> + struct virtio_can_control_out cpkt_out;
> + struct virtio_can_control_in cpkt_in;
Consider using a container struct as you did for the tx message, e.g.:
struct virtio_can_control {
struct virtio_can_control_out ctrl_out;
struct virtio_can_control_in ctrl_in;
};
> + /* Data to get and maintain the putidx for local TX echo */
> + struct ida tx_putidx_ida;
> + /* In flight TX messages */
> + atomic_t tx_inflight;
> + /* BusOff pending. Reset after successful indication to upper layer */
> + bool busoff_pending;
> +};
> +
[...]
> +
> +/* Send a control message with message type either
> + *
> + * - VIRTIO_CAN_SET_CTRL_MODE_START or
> + * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
> + *
> + * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
> + * for this Linux driver to have an asynchronous implementation of the mode
> + * setting function so in order to keep things simple the function is
> + * implemented as synchronous function. Design pattern is
> + * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
> + */
> +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 device *dev = &priv->vdev->dev;
> + struct virtqueue *vq;
> + unsigned int len;
> + int err;
> +
> + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> +
> + /* The function may be serialized by rtnl lock. Not sure.
> + * Better safe than sorry.
> + */
> + mutex_lock(&priv->ctrl_lock);
> +
> + priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
> + sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
> + sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
> +
> + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
> + if (err != 0) {
> + /* Not expected to happen */
> + dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
> + }
Here it should return VIRTIO_CAN_RESULT_NOT_OK after unlocking the
mutex, or it might wait for completion indefinitley below.
> +
> + if (!virtqueue_kick(vq)) {
> + /* Not expected to happen */
> + dev_err(dev, "%s(): Kick failed\n", __func__);
> + }
And here too.
> +
> + while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
> + wait_for_completion(&priv->ctrl_done);
> +
> + mutex_unlock(&priv->ctrl_lock);
> +
> + return priv->cpkt_in.result;
> +}
> +
[...]
> +static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
> + struct net_device *dev)
> +{
> + const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
> + struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
> + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> + struct virtio_can_priv *priv = netdev_priv(dev);
> + netdev_tx_t xmit_ret = NETDEV_TX_OK;
> + struct virtio_can_tx *can_tx_msg;
> + struct virtqueue *vq;
> + unsigned long flags;
> + u32 can_flags;
> + int putidx;
> + int err;
> +
> + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> +
> + 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), GFP_ATOMIC);
> + if (!can_tx_msg) {
> + dev->stats.tx_dropped++;
> + goto kick; /* No way to return NET_XMIT_DROP here */
> + }
> +
Since we are allocating tx messages dynamically, the sdu[64] array inside
struct virtio_can_tx_out can be converted to a flexible array and here
the allocation can become:
can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
This would save memory in particular on CAN-CC interfaces, where 56 bytes
per message would otherwise be lost (not to mention the case if/when
CAN-XL will be supported).
> + can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
> + 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);
> + can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
> +
> + /* Prepare sending of virtio message */
> + 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);
> + dev->stats.tx_dropped++;
> + goto kick; /* No way to return NET_XMIT_DROP here */
> + }
> +
> + can_tx_msg->putidx = (unsigned int)putidx;
> +
> + /* Protect list operation */
> + spin_lock_irqsave(&priv->tx_lock, flags);
> + list_add_tail(&can_tx_msg->list, &priv->tx_list);
> + spin_unlock_irqrestore(&priv->tx_lock, flags);
> +
> + /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
> + can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
> +
> + /* Protect queue and list operations */
> + spin_lock_irqsave(&priv->tx_lock, flags);
> + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
> + if (unlikely(err)) { /* checking vq->num_free in flow control */
> + list_del(&can_tx_msg->list);
> + can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
> + virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
> + spin_unlock_irqrestore(&priv->tx_lock, flags);
> + netif_stop_queue(dev);
> + kfree(can_tx_msg);
> + /* Expected never to be seen */
> + netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
> + xmit_ret = NETDEV_TX_BUSY;
> + 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");
> + }
> +
> + spin_unlock_irqrestore(&priv->tx_lock, flags);
> +
> +kick:
> + if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
> + 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,
> + .ndo_change_mtu = can_change_mtu,
> +};
> +
> +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);
> +}
> +
> +/* Compare with m_can.c/m_can_echo_tx_event() */
> +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 long flags;
> + unsigned int len;
> + u8 result;
> +
> + stats = &dev->stats;
> +
> + /* Protect list and virtio queue operations */
> + spin_lock_irqsave(&can_priv->tx_lock, flags);
> +
> + can_tx_msg = virtqueue_get_buf(vq, &len);
> + if (!can_tx_msg) {
> + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> + return 0; /* No more data */
> + }
> +
> + 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) {
> + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> + * echoed. Intentional to bring a waiting process in an upper
> + * layer to an end.
> + * TODO: Any better means to indicate a problem here?
> + */
> + if (result != VIRTIO_CAN_RESULT_OK)
> + netdev_warn(dev, "TX ACK: Result = %u\n", result);
Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
For sure, counting the known errors as valid tx_packets and tx_bytes
is misleading.
> +
> + 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);
> + }
> +
> + list_del(&can_tx_msg->list);
> + 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);
> + }
> +
> + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> +
> + kfree(can_tx_msg);
> +
> + return 1; /* Queue was not empty so there may be more data */
> +}
> +
[...]
> +
> +static int virtio_can_find_vqs(struct virtio_can_priv *priv)
> +{
> + /* The order of RX and TX is exactly the opposite as in console and
> + * network. Does not play any role but is a bad trap.
> + */
> + static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
> + "can-tx",
> + "can-rx",
> + "can-state-ctrl"
> + };
> +
> + priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
> + priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
> + priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
> +
> + /* Find the queues. */
> + return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
> + priv->io_callbacks, io_names, NULL);
> +}
Syntax of virtio_find_vqs changed a bit, here should now be:
struct virtqueue_info vqs_info[] = {
{ "can-tx", virtio_can_tx_intr },
{ "can-rx", virtio_can_rx_intr },
{ "can-state-ctrl", virtio_can_control_intr },
};
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 list_head *cursor, *next;
> + struct virtqueue *vq;
> +
> + /* Reset the device */
> + if (vdev->config->reset)
> + vdev->config->reset(vdev);
> +
> + /* From here we have dead silence from the device side so no locks
> + * are needed to protect against device side events.
> + */
> +
> + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> + while (virtqueue_detach_unused_buf(vq))
> + ; /* Do nothing, content allocated statically */
> +
> + vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
> + while (virtqueue_detach_unused_buf(vq))
> + ; /* Do nothing, content allocated statically */
> +
> + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> + while (virtqueue_detach_unused_buf(vq))
> + ; /* Do nothing, content to be de-allocated separately */
> +
> + /* Is keeping track of allocated elements by an own linked list
> + * really necessary or may this be optimized using only
> + * virtqueue_detach_unused_buf()?
> + */
> + list_for_each_safe(cursor, next, &priv->tx_list) {
> + struct virtio_can_tx *can_tx;
> +
> + can_tx = list_entry(cursor, struct virtio_can_tx, list);
> + list_del(cursor);
> + kfree(can_tx);
> + }
I'd drop the tx_list entirely and rely on virtqueue_detach_unused_buf();
this would allow to remove at least one spinlock save/restore pair at
each transmission.
> +
> + if (vdev->config->del_vqs)
> + vdev->config->del_vqs(vdev);
> +}
> +
[...]
> diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
> new file mode 100644
> index 000000000000..7cf613bb3f1a
> --- /dev/null
> +++ b/include/uapi/linux/virtio_can.h
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +/*
> + * Copyright (C) 2021-2023 OpenSynergy GmbH
> + */
> +#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_LATE_TX_ACK 2
> +#define VIRTIO_CAN_F_RTR_FRAMES 3
> +
The values for VIRTIO_CAN_F_LATE_TX_ACK and VIRTIO_CAN_F_RTR_FRAMES are
inverted w.r.t. the merged virto-can spec [1].
Note that this is the only deviation from the spec I found.
> +/* 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
> +
> +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[64];
> +};
> +
sdu[] here might be a flexible array, if the driver allocates
virtio_can_tx_out structs dyncamically (see above). This would be
beneficial in case of CAN-XL frames (if/when they will be supported).
> +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[64];
> +};
> +
Again, sdu[] might be a flexible array.
> +/* 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 */
>
Thank you for your work!
Regards,
Francesco
[1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L45
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-09-11 20:59 ` Francesco Valla
@ 2025-10-01 15:23 ` Matias Ezequiel Vara Larsen
2025-10-10 15:46 ` Matias Ezequiel Vara Larsen
` (3 subsequent siblings)
4 siblings, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-01 15:23 UTC (permalink / raw)
To: David S. Miller
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development, francesco
On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> Hello Mikhail, Harald,
>
> hoping there will be a v6 of this patch soon, a few comments:
>
I am thinking to send a v6 that addresses all the comments soon.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-09-11 20:59 ` Francesco Valla
2025-10-01 15:23 ` Matias Ezequiel Vara Larsen
@ 2025-10-10 15:46 ` Matias Ezequiel Vara Larsen
2025-10-10 21:20 ` Francesco Valla
2025-10-13 16:35 ` Matias Ezequiel Vara Larsen
` (2 subsequent siblings)
4 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-10 15:46 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> Hello Mikhail, Harald,
>
> hoping there will be a v6 of this patch soon, a few comments:
>
I am working on the v6 by addressing the comments in this thread.
> On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
>
> [...]
>
> > +
> > +/* 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];
> > + /* I/O callback function pointers for the virtqueues */
> > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > + /* Lock for TX operations */
> > + spinlock_t tx_lock;
> > + /* Control queue lock. Defensive programming, may be not needed */
> > + struct mutex ctrl_lock;
> > + /* Wait for control queue processing without polling */
> > + struct completion ctrl_done;
> > + /* List of virtio CAN TX message */
> > + struct list_head tx_list;
> > + /* Array of receive queue messages */
> > + struct virtio_can_rx rpkt[128];
>
> This array should probably be allocated dynamically at probe - maybe
> using a module parameter instead of a hardcoded value as length?
>
If I allocate this array in probe(), I would not know sdu[] in advance
if I defined it as a flexible array. That made me wonder: can sdu[] be
defined as flexible array for rx?
Thanks.
> > + /* Those control queue messages cannot live on the stack! */
> > + struct virtio_can_control_out cpkt_out;
> > + struct virtio_can_control_in cpkt_in;
>
> Consider using a container struct as you did for the tx message, e.g.:
>
> struct virtio_can_control {
> struct virtio_can_control_out ctrl_out;
> struct virtio_can_control_in ctrl_in;
> };
>
> > + /* Data to get and maintain the putidx for local TX echo */
> > + struct ida tx_putidx_ida;
> > + /* In flight TX messages */
> > + atomic_t tx_inflight;
> > + /* BusOff pending. Reset after successful indication to upper layer */
> > + bool busoff_pending;
> > +};
> > +
>
> [...]
>
> > +
> > +/* Send a control message with message type either
> > + *
> > + * - VIRTIO_CAN_SET_CTRL_MODE_START or
> > + * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
> > + *
> > + * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
> > + * for this Linux driver to have an asynchronous implementation of the mode
> > + * setting function so in order to keep things simple the function is
> > + * implemented as synchronous function. Design pattern is
> > + * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
> > + */
> > +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 device *dev = &priv->vdev->dev;
> > + struct virtqueue *vq;
> > + unsigned int len;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > +
> > + /* The function may be serialized by rtnl lock. Not sure.
> > + * Better safe than sorry.
> > + */
> > + mutex_lock(&priv->ctrl_lock);
> > +
> > + priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
> > + sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
> > + sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
> > +
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
> > + if (err != 0) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
> > + }
>
> Here it should return VIRTIO_CAN_RESULT_NOT_OK after unlocking the
> mutex, or it might wait for completion indefinitley below.
>
> > +
> > + if (!virtqueue_kick(vq)) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): Kick failed\n", __func__);
> > + }
>
> And here too.
>
> > +
> > + while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
> > + wait_for_completion(&priv->ctrl_done);
> > +
> > + mutex_unlock(&priv->ctrl_lock);
> > +
> > + return priv->cpkt_in.result;
> > +}
> > +
>
> [...]
>
> > +static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
> > + struct net_device *dev)
> > +{
> > + const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
> > + struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
> > + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> > + struct virtio_can_priv *priv = netdev_priv(dev);
> > + netdev_tx_t xmit_ret = NETDEV_TX_OK;
> > + struct virtio_can_tx *can_tx_msg;
> > + struct virtqueue *vq;
> > + unsigned long flags;
> > + u32 can_flags;
> > + int putidx;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > +
> > + 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), GFP_ATOMIC);
> > + if (!can_tx_msg) {
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
>
> Since we are allocating tx messages dynamically, the sdu[64] array inside
> struct virtio_can_tx_out can be converted to a flexible array and here
> the allocation can become:
>
> can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
>
> This would save memory in particular on CAN-CC interfaces, where 56 bytes
> per message would otherwise be lost (not to mention the case if/when
> CAN-XL will be supported).
>
> > + can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
> > + 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);
> > + can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
> > +
> > + /* Prepare sending of virtio message */
> > + 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);
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
> > + can_tx_msg->putidx = (unsigned int)putidx;
> > +
> > + /* Protect list operation */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + list_add_tail(&can_tx_msg->list, &priv->tx_list);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > + /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
> > + can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
> > +
> > + /* Protect queue and list operations */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
> > + if (unlikely(err)) { /* checking vq->num_free in flow control */
> > + list_del(&can_tx_msg->list);
> > + can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
> > + virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > + netif_stop_queue(dev);
> > + kfree(can_tx_msg);
> > + /* Expected never to be seen */
> > + netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
> > + xmit_ret = NETDEV_TX_BUSY;
> > + 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");
> > + }
> > +
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > +kick:
> > + if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
> > + 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,
> > + .ndo_change_mtu = can_change_mtu,
> > +};
> > +
> > +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);
> > +}
> > +
> > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > +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 long flags;
> > + unsigned int len;
> > + u8 result;
> > +
> > + stats = &dev->stats;
> > +
> > + /* Protect list and virtio queue operations */
> > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > +
> > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > + if (!can_tx_msg) {
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > + return 0; /* No more data */
> > + }
> > +
> > + 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) {
> > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > + * echoed. Intentional to bring a waiting process in an upper
> > + * layer to an end.
> > + * TODO: Any better means to indicate a problem here?
> > + */
> > + if (result != VIRTIO_CAN_RESULT_OK)
> > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
>
> Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
>
> For sure, counting the known errors as valid tx_packets and tx_bytes
> is misleading.
>
> > +
> > + 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);
> > + }
> > +
> > + list_del(&can_tx_msg->list);
> > + 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);
> > + }
> > +
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > +
> > + kfree(can_tx_msg);
> > +
> > + return 1; /* Queue was not empty so there may be more data */
> > +}
> > +
>
> [...]
>
> > +
> > +static int virtio_can_find_vqs(struct virtio_can_priv *priv)
> > +{
> > + /* The order of RX and TX is exactly the opposite as in console and
> > + * network. Does not play any role but is a bad trap.
> > + */
> > + static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
> > + "can-tx",
> > + "can-rx",
> > + "can-state-ctrl"
> > + };
> > +
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
> > +
> > + /* Find the queues. */
> > + return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
> > + priv->io_callbacks, io_names, NULL);
> > +}
>
> Syntax of virtio_find_vqs changed a bit, here should now be:
>
> struct virtqueue_info vqs_info[] = {
> { "can-tx", virtio_can_tx_intr },
> { "can-rx", virtio_can_rx_intr },
> { "can-state-ctrl", virtio_can_control_intr },
> };
>
> 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 list_head *cursor, *next;
> > + struct virtqueue *vq;
> > +
> > + /* Reset the device */
> > + if (vdev->config->reset)
> > + vdev->config->reset(vdev);
> > +
> > + /* From here we have dead silence from the device side so no locks
> > + * are needed to protect against device side events.
> > + */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content to be de-allocated separately */
> > +
> > + /* Is keeping track of allocated elements by an own linked list
> > + * really necessary or may this be optimized using only
> > + * virtqueue_detach_unused_buf()?
> > + */
> > + list_for_each_safe(cursor, next, &priv->tx_list) {
> > + struct virtio_can_tx *can_tx;
> > +
> > + can_tx = list_entry(cursor, struct virtio_can_tx, list);
> > + list_del(cursor);
> > + kfree(can_tx);
> > + }
>
> I'd drop the tx_list entirely and rely on virtqueue_detach_unused_buf();
> this would allow to remove at least one spinlock save/restore pair at
> each transmission.
>
> > +
> > + if (vdev->config->del_vqs)
> > + vdev->config->del_vqs(vdev);
> > +}
> > +
>
> [...]
>
> > diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
> > new file mode 100644
> > index 000000000000..7cf613bb3f1a
> > --- /dev/null
> > +++ b/include/uapi/linux/virtio_can.h
> > @@ -0,0 +1,75 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause */
> > +/*
> > + * Copyright (C) 2021-2023 OpenSynergy GmbH
> > + */
> > +#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_LATE_TX_ACK 2
> > +#define VIRTIO_CAN_F_RTR_FRAMES 3
> > +
>
> The values for VIRTIO_CAN_F_LATE_TX_ACK and VIRTIO_CAN_F_RTR_FRAMES are
> inverted w.r.t. the merged virto-can spec [1].
>
> Note that this is the only deviation from the spec I found.
>
> > +/* 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
> > +
> > +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[64];
> > +};
> > +
>
> sdu[] here might be a flexible array, if the driver allocates
> virtio_can_tx_out structs dyncamically (see above). This would be
> beneficial in case of CAN-XL frames (if/when they will be supported).
>
> > +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[64];
> > +};
> > +
>
> Again, sdu[] might be a flexible array.
>
> > +/* 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 */
> >
>
> Thank you for your work!
>
> Regards,
> Francesco
>
>
> [1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L45
>
>
>
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-10 15:46 ` Matias Ezequiel Vara Larsen
@ 2025-10-10 21:20 ` Francesco Valla
2025-10-13 9:52 ` Matias Ezequiel Vara Larsen
2025-10-13 14:15 ` Matias Ezequiel Vara Larsen
0 siblings, 2 replies; 30+ messages in thread
From: Francesco Valla @ 2025-10-10 21:20 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Friday, 10 October 2025 at 17:46:25 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > Hello Mikhail, Harald,
> >
> > hoping there will be a v6 of this patch soon, a few comments:
> >
>
> I am working on the v6 by addressing the comments in this thread.
>
> > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> >
> > [...]
> >
> > > +
> > > +/* 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];
> > > + /* I/O callback function pointers for the virtqueues */
> > > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > > + /* Lock for TX operations */
> > > + spinlock_t tx_lock;
> > > + /* Control queue lock. Defensive programming, may be not needed */
> > > + struct mutex ctrl_lock;
> > > + /* Wait for control queue processing without polling */
> > > + struct completion ctrl_done;
> > > + /* List of virtio CAN TX message */
> > > + struct list_head tx_list;
> > > + /* Array of receive queue messages */
> > > + struct virtio_can_rx rpkt[128];
> >
> > This array should probably be allocated dynamically at probe - maybe
> > using a module parameter instead of a hardcoded value as length?
> >
>
> If I allocate this array in probe(), I would not know sdu[] in advance
> if I defined it as a flexible array. That made me wonder: can sdu[] be
> defined as flexible array for rx?
>
> Thanks.
>
One thing that can be done is to define struct virtio_can_rx as:
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(length);
};
and then allocate the rpkt[] array using the maximum length for SDU:
priv->rpkt = kcalloc(num_rx_buffers,
sizeof(struct virtio_can_rx) + VIRTIO_CAN_MAX_DLEN,
GFP_KERNEL);
In this way, the size of each member of rpkt[] is known and is thus
suitable for virtio_can_populate_vqs().
Regards,
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-10 21:20 ` Francesco Valla
@ 2025-10-13 9:52 ` Matias Ezequiel Vara Larsen
2025-10-13 16:29 ` Francesco Valla
2025-10-13 14:15 ` Matias Ezequiel Vara Larsen
1 sibling, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-13 9:52 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Fri, Oct 10, 2025 at 11:20:22PM +0200, Francesco Valla wrote:
> On Friday, 10 October 2025 at 17:46:25 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > Hello Mikhail, Harald,
> > >
> > > hoping there will be a v6 of this patch soon, a few comments:
> > >
> >
> > I am working on the v6 by addressing the comments in this thread.
> >
> > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > >
> > > [...]
> > >
> > > > +
> > > > +/* 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];
> > > > + /* I/O callback function pointers for the virtqueues */
> > > > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > > > + /* Lock for TX operations */
> > > > + spinlock_t tx_lock;
> > > > + /* Control queue lock. Defensive programming, may be not needed */
> > > > + struct mutex ctrl_lock;
> > > > + /* Wait for control queue processing without polling */
> > > > + struct completion ctrl_done;
> > > > + /* List of virtio CAN TX message */
> > > > + struct list_head tx_list;
> > > > + /* Array of receive queue messages */
> > > > + struct virtio_can_rx rpkt[128];
> > >
> > > This array should probably be allocated dynamically at probe - maybe
> > > using a module parameter instead of a hardcoded value as length?
> > >
> >
> > If I allocate this array in probe(), I would not know sdu[] in advance
> > if I defined it as a flexible array. That made me wonder: can sdu[] be
> > defined as flexible array for rx?
> >
> > Thanks.
> >
>
> One thing that can be done is to define struct virtio_can_rx as:
>
> 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(length);
> };
>
> and then allocate the rpkt[] array using the maximum length for SDU:
>
> priv->rpkt = kcalloc(num_rx_buffers,
> sizeof(struct virtio_can_rx) + VIRTIO_CAN_MAX_DLEN,
> GFP_KERNEL);
>
> In this way, the size of each member of rpkt[] is known and is thus
> suitable for virtio_can_populate_vqs().
>
>
Thanks for your answer. What is the value of VIRTIO_CAN_MAX_DLEN? I
can't find it nor in the code or in the spec. I guess is 64 bytes? Also,
IIUC, using __counted_by() would not end up saving space but adding an
extra check for the compiler. Am I right? In that case, can't I just use
a fixed array of VIRTIO_CAN_MAX_DLEN bytes?
Thanks, Matias.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-10 21:20 ` Francesco Valla
2025-10-13 9:52 ` Matias Ezequiel Vara Larsen
@ 2025-10-13 14:15 ` Matias Ezequiel Vara Larsen
1 sibling, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-13 14:15 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Fri, Oct 10, 2025 at 11:20:22PM +0200, Francesco Valla wrote:
> On Friday, 10 October 2025 at 17:46:25 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > Hello Mikhail, Harald,
> > >
> > > hoping there will be a v6 of this patch soon, a few comments:
> > >
> >
> > I am working on the v6 by addressing the comments in this thread.
> >
> > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > >
> > > [...]
> > >
> > > > +
> > > > +/* 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];
> > > > + /* I/O callback function pointers for the virtqueues */
> > > > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > > > + /* Lock for TX operations */
> > > > + spinlock_t tx_lock;
> > > > + /* Control queue lock. Defensive programming, may be not needed */
> > > > + struct mutex ctrl_lock;
> > > > + /* Wait for control queue processing without polling */
> > > > + struct completion ctrl_done;
> > > > + /* List of virtio CAN TX message */
> > > > + struct list_head tx_list;
> > > > + /* Array of receive queue messages */
> > > > + struct virtio_can_rx rpkt[128];
> > >
> > > This array should probably be allocated dynamically at probe - maybe
> > > using a module parameter instead of a hardcoded value as length?
> > >
> >
> > If I allocate this array in probe(), I would not know sdu[] in advance
> > if I defined it as a flexible array. That made me wonder: can sdu[] be
> > defined as flexible array for rx?
> >
> > Thanks.
> >
>
> One thing that can be done is to define struct virtio_can_rx as:
>
> 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(length);
> };
>
> and then allocate the rpkt[] array using the maximum length for SDU:
>
> priv->rpkt = kcalloc(num_rx_buffers,
> sizeof(struct virtio_can_rx) + VIRTIO_CAN_MAX_DLEN,
> GFP_KERNEL);
>
> In this way, the size of each member of rpkt[] is known and is thus
> suitable for virtio_can_populate_vqs().
>
>
From the spec, VIRTIO_CAN_MAX_DLEN shall be 2048 bytes that corresponds
with CAN-XL frame.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-13 9:52 ` Matias Ezequiel Vara Larsen
@ 2025-10-13 16:29 ` Francesco Valla
2025-10-13 18:07 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Francesco Valla @ 2025-10-13 16:29 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
Hello Matias,
On Monday, 13 October 2025 at 11:52:49 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Fri, Oct 10, 2025 at 11:20:22PM +0200, Francesco Valla wrote:
> > On Friday, 10 October 2025 at 17:46:25 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > Hello Mikhail, Harald,
> > > >
> > > > hoping there will be a v6 of this patch soon, a few comments:
> > > >
> > >
> > > I am working on the v6 by addressing the comments in this thread.
> > >
> > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > >
> > > > [...]
> > > >
> > > > > +
> > > > > +/* 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];
> > > > > + /* I/O callback function pointers for the virtqueues */
> > > > > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > > > > + /* Lock for TX operations */
> > > > > + spinlock_t tx_lock;
> > > > > + /* Control queue lock. Defensive programming, may be not needed */
> > > > > + struct mutex ctrl_lock;
> > > > > + /* Wait for control queue processing without polling */
> > > > > + struct completion ctrl_done;
> > > > > + /* List of virtio CAN TX message */
> > > > > + struct list_head tx_list;
> > > > > + /* Array of receive queue messages */
> > > > > + struct virtio_can_rx rpkt[128];
> > > >
> > > > This array should probably be allocated dynamically at probe - maybe
> > > > using a module parameter instead of a hardcoded value as length?
> > > >
> > >
> > > If I allocate this array in probe(), I would not know sdu[] in advance
> > > if I defined it as a flexible array. That made me wonder: can sdu[] be
> > > defined as flexible array for rx?
> > >
> > > Thanks.
> > >
> >
> > One thing that can be done is to define struct virtio_can_rx as:
> >
> > 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(length);
> > };
> >
> > and then allocate the rpkt[] array using the maximum length for SDU:
> >
> > priv->rpkt = kcalloc(num_rx_buffers,
> > sizeof(struct virtio_can_rx) + VIRTIO_CAN_MAX_DLEN,
> > GFP_KERNEL);
> >
> > In this way, the size of each member of rpkt[] is known and is thus
> > suitable for virtio_can_populate_vqs().
> >
> >
>
> Thanks for your answer. What is the value of VIRTIO_CAN_MAX_DLEN? I
> can't find it nor in the code or in the spec. I guess is 64 bytes? Also,
> IIUC, using __counted_by() would not end up saving space but adding an
> extra check for the compiler. Am I right? In that case, can't I just use
> a fixed array of VIRTIO_CAN_MAX_DLEN bytes?
My bad, I forgot to say that VIRTIO_CAN_MAX_DLEN has to be defined, but:
given some more thoughts, maybe this can be a dynamic value based on
the features received from the virtio framework, to avoid wasting memory?
E.g.:
if (virtio_has_feature(VIRTIO_CAN_F_CAN_FD))
sdu_len = CANFD_MAX_DLEN;
else
sdu_len = CAN_MAX_DLEN;
priv->rpkt = kcalloc(num_rx_buffers, sizeof(struct virtio_can_rx) + sdu_len,
GFP_KERNEL);
My understanding of __counted_by() is the same: additional checks but nothing
more.
CAN-XL appears to be not supported by the virtio specs v1.4 [1], but if/when
it will be, the addition of an additional case would be simple.
[1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L33
>
> Thanks, Matias.
>
>
Many thanks to you for your effort!
Regards,
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-09-11 20:59 ` Francesco Valla
2025-10-01 15:23 ` Matias Ezequiel Vara Larsen
2025-10-10 15:46 ` Matias Ezequiel Vara Larsen
@ 2025-10-13 16:35 ` Matias Ezequiel Vara Larsen
2025-10-14 6:54 ` Francesco Valla
2025-10-14 10:15 ` Matias Ezequiel Vara Larsen
2025-10-14 10:25 ` Matias Ezequiel Vara Larsen
4 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-13 16:35 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> Hello Mikhail, Harald,
>
> hoping there will be a v6 of this patch soon, a few comments:
>
> On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
>
> [...]
>
> > +
> > +/* 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];
> > + /* I/O callback function pointers for the virtqueues */
> > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > + /* Lock for TX operations */
> > + spinlock_t tx_lock;
> > + /* Control queue lock. Defensive programming, may be not needed */
> > + struct mutex ctrl_lock;
> > + /* Wait for control queue processing without polling */
> > + struct completion ctrl_done;
> > + /* List of virtio CAN TX message */
> > + struct list_head tx_list;
> > + /* Array of receive queue messages */
> > + struct virtio_can_rx rpkt[128];
>
> This array should probably be allocated dynamically at probe - maybe
> using a module parameter instead of a hardcoded value as length?
>
> > + /* Those control queue messages cannot live on the stack! */
> > + struct virtio_can_control_out cpkt_out;
> > + struct virtio_can_control_in cpkt_in;
>
> Consider using a container struct as you did for the tx message, e.g.:
>
> struct virtio_can_control {
> struct virtio_can_control_out ctrl_out;
> struct virtio_can_control_in ctrl_in;
> };
>
> > + /* Data to get and maintain the putidx for local TX echo */
> > + struct ida tx_putidx_ida;
> > + /* In flight TX messages */
> > + atomic_t tx_inflight;
> > + /* BusOff pending. Reset after successful indication to upper layer */
> > + bool busoff_pending;
> > +};
> > +
>
> [...]
>
> > +
> > +/* Send a control message with message type either
> > + *
> > + * - VIRTIO_CAN_SET_CTRL_MODE_START or
> > + * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
> > + *
> > + * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
> > + * for this Linux driver to have an asynchronous implementation of the mode
> > + * setting function so in order to keep things simple the function is
> > + * implemented as synchronous function. Design pattern is
> > + * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
> > + */
> > +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 device *dev = &priv->vdev->dev;
> > + struct virtqueue *vq;
> > + unsigned int len;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > +
> > + /* The function may be serialized by rtnl lock. Not sure.
> > + * Better safe than sorry.
> > + */
> > + mutex_lock(&priv->ctrl_lock);
> > +
> > + priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
> > + sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
> > + sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
> > +
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
> > + if (err != 0) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
> > + }
>
> Here it should return VIRTIO_CAN_RESULT_NOT_OK after unlocking the
> mutex, or it might wait for completion indefinitley below.
>
> > +
> > + if (!virtqueue_kick(vq)) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): Kick failed\n", __func__);
> > + }
>
> And here too.
>
> > +
> > + while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
> > + wait_for_completion(&priv->ctrl_done);
> > +
> > + mutex_unlock(&priv->ctrl_lock);
> > +
> > + return priv->cpkt_in.result;
> > +}
> > +
>
> [...]
>
> > +static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
> > + struct net_device *dev)
> > +{
> > + const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
> > + struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
> > + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> > + struct virtio_can_priv *priv = netdev_priv(dev);
> > + netdev_tx_t xmit_ret = NETDEV_TX_OK;
> > + struct virtio_can_tx *can_tx_msg;
> > + struct virtqueue *vq;
> > + unsigned long flags;
> > + u32 can_flags;
> > + int putidx;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > +
> > + 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), GFP_ATOMIC);
> > + if (!can_tx_msg) {
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
>
> Since we are allocating tx messages dynamically, the sdu[64] array inside
> struct virtio_can_tx_out can be converted to a flexible array and here
> the allocation can become:
>
> can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
>
> This would save memory in particular on CAN-CC interfaces, where 56 bytes
> per message would otherwise be lost (not to mention the case if/when
> CAN-XL will be supported).
>
> > + can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
> > + 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);
> > + can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
> > +
> > + /* Prepare sending of virtio message */
> > + 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);
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
> > + can_tx_msg->putidx = (unsigned int)putidx;
> > +
> > + /* Protect list operation */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + list_add_tail(&can_tx_msg->list, &priv->tx_list);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > + /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
> > + can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
> > +
> > + /* Protect queue and list operations */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
> > + if (unlikely(err)) { /* checking vq->num_free in flow control */
> > + list_del(&can_tx_msg->list);
> > + can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
> > + virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > + netif_stop_queue(dev);
> > + kfree(can_tx_msg);
> > + /* Expected never to be seen */
> > + netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
> > + xmit_ret = NETDEV_TX_BUSY;
> > + 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");
> > + }
> > +
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > +kick:
> > + if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
> > + 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,
> > + .ndo_change_mtu = can_change_mtu,
> > +};
> > +
> > +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);
> > +}
> > +
> > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > +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 long flags;
> > + unsigned int len;
> > + u8 result;
> > +
> > + stats = &dev->stats;
> > +
> > + /* Protect list and virtio queue operations */
> > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > +
> > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > + if (!can_tx_msg) {
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > + return 0; /* No more data */
> > + }
> > +
> > + 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) {
> > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > + * echoed. Intentional to bring a waiting process in an upper
> > + * layer to an end.
> > + * TODO: Any better means to indicate a problem here?
> > + */
> > + if (result != VIRTIO_CAN_RESULT_OK)
> > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
>
> Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
>
> For sure, counting the known errors as valid tx_packets and tx_bytes
> is misleading.
>
> > +
> > + 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);
> > + }
> > +
> > + list_del(&can_tx_msg->list);
> > + 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);
> > + }
> > +
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > +
> > + kfree(can_tx_msg);
> > +
> > + return 1; /* Queue was not empty so there may be more data */
> > +}
> > +
>
> [...]
>
> > +
> > +static int virtio_can_find_vqs(struct virtio_can_priv *priv)
> > +{
> > + /* The order of RX and TX is exactly the opposite as in console and
> > + * network. Does not play any role but is a bad trap.
> > + */
> > + static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
> > + "can-tx",
> > + "can-rx",
> > + "can-state-ctrl"
> > + };
> > +
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
> > +
> > + /* Find the queues. */
> > + return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
> > + priv->io_callbacks, io_names, NULL);
> > +}
>
> Syntax of virtio_find_vqs changed a bit, here should now be:
>
> struct virtqueue_info vqs_info[] = {
> { "can-tx", virtio_can_tx_intr },
> { "can-rx", virtio_can_rx_intr },
> { "can-state-ctrl", virtio_can_control_intr },
> };
>
> 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 list_head *cursor, *next;
> > + struct virtqueue *vq;
> > +
> > + /* Reset the device */
> > + if (vdev->config->reset)
> > + vdev->config->reset(vdev);
> > +
> > + /* From here we have dead silence from the device side so no locks
> > + * are needed to protect against device side events.
> > + */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content to be de-allocated separately */
> > +
> > + /* Is keeping track of allocated elements by an own linked list
> > + * really necessary or may this be optimized using only
> > + * virtqueue_detach_unused_buf()?
> > + */
> > + list_for_each_safe(cursor, next, &priv->tx_list) {
> > + struct virtio_can_tx *can_tx;
> > +
> > + can_tx = list_entry(cursor, struct virtio_can_tx, list);
> > + list_del(cursor);
> > + kfree(can_tx);
> > + }
>
> I'd drop the tx_list entirely and rely on virtqueue_detach_unused_buf();
> this would allow to remove at least one spinlock save/restore pair at
> each transmission.
>
> > +
> > + if (vdev->config->del_vqs)
> > + vdev->config->del_vqs(vdev);
> > +}
> > +
>
> [...]
>
> > diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
> > new file mode 100644
> > index 000000000000..7cf613bb3f1a
> > --- /dev/null
> > +++ b/include/uapi/linux/virtio_can.h
> > @@ -0,0 +1,75 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause */
> > +/*
> > + * Copyright (C) 2021-2023 OpenSynergy GmbH
> > + */
> > +#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_LATE_TX_ACK 2
> > +#define VIRTIO_CAN_F_RTR_FRAMES 3
> > +
>
> The values for VIRTIO_CAN_F_LATE_TX_ACK and VIRTIO_CAN_F_RTR_FRAMES are
> inverted w.r.t. the merged virto-can spec [1].
>
> Note that this is the only deviation from the spec I found.
>
> > +/* 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
> > +
> > +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[64];
> > +};
> > +
>
> sdu[] here might be a flexible array, if the driver allocates
> virtio_can_tx_out structs dyncamically (see above). This would be
> beneficial in case of CAN-XL frames (if/when they will be supported).
>
If we use a flexible array for sdu[] here, then we will have a problem
when defining the virtio_can_tx struct since it is not in the end of the
structure. I think it is a good idea to define it as a flexible array
but I do not know how.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-13 16:29 ` Francesco Valla
@ 2025-10-13 18:07 ` Matias Ezequiel Vara Larsen
0 siblings, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-13 18:07 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Mon, Oct 13, 2025 at 06:29:44PM +0200, Francesco Valla wrote:
> Hello Matias,
>
> On Monday, 13 October 2025 at 11:52:49 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Fri, Oct 10, 2025 at 11:20:22PM +0200, Francesco Valla wrote:
> > > On Friday, 10 October 2025 at 17:46:25 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > > Hello Mikhail, Harald,
> > > > >
> > > > > hoping there will be a v6 of this patch soon, a few comments:
> > > > >
> > > >
> > > > I am working on the v6 by addressing the comments in this thread.
> > > >
> > > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > > >
> > > > > [...]
> > > > >
> > > > > > +
> > > > > > +/* 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];
> > > > > > + /* I/O callback function pointers for the virtqueues */
> > > > > > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > > > > > + /* Lock for TX operations */
> > > > > > + spinlock_t tx_lock;
> > > > > > + /* Control queue lock. Defensive programming, may be not needed */
> > > > > > + struct mutex ctrl_lock;
> > > > > > + /* Wait for control queue processing without polling */
> > > > > > + struct completion ctrl_done;
> > > > > > + /* List of virtio CAN TX message */
> > > > > > + struct list_head tx_list;
> > > > > > + /* Array of receive queue messages */
> > > > > > + struct virtio_can_rx rpkt[128];
> > > > >
> > > > > This array should probably be allocated dynamically at probe - maybe
> > > > > using a module parameter instead of a hardcoded value as length?
> > > > >
> > > >
> > > > If I allocate this array in probe(), I would not know sdu[] in advance
> > > > if I defined it as a flexible array. That made me wonder: can sdu[] be
> > > > defined as flexible array for rx?
> > > >
> > > > Thanks.
> > > >
> > >
> > > One thing that can be done is to define struct virtio_can_rx as:
> > >
> > > 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(length);
> > > };
> > >
> > > and then allocate the rpkt[] array using the maximum length for SDU:
> > >
> > > priv->rpkt = kcalloc(num_rx_buffers,
> > > sizeof(struct virtio_can_rx) + VIRTIO_CAN_MAX_DLEN,
> > > GFP_KERNEL);
> > >
> > > In this way, the size of each member of rpkt[] is known and is thus
> > > suitable for virtio_can_populate_vqs().
> > >
> > >
> >
> > Thanks for your answer. What is the value of VIRTIO_CAN_MAX_DLEN? I
> > can't find it nor in the code or in the spec. I guess is 64 bytes? Also,
> > IIUC, using __counted_by() would not end up saving space but adding an
> > extra check for the compiler. Am I right? In that case, can't I just use
> > a fixed array of VIRTIO_CAN_MAX_DLEN bytes?
>
> My bad, I forgot to say that VIRTIO_CAN_MAX_DLEN has to be defined, but:
> given some more thoughts, maybe this can be a dynamic value based on
> the features received from the virtio framework, to avoid wasting memory?
>
> E.g.:
>
> if (virtio_has_feature(VIRTIO_CAN_F_CAN_FD))
> sdu_len = CANFD_MAX_DLEN;
> else
> sdu_len = CAN_MAX_DLEN;
>
> priv->rpkt = kcalloc(num_rx_buffers, sizeof(struct virtio_can_rx) + sdu_len,
> GFP_KERNEL);
>
>
> My understanding of __counted_by() is the same: additional checks but nothing
> more.
>
>
> CAN-XL appears to be not supported by the virtio specs v1.4 [1], but if/when
> it will be, the addition of an additional case would be simple.
>
> [1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L33
>
Sounds good, I'll add that.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-13 16:35 ` Matias Ezequiel Vara Larsen
@ 2025-10-14 6:54 ` Francesco Valla
2025-10-14 10:42 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Francesco Valla @ 2025-10-14 6:54 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Monday, 13 October 2025 at 18:35:51 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > [...]
> >
> > > +
> > > +/* 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[64];
> > > +};
> > > +
> >
> > sdu[] here might be a flexible array, if the driver allocates
> > virtio_can_tx_out structs dyncamically (see above). This would be
> > beneficial in case of CAN-XL frames (if/when they will be supported).
> >
>
> If we use a flexible array for sdu[] here, then we will have a problem
> when defining the virtio_can_tx struct since it is not in the end of the
> structure. I think it is a good idea to define it as a flexible array
> but I do not know how.
In this case, I'd move struct virtio_can_tx_out at the end of the
virtio_can_tx struct - in this way, sdu[] would be at the end:
struct virtio_can_tx {
struct list_head list;
unsigned int putidx;
struct virtio_can_tx_in tx_in;
struct virtio_can_tx_out tx_out;
};
Maybe an additional comment declaring why it is done this way would
be a good idea? Also considering that the two structures are defined
in different files.
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-09-11 20:59 ` Francesco Valla
` (2 preceding siblings ...)
2025-10-13 16:35 ` Matias Ezequiel Vara Larsen
@ 2025-10-14 10:15 ` Matias Ezequiel Vara Larsen
2025-10-14 16:01 ` Francesco Valla
2025-10-14 10:25 ` Matias Ezequiel Vara Larsen
4 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-14 10:15 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> Hello Mikhail, Harald,
>
> hoping there will be a v6 of this patch soon, a few comments:
>
> On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
>
> [...]
>
> > +
> > +/* 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];
> > + /* I/O callback function pointers for the virtqueues */
> > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > + /* Lock for TX operations */
> > + spinlock_t tx_lock;
> > + /* Control queue lock. Defensive programming, may be not needed */
> > + struct mutex ctrl_lock;
> > + /* Wait for control queue processing without polling */
> > + struct completion ctrl_done;
> > + /* List of virtio CAN TX message */
> > + struct list_head tx_list;
> > + /* Array of receive queue messages */
> > + struct virtio_can_rx rpkt[128];
>
> This array should probably be allocated dynamically at probe - maybe
> using a module parameter instead of a hardcoded value as length?
>
> > + /* Those control queue messages cannot live on the stack! */
> > + struct virtio_can_control_out cpkt_out;
> > + struct virtio_can_control_in cpkt_in;
>
> Consider using a container struct as you did for the tx message, e.g.:
>
> struct virtio_can_control {
> struct virtio_can_control_out ctrl_out;
> struct virtio_can_control_in ctrl_in;
> };
>
> > + /* Data to get and maintain the putidx for local TX echo */
> > + struct ida tx_putidx_ida;
> > + /* In flight TX messages */
> > + atomic_t tx_inflight;
> > + /* BusOff pending. Reset after successful indication to upper layer */
> > + bool busoff_pending;
> > +};
> > +
>
> [...]
>
> > +
> > +/* Send a control message with message type either
> > + *
> > + * - VIRTIO_CAN_SET_CTRL_MODE_START or
> > + * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
> > + *
> > + * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
> > + * for this Linux driver to have an asynchronous implementation of the mode
> > + * setting function so in order to keep things simple the function is
> > + * implemented as synchronous function. Design pattern is
> > + * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
> > + */
> > +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 device *dev = &priv->vdev->dev;
> > + struct virtqueue *vq;
> > + unsigned int len;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > +
> > + /* The function may be serialized by rtnl lock. Not sure.
> > + * Better safe than sorry.
> > + */
> > + mutex_lock(&priv->ctrl_lock);
> > +
> > + priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
> > + sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
> > + sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
> > +
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
> > + if (err != 0) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
> > + }
>
> Here it should return VIRTIO_CAN_RESULT_NOT_OK after unlocking the
> mutex, or it might wait for completion indefinitley below.
>
> > +
> > + if (!virtqueue_kick(vq)) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): Kick failed\n", __func__);
> > + }
>
> And here too.
>
> > +
> > + while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
> > + wait_for_completion(&priv->ctrl_done);
> > +
> > + mutex_unlock(&priv->ctrl_lock);
> > +
> > + return priv->cpkt_in.result;
> > +}
> > +
>
> [...]
>
> > +static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
> > + struct net_device *dev)
> > +{
> > + const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
> > + struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
> > + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> > + struct virtio_can_priv *priv = netdev_priv(dev);
> > + netdev_tx_t xmit_ret = NETDEV_TX_OK;
> > + struct virtio_can_tx *can_tx_msg;
> > + struct virtqueue *vq;
> > + unsigned long flags;
> > + u32 can_flags;
> > + int putidx;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > +
> > + 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), GFP_ATOMIC);
> > + if (!can_tx_msg) {
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
>
> Since we are allocating tx messages dynamically, the sdu[64] array inside
> struct virtio_can_tx_out can be converted to a flexible array and here
> the allocation can become:
>
> can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
>
> This would save memory in particular on CAN-CC interfaces, where 56 bytes
> per message would otherwise be lost (not to mention the case if/when
> CAN-XL will be supported).
>
> > + can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
> > + 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);
> > + can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
> > +
> > + /* Prepare sending of virtio message */
> > + 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);
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
> > + can_tx_msg->putidx = (unsigned int)putidx;
> > +
> > + /* Protect list operation */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + list_add_tail(&can_tx_msg->list, &priv->tx_list);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > + /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
> > + can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
> > +
> > + /* Protect queue and list operations */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
> > + if (unlikely(err)) { /* checking vq->num_free in flow control */
> > + list_del(&can_tx_msg->list);
> > + can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
> > + virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > + netif_stop_queue(dev);
> > + kfree(can_tx_msg);
> > + /* Expected never to be seen */
> > + netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
> > + xmit_ret = NETDEV_TX_BUSY;
> > + 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");
> > + }
> > +
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > +kick:
> > + if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
> > + 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,
> > + .ndo_change_mtu = can_change_mtu,
> > +};
> > +
> > +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);
> > +}
> > +
> > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > +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 long flags;
> > + unsigned int len;
> > + u8 result;
> > +
> > + stats = &dev->stats;
> > +
> > + /* Protect list and virtio queue operations */
> > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > +
> > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > + if (!can_tx_msg) {
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > + return 0; /* No more data */
> > + }
> > +
> > + 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) {
> > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > + * echoed. Intentional to bring a waiting process in an upper
> > + * layer to an end.
> > + * TODO: Any better means to indicate a problem here?
> > + */
> > + if (result != VIRTIO_CAN_RESULT_OK)
> > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
>
> Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
>
I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
a problem in the rx path and this is the tx path. I think the comment
refers to improving the way the driver informs this error to the user
but I may be wrong.
> For sure, counting the known errors as valid tx_packets and tx_bytes
> is misleading.
>
I'll remove the counters below.
> > +
> > + 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);
> > + }
> > +
> > + list_del(&can_tx_msg->list);
> > + 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);
> > + }
> > +
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > +
> > + kfree(can_tx_msg);
> > +
> > + return 1; /* Queue was not empty so there may be more data */
> > +}
> > +
>
> [...]
>
> > +
> > +static int virtio_can_find_vqs(struct virtio_can_priv *priv)
> > +{
> > + /* The order of RX and TX is exactly the opposite as in console and
> > + * network. Does not play any role but is a bad trap.
> > + */
> > + static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
> > + "can-tx",
> > + "can-rx",
> > + "can-state-ctrl"
> > + };
> > +
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
> > +
> > + /* Find the queues. */
> > + return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
> > + priv->io_callbacks, io_names, NULL);
> > +}
>
> Syntax of virtio_find_vqs changed a bit, here should now be:
>
> struct virtqueue_info vqs_info[] = {
> { "can-tx", virtio_can_tx_intr },
> { "can-rx", virtio_can_rx_intr },
> { "can-state-ctrl", virtio_can_control_intr },
> };
>
> 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 list_head *cursor, *next;
> > + struct virtqueue *vq;
> > +
> > + /* Reset the device */
> > + if (vdev->config->reset)
> > + vdev->config->reset(vdev);
> > +
> > + /* From here we have dead silence from the device side so no locks
> > + * are needed to protect against device side events.
> > + */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content to be de-allocated separately */
> > +
> > + /* Is keeping track of allocated elements by an own linked list
> > + * really necessary or may this be optimized using only
> > + * virtqueue_detach_unused_buf()?
> > + */
> > + list_for_each_safe(cursor, next, &priv->tx_list) {
> > + struct virtio_can_tx *can_tx;
> > +
> > + can_tx = list_entry(cursor, struct virtio_can_tx, list);
> > + list_del(cursor);
> > + kfree(can_tx);
> > + }
>
> I'd drop the tx_list entirely and rely on virtqueue_detach_unused_buf();
> this would allow to remove at least one spinlock save/restore pair at
> each transmission.
>
> > +
> > + if (vdev->config->del_vqs)
> > + vdev->config->del_vqs(vdev);
> > +}
> > +
>
> [...]
>
> > diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
> > new file mode 100644
> > index 000000000000..7cf613bb3f1a
> > --- /dev/null
> > +++ b/include/uapi/linux/virtio_can.h
> > @@ -0,0 +1,75 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause */
> > +/*
> > + * Copyright (C) 2021-2023 OpenSynergy GmbH
> > + */
> > +#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_LATE_TX_ACK 2
> > +#define VIRTIO_CAN_F_RTR_FRAMES 3
> > +
>
> The values for VIRTIO_CAN_F_LATE_TX_ACK and VIRTIO_CAN_F_RTR_FRAMES are
> inverted w.r.t. the merged virto-can spec [1].
>
> Note that this is the only deviation from the spec I found.
>
> > +/* 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
> > +
> > +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[64];
> > +};
> > +
>
> sdu[] here might be a flexible array, if the driver allocates
> virtio_can_tx_out structs dyncamically (see above). This would be
> beneficial in case of CAN-XL frames (if/when they will be supported).
>
> > +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[64];
> > +};
> > +
>
> Again, sdu[] might be a flexible array.
>
> > +/* 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 */
> >
>
> Thank you for your work!
>
> Regards,
> Francesco
>
>
> [1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L45
>
>
>
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-09-11 20:59 ` Francesco Valla
` (3 preceding siblings ...)
2025-10-14 10:15 ` Matias Ezequiel Vara Larsen
@ 2025-10-14 10:25 ` Matias Ezequiel Vara Larsen
4 siblings, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-14 10:25 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> Hello Mikhail, Harald,
>
> hoping there will be a v6 of this patch soon, a few comments:
>
> On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
>
> [...]
>
> > +
> > +/* 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];
> > + /* I/O callback function pointers for the virtqueues */
> > + vq_callback_t *io_callbacks[VIRTIO_CAN_QUEUE_COUNT];
> > + /* Lock for TX operations */
> > + spinlock_t tx_lock;
> > + /* Control queue lock. Defensive programming, may be not needed */
> > + struct mutex ctrl_lock;
> > + /* Wait for control queue processing without polling */
> > + struct completion ctrl_done;
> > + /* List of virtio CAN TX message */
> > + struct list_head tx_list;
> > + /* Array of receive queue messages */
> > + struct virtio_can_rx rpkt[128];
>
> This array should probably be allocated dynamically at probe - maybe
> using a module parameter instead of a hardcoded value as length?
>
> > + /* Those control queue messages cannot live on the stack! */
> > + struct virtio_can_control_out cpkt_out;
> > + struct virtio_can_control_in cpkt_in;
>
> Consider using a container struct as you did for the tx message, e.g.:
>
> struct virtio_can_control {
> struct virtio_can_control_out ctrl_out;
> struct virtio_can_control_in ctrl_in;
> };
>
> > + /* Data to get and maintain the putidx for local TX echo */
> > + struct ida tx_putidx_ida;
> > + /* In flight TX messages */
> > + atomic_t tx_inflight;
> > + /* BusOff pending. Reset after successful indication to upper layer */
> > + bool busoff_pending;
> > +};
> > +
>
> [...]
>
> > +
> > +/* Send a control message with message type either
> > + *
> > + * - VIRTIO_CAN_SET_CTRL_MODE_START or
> > + * - VIRTIO_CAN_SET_CTRL_MODE_STOP.
> > + *
> > + * Unlike AUTOSAR CAN Driver Can_SetControllerMode() there is no requirement
> > + * for this Linux driver to have an asynchronous implementation of the mode
> > + * setting function so in order to keep things simple the function is
> > + * implemented as synchronous function. Design pattern is
> > + * virtio_console.c/__send_control_msg() & virtio_net.c/virtnet_send_command().
> > + */
> > +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 device *dev = &priv->vdev->dev;
> > + struct virtqueue *vq;
> > + unsigned int len;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > +
> > + /* The function may be serialized by rtnl lock. Not sure.
> > + * Better safe than sorry.
> > + */
> > + mutex_lock(&priv->ctrl_lock);
> > +
> > + priv->cpkt_out.msg_type = cpu_to_le16(msg_type);
> > + sg_init_one(&sg_out, &priv->cpkt_out, sizeof(priv->cpkt_out));
> > + sg_init_one(&sg_in, &priv->cpkt_in, sizeof(priv->cpkt_in));
> > +
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, priv, GFP_ATOMIC);
> > + if (err != 0) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): virtqueue_add_sgs() failed\n", __func__);
> > + }
>
> Here it should return VIRTIO_CAN_RESULT_NOT_OK after unlocking the
> mutex, or it might wait for completion indefinitley below.
>
> > +
> > + if (!virtqueue_kick(vq)) {
> > + /* Not expected to happen */
> > + dev_err(dev, "%s(): Kick failed\n", __func__);
> > + }
>
> And here too.
>
> > +
> > + while (!virtqueue_get_buf(vq, &len) && !virtqueue_is_broken(vq))
> > + wait_for_completion(&priv->ctrl_done);
> > +
> > + mutex_unlock(&priv->ctrl_lock);
> > +
> > + return priv->cpkt_in.result;
> > +}
> > +
>
> [...]
>
> > +static netdev_tx_t virtio_can_start_xmit(struct sk_buff *skb,
> > + struct net_device *dev)
> > +{
> > + const unsigned int hdr_size = offsetof(struct virtio_can_tx_out, sdu);
> > + struct scatterlist sg_out, sg_in, *sgs[2] = { &sg_out, &sg_in };
> > + struct canfd_frame *cf = (struct canfd_frame *)skb->data;
> > + struct virtio_can_priv *priv = netdev_priv(dev);
> > + netdev_tx_t xmit_ret = NETDEV_TX_OK;
> > + struct virtio_can_tx *can_tx_msg;
> > + struct virtqueue *vq;
> > + unsigned long flags;
> > + u32 can_flags;
> > + int putidx;
> > + int err;
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > +
> > + 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), GFP_ATOMIC);
> > + if (!can_tx_msg) {
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
>
> Since we are allocating tx messages dynamically, the sdu[64] array inside
> struct virtio_can_tx_out can be converted to a flexible array and here
> the allocation can become:
>
> can_tx_msg = kzalloc(sizeof(*can_tx_msg) + cf->len, GFP_ATOMIC);
>
> This would save memory in particular on CAN-CC interfaces, where 56 bytes
> per message would otherwise be lost (not to mention the case if/when
> CAN-XL will be supported).
>
> > + can_tx_msg->tx_out.msg_type = cpu_to_le16(VIRTIO_CAN_TX);
> > + 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);
> > + can_tx_msg->tx_out.length = cpu_to_le16(cf->len);
> > +
> > + /* Prepare sending of virtio message */
> > + 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);
> > + dev->stats.tx_dropped++;
> > + goto kick; /* No way to return NET_XMIT_DROP here */
> > + }
> > +
> > + can_tx_msg->putidx = (unsigned int)putidx;
> > +
> > + /* Protect list operation */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + list_add_tail(&can_tx_msg->list, &priv->tx_list);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > + /* Push loopback echo. Will be looped back on TX interrupt/TX NAPI */
> > + can_put_echo_skb(skb, dev, can_tx_msg->putidx, 0);
> > +
> > + /* Protect queue and list operations */
> > + spin_lock_irqsave(&priv->tx_lock, flags);
> > + err = virtqueue_add_sgs(vq, sgs, 1u, 1u, can_tx_msg, GFP_ATOMIC);
> > + if (unlikely(err)) { /* checking vq->num_free in flow control */
> > + list_del(&can_tx_msg->list);
> > + can_free_echo_skb(dev, can_tx_msg->putidx, NULL);
> > + virtio_can_free_tx_idx(priv, can_tx_msg->putidx);
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > + netif_stop_queue(dev);
> > + kfree(can_tx_msg);
> > + /* Expected never to be seen */
> > + netdev_warn(dev, "TX: Stop queue, err = %d\n", err);
> > + xmit_ret = NETDEV_TX_BUSY;
> > + 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");
> > + }
> > +
> > + spin_unlock_irqrestore(&priv->tx_lock, flags);
> > +
> > +kick:
> > + if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
> > + 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,
> > + .ndo_change_mtu = can_change_mtu,
> > +};
> > +
> > +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);
> > +}
> > +
> > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > +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 long flags;
> > + unsigned int len;
> > + u8 result;
> > +
> > + stats = &dev->stats;
> > +
> > + /* Protect list and virtio queue operations */
> > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > +
> > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > + if (!can_tx_msg) {
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > + return 0; /* No more data */
> > + }
> > +
> > + 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) {
> > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > + * echoed. Intentional to bring a waiting process in an upper
> > + * layer to an end.
> > + * TODO: Any better means to indicate a problem here?
> > + */
> > + if (result != VIRTIO_CAN_RESULT_OK)
> > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
>
> Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
>
> For sure, counting the known errors as valid tx_packets and tx_bytes
> is misleading.
>
Rethinking about this, I think counters are OK since we are getting
buffers that are in the used ring of tx queue so they are actually sent.
> > +
> > + 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);
> > + }
> > +
> > + list_del(&can_tx_msg->list);
> > + 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);
> > + }
> > +
> > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > +
> > + kfree(can_tx_msg);
> > +
> > + return 1; /* Queue was not empty so there may be more data */
> > +}
> > +
>
> [...]
>
> > +
> > +static int virtio_can_find_vqs(struct virtio_can_priv *priv)
> > +{
> > + /* The order of RX and TX is exactly the opposite as in console and
> > + * network. Does not play any role but is a bad trap.
> > + */
> > + static const char * const io_names[VIRTIO_CAN_QUEUE_COUNT] = {
> > + "can-tx",
> > + "can-rx",
> > + "can-state-ctrl"
> > + };
> > +
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_TX] = virtio_can_tx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_RX] = virtio_can_rx_intr;
> > + priv->io_callbacks[VIRTIO_CAN_QUEUE_CONTROL] = virtio_can_control_intr;
> > +
> > + /* Find the queues. */
> > + return virtio_find_vqs(priv->vdev, VIRTIO_CAN_QUEUE_COUNT, priv->vqs,
> > + priv->io_callbacks, io_names, NULL);
> > +}
>
> Syntax of virtio_find_vqs changed a bit, here should now be:
>
> struct virtqueue_info vqs_info[] = {
> { "can-tx", virtio_can_tx_intr },
> { "can-rx", virtio_can_rx_intr },
> { "can-state-ctrl", virtio_can_control_intr },
> };
>
> 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 list_head *cursor, *next;
> > + struct virtqueue *vq;
> > +
> > + /* Reset the device */
> > + if (vdev->config->reset)
> > + vdev->config->reset(vdev);
> > +
> > + /* From here we have dead silence from the device side so no locks
> > + * are needed to protect against device side events.
> > + */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_CONTROL];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_RX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content allocated statically */
> > +
> > + vq = priv->vqs[VIRTIO_CAN_QUEUE_TX];
> > + while (virtqueue_detach_unused_buf(vq))
> > + ; /* Do nothing, content to be de-allocated separately */
> > +
> > + /* Is keeping track of allocated elements by an own linked list
> > + * really necessary or may this be optimized using only
> > + * virtqueue_detach_unused_buf()?
> > + */
> > + list_for_each_safe(cursor, next, &priv->tx_list) {
> > + struct virtio_can_tx *can_tx;
> > +
> > + can_tx = list_entry(cursor, struct virtio_can_tx, list);
> > + list_del(cursor);
> > + kfree(can_tx);
> > + }
>
> I'd drop the tx_list entirely and rely on virtqueue_detach_unused_buf();
> this would allow to remove at least one spinlock save/restore pair at
> each transmission.
>
> > +
> > + if (vdev->config->del_vqs)
> > + vdev->config->del_vqs(vdev);
> > +}
> > +
>
> [...]
>
> > diff --git a/include/uapi/linux/virtio_can.h b/include/uapi/linux/virtio_can.h
> > new file mode 100644
> > index 000000000000..7cf613bb3f1a
> > --- /dev/null
> > +++ b/include/uapi/linux/virtio_can.h
> > @@ -0,0 +1,75 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause */
> > +/*
> > + * Copyright (C) 2021-2023 OpenSynergy GmbH
> > + */
> > +#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_LATE_TX_ACK 2
> > +#define VIRTIO_CAN_F_RTR_FRAMES 3
> > +
>
> The values for VIRTIO_CAN_F_LATE_TX_ACK and VIRTIO_CAN_F_RTR_FRAMES are
> inverted w.r.t. the merged virto-can spec [1].
>
> Note that this is the only deviation from the spec I found.
>
> > +/* 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
> > +
> > +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[64];
> > +};
> > +
>
> sdu[] here might be a flexible array, if the driver allocates
> virtio_can_tx_out structs dyncamically (see above). This would be
> beneficial in case of CAN-XL frames (if/when they will be supported).
>
> > +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[64];
> > +};
> > +
>
> Again, sdu[] might be a flexible array.
>
> > +/* 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 */
> >
>
> Thank you for your work!
>
> Regards,
> Francesco
>
>
> [1] https://github.com/oasis-tcs/virtio-spec/blob/virtio-1.4/device-types/can/description.tex#L45
>
>
>
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-14 6:54 ` Francesco Valla
@ 2025-10-14 10:42 ` Matias Ezequiel Vara Larsen
0 siblings, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-14 10:42 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tue, Oct 14, 2025 at 08:54:23AM +0200, Francesco Valla wrote:
> On Monday, 13 October 2025 at 18:35:51 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > [...]
> > >
> > > > +
> > > > +/* 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[64];
> > > > +};
> > > > +
> > >
> > > sdu[] here might be a flexible array, if the driver allocates
> > > virtio_can_tx_out structs dyncamically (see above). This would be
> > > beneficial in case of CAN-XL frames (if/when they will be supported).
> > >
> >
> > If we use a flexible array for sdu[] here, then we will have a problem
> > when defining the virtio_can_tx struct since it is not in the end of the
> > structure. I think it is a good idea to define it as a flexible array
> > but I do not know how.
>
> In this case, I'd move struct virtio_can_tx_out at the end of the
> virtio_can_tx struct - in this way, sdu[] would be at the end:
>
> struct virtio_can_tx {
> struct list_head list;
> unsigned int putidx;
> struct virtio_can_tx_in tx_in;
> struct virtio_can_tx_out tx_out;
> };
>
Done.
> Maybe an additional comment declaring why it is done this way would
> be a good idea? Also considering that the two structures are defined
> in different files.
>
I am not sure if a comment is required since moving the tx_out field
would make the compiler complains anyway but I do not have an strong
opinion. Also, would it help to put both structures in the same file?
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-14 10:15 ` Matias Ezequiel Vara Larsen
@ 2025-10-14 16:01 ` Francesco Valla
2025-10-20 14:56 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Francesco Valla @ 2025-10-14 16:01 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > Hello Mikhail, Harald,
> >
> > hoping there will be a v6 of this patch soon, a few comments:
> >
> > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> >
> > [...]
> > > +
> > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > +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 long flags;
> > > + unsigned int len;
> > > + u8 result;
> > > +
> > > + stats = &dev->stats;
> > > +
> > > + /* Protect list and virtio queue operations */
> > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > +
> > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > + if (!can_tx_msg) {
> > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > + return 0; /* No more data */
> > > + }
> > > +
> > > + 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) {
> > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > + * echoed. Intentional to bring a waiting process in an upper
> > > + * layer to an end.
> > > + * TODO: Any better means to indicate a problem here?
> > > + */
> > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> >
> > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> >
> I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> a problem in the rx path and this is the tx path. I think the comment
> refers to improving the way the driver informs this error to the user
> but I may be wrong.
>
Since we have no detail of what went wrong here, I suggested
CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
controller error with id CAN_ERR_CRTL; however, a different error might be
more appropriate.
For sure, at least in my experience, having a warn printed to kmsg is *not*
enough, as the application sending the message(s) would not be able to detect
the error.
> > For sure, counting the known errors as valid tx_packets and tx_bytes
> > is misleading.
> >
>
> I'll remove the counters below.
>
We don't really know what's wrong here - the packet might have been sent and
and then not ACK'ed, as well as any other error condition (as it happens in the
reference implementation from the original authors [1]). Echoing the packet
only "to bring a waiting process in an upper layer to an end" and incrementing
counters feels wrong, but maybe someone more expert than me can advise better
here.
[1] https://github.com/OpenSynergy/qemu/commit/115540168f92ba5351a20b9c62552782ea1e3e04
Regards,
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-14 16:01 ` Francesco Valla
@ 2025-10-20 14:56 ` Matias Ezequiel Vara Larsen
2025-10-20 21:24 ` Francesco Valla
0 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-20 14:56 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > Hello Mikhail, Harald,
> > >
> > > hoping there will be a v6 of this patch soon, a few comments:
> > >
> > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > >
> > > [...]
> > > > +
> > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > +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 long flags;
> > > > + unsigned int len;
> > > > + u8 result;
> > > > +
> > > > + stats = &dev->stats;
> > > > +
> > > > + /* Protect list and virtio queue operations */
> > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > +
> > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > + if (!can_tx_msg) {
> > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > + return 0; /* No more data */
> > > > + }
> > > > +
> > > > + 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) {
> > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > + * layer to an end.
> > > > + * TODO: Any better means to indicate a problem here?
> > > > + */
> > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > >
> > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > >
> > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > a problem in the rx path and this is the tx path. I think the comment
> > refers to improving the way the driver informs this error to the user
> > but I may be wrong.
> >
>
> Since we have no detail of what went wrong here, I suggested
> CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> controller error with id CAN_ERR_CRTL; however, a different error might be
> more appropriate.
>
> For sure, at least in my experience, having a warn printed to kmsg is *not*
> enough, as the application sending the message(s) would not be able to detect
> the error.
>
>
> > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > is misleading.
> > >
> >
> > I'll remove the counters below.
> >
>
> We don't really know what's wrong here - the packet might have been sent and
> and then not ACK'ed, as well as any other error condition (as it happens in the
> reference implementation from the original authors [1]). Echoing the packet
> only "to bring a waiting process in an upper layer to an end" and incrementing
> counters feels wrong, but maybe someone more expert than me can advise better
> here.
>
>
I agree. IIUC, in case there has been a problem during transmission, I
should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
stats. Is this correct?
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-20 14:56 ` Matias Ezequiel Vara Larsen
@ 2025-10-20 21:24 ` Francesco Valla
2025-10-21 9:40 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Francesco Valla @ 2025-10-20 21:24 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Monday, 20 October 2025 at 16:56:08 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> > On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > Hello Mikhail, Harald,
> > > >
> > > > hoping there will be a v6 of this patch soon, a few comments:
> > > >
> > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > >
> > > > [...]
> > > > > +
> > > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > > +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 long flags;
> > > > > + unsigned int len;
> > > > > + u8 result;
> > > > > +
> > > > > + stats = &dev->stats;
> > > > > +
> > > > > + /* Protect list and virtio queue operations */
> > > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > > +
> > > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > > + if (!can_tx_msg) {
> > > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > > + return 0; /* No more data */
> > > > > + }
> > > > > +
> > > > > + 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) {
> > > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > > + * layer to an end.
> > > > > + * TODO: Any better means to indicate a problem here?
> > > > > + */
> > > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > > >
> > > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > > >
> > > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > > a problem in the rx path and this is the tx path. I think the comment
> > > refers to improving the way the driver informs this error to the user
> > > but I may be wrong.
> > >
> >
> > Since we have no detail of what went wrong here, I suggested
> > CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> > controller error with id CAN_ERR_CRTL; however, a different error might be
> > more appropriate.
> >
> > For sure, at least in my experience, having a warn printed to kmsg is *not*
> > enough, as the application sending the message(s) would not be able to detect
> > the error.
> >
> >
> > > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > > is misleading.
> > > >
> > >
> > > I'll remove the counters below.
> > >
> >
> > We don't really know what's wrong here - the packet might have been sent and
> > and then not ACK'ed, as well as any other error condition (as it happens in the
> > reference implementation from the original authors [1]). Echoing the packet
> > only "to bring a waiting process in an upper layer to an end" and incrementing
> > counters feels wrong, but maybe someone more expert than me can advise better
> > here.
> >
> >
>
> I agree. IIUC, in case there has been a problem during transmission, I
> should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
> netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
> stats. Is this correct?
>
> Matias
>
>
That's my understanding too! stats->tx_dropped should be the right value to
increment (see for example [1]).
[1] https://elixir.bootlin.com/linux/v6.17.3/source/drivers/net/can/ctucanfd/ctucanfd_base.c#L1035
Regards,
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-20 21:24 ` Francesco Valla
@ 2025-10-21 9:40 ` Matias Ezequiel Vara Larsen
2025-10-21 12:08 ` Francesco Valla
0 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-21 9:40 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Mon, Oct 20, 2025 at 11:24:15PM +0200, Francesco Valla wrote:
> On Monday, 20 October 2025 at 16:56:08 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> > > On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > > Hello Mikhail, Harald,
> > > > >
> > > > > hoping there will be a v6 of this patch soon, a few comments:
> > > > >
> > > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > > >
> > > > > [...]
> > > > > > +
> > > > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > > > +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 long flags;
> > > > > > + unsigned int len;
> > > > > > + u8 result;
> > > > > > +
> > > > > > + stats = &dev->stats;
> > > > > > +
> > > > > > + /* Protect list and virtio queue operations */
> > > > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > > > +
> > > > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > > > + if (!can_tx_msg) {
> > > > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > > > + return 0; /* No more data */
> > > > > > + }
> > > > > > +
> > > > > > + 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) {
> > > > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > > > + * layer to an end.
> > > > > > + * TODO: Any better means to indicate a problem here?
> > > > > > + */
> > > > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > > > >
> > > > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > > > >
> > > > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > > > a problem in the rx path and this is the tx path. I think the comment
> > > > refers to improving the way the driver informs this error to the user
> > > > but I may be wrong.
> > > >
> > >
> > > Since we have no detail of what went wrong here, I suggested
> > > CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> > > controller error with id CAN_ERR_CRTL; however, a different error might be
> > > more appropriate.
> > >
> > > For sure, at least in my experience, having a warn printed to kmsg is *not*
> > > enough, as the application sending the message(s) would not be able to detect
> > > the error.
> > >
> > >
> > > > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > > > is misleading.
> > > > >
> > > >
> > > > I'll remove the counters below.
> > > >
> > >
> > > We don't really know what's wrong here - the packet might have been sent and
> > > and then not ACK'ed, as well as any other error condition (as it happens in the
> > > reference implementation from the original authors [1]). Echoing the packet
> > > only "to bring a waiting process in an upper layer to an end" and incrementing
> > > counters feels wrong, but maybe someone more expert than me can advise better
> > > here.
> > >
> > >
> >
> > I agree. IIUC, in case there has been a problem during transmission, I
> > should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
> > netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
> > stats. Is this correct?
> >
> > Matias
> >
> >
>
> That's my understanding too! stats->tx_dropped should be the right value to
> increment (see for example [1]).
>
> [1] https://elixir.bootlin.com/linux/v6.17.3/source/drivers/net/can/ctucanfd/ctucanfd_base.c#L1035
>
I think the counter to increment would be stats->tx_errors in this case ...
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-21 9:40 ` Matias Ezequiel Vara Larsen
@ 2025-10-21 12:08 ` Francesco Valla
2025-10-21 13:15 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Francesco Valla @ 2025-10-21 12:08 UTC (permalink / raw)
To: Matias Ezequiel Vara Larsen
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tuesday, 21 October 2025 at 11:40:07 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> On Mon, Oct 20, 2025 at 11:24:15PM +0200, Francesco Valla wrote:
> > On Monday, 20 October 2025 at 16:56:08 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> > > > On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > > > Hello Mikhail, Harald,
> > > > > >
> > > > > > hoping there will be a v6 of this patch soon, a few comments:
> > > > > >
> > > > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > > > >
> > > > > > [...]
> > > > > > > +
> > > > > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > > > > +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 long flags;
> > > > > > > + unsigned int len;
> > > > > > > + u8 result;
> > > > > > > +
> > > > > > > + stats = &dev->stats;
> > > > > > > +
> > > > > > > + /* Protect list and virtio queue operations */
> > > > > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > > > > +
> > > > > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > > > > + if (!can_tx_msg) {
> > > > > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > > > > + return 0; /* No more data */
> > > > > > > + }
> > > > > > > +
> > > > > > > + 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) {
> > > > > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > > > > + * layer to an end.
> > > > > > > + * TODO: Any better means to indicate a problem here?
> > > > > > > + */
> > > > > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > > > > >
> > > > > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > > > > >
> > > > > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > > > > a problem in the rx path and this is the tx path. I think the comment
> > > > > refers to improving the way the driver informs this error to the user
> > > > > but I may be wrong.
> > > > >
> > > >
> > > > Since we have no detail of what went wrong here, I suggested
> > > > CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> > > > controller error with id CAN_ERR_CRTL; however, a different error might be
> > > > more appropriate.
> > > >
> > > > For sure, at least in my experience, having a warn printed to kmsg is *not*
> > > > enough, as the application sending the message(s) would not be able to detect
> > > > the error.
> > > >
> > > >
> > > > > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > > > > is misleading.
> > > > > >
> > > > >
> > > > > I'll remove the counters below.
> > > > >
> > > >
> > > > We don't really know what's wrong here - the packet might have been sent and
> > > > and then not ACK'ed, as well as any other error condition (as it happens in the
> > > > reference implementation from the original authors [1]). Echoing the packet
> > > > only "to bring a waiting process in an upper layer to an end" and incrementing
> > > > counters feels wrong, but maybe someone more expert than me can advise better
> > > > here.
> > > >
> > > >
> > >
> > > I agree. IIUC, in case there has been a problem during transmission, I
> > > should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
> > > netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
> > > stats. Is this correct?
> > >
> > > Matias
> > >
> > >
> >
> > That's my understanding too! stats->tx_dropped should be the right value to
> > increment (see for example [1]).
> >
> > [1] https://elixir.bootlin.com/linux/v6.17.3/source/drivers/net/can/ctucanfd/ctucanfd_base.c#L1035
> >
>
> I think the counter to increment would be stats->tx_errors in this case ...
>
I don't fully agree. tx_errors is for CAN frames that got transmitted but then
lead to an error (e.g.: no ACK), while here we might be dealing with frames
that didn't even manage to reach the transmission queue [1].
An exception to this may arise when the VIRTIO_CAN_F_CAN_LATE_TX_ACK feature
is negotiated; in this case, a VIRTIO_CAN_RESULT_NOT_OK may indicate either a
dropped frame (tx_dropped) or a failed transmission (tx_error) [2].
[1] https://github.com/oasis-tcs/virtio-spec/blob/master/device-types/can/description.tex#L139
[2] https://github.com/oasis-tcs/virtio-spec/blob/master/device-types/can/description.tex#L196
BR,
Francesco
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-21 12:08 ` Francesco Valla
@ 2025-10-21 13:15 ` Matias Ezequiel Vara Larsen
2025-10-31 12:21 ` Matias Ezequiel Vara Larsen
0 siblings, 1 reply; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-21 13:15 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tue, Oct 21, 2025 at 02:08:35PM +0200, Francesco Valla wrote:
> On Tuesday, 21 October 2025 at 11:40:07 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > On Mon, Oct 20, 2025 at 11:24:15PM +0200, Francesco Valla wrote:
> > > On Monday, 20 October 2025 at 16:56:08 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> > > > > On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > > > > Hello Mikhail, Harald,
> > > > > > >
> > > > > > > hoping there will be a v6 of this patch soon, a few comments:
> > > > > > >
> > > > > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > > > > >
> > > > > > > [...]
> > > > > > > > +
> > > > > > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > > > > > +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 long flags;
> > > > > > > > + unsigned int len;
> > > > > > > > + u8 result;
> > > > > > > > +
> > > > > > > > + stats = &dev->stats;
> > > > > > > > +
> > > > > > > > + /* Protect list and virtio queue operations */
> > > > > > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > > > > > +
> > > > > > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > > > > > + if (!can_tx_msg) {
> > > > > > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > > > > > + return 0; /* No more data */
> > > > > > > > + }
> > > > > > > > +
> > > > > > > > + 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) {
> > > > > > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > > > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > > > > > + * layer to an end.
> > > > > > > > + * TODO: Any better means to indicate a problem here?
> > > > > > > > + */
> > > > > > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > > > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > > > > > >
> > > > > > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > > > > > >
> > > > > > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > > > > > a problem in the rx path and this is the tx path. I think the comment
> > > > > > refers to improving the way the driver informs this error to the user
> > > > > > but I may be wrong.
> > > > > >
> > > > >
> > > > > Since we have no detail of what went wrong here, I suggested
> > > > > CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> > > > > controller error with id CAN_ERR_CRTL; however, a different error might be
> > > > > more appropriate.
> > > > >
> > > > > For sure, at least in my experience, having a warn printed to kmsg is *not*
> > > > > enough, as the application sending the message(s) would not be able to detect
> > > > > the error.
> > > > >
> > > > >
> > > > > > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > > > > > is misleading.
> > > > > > >
> > > > > >
> > > > > > I'll remove the counters below.
> > > > > >
> > > > >
> > > > > We don't really know what's wrong here - the packet might have been sent and
> > > > > and then not ACK'ed, as well as any other error condition (as it happens in the
> > > > > reference implementation from the original authors [1]). Echoing the packet
> > > > > only "to bring a waiting process in an upper layer to an end" and incrementing
> > > > > counters feels wrong, but maybe someone more expert than me can advise better
> > > > > here.
> > > > >
> > > > >
> > > >
> > > > I agree. IIUC, in case there has been a problem during transmission, I
> > > > should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
> > > > netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
> > > > stats. Is this correct?
> > > >
> > > > Matias
> > > >
> > > >
> > >
> > > That's my understanding too! stats->tx_dropped should be the right value to
> > > increment (see for example [1]).
> > >
> > > [1] https://elixir.bootlin.com/linux/v6.17.3/source/drivers/net/can/ctucanfd/ctucanfd_base.c#L1035
> > >
> >
> > I think the counter to increment would be stats->tx_errors in this case ...
> >
>
> I don't fully agree. tx_errors is for CAN frames that got transmitted but then
> lead to an error (e.g.: no ACK), while here we might be dealing with frames
> that didn't even manage to reach the transmission queue [1].
>
Let's use tx_dropped then, I honestly do not have an strong opinion
about it. We can change that later if we are not happy.
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v5] can: virtio: Initial virtio CAN driver.
2025-10-21 13:15 ` Matias Ezequiel Vara Larsen
@ 2025-10-31 12:21 ` Matias Ezequiel Vara Larsen
0 siblings, 0 replies; 30+ messages in thread
From: Matias Ezequiel Vara Larsen @ 2025-10-31 12:21 UTC (permalink / raw)
To: Francesco Valla
Cc: Marc Kleine-Budde, Paolo Abeni, Harald Mommer,
Mikhail Golubev-Ciuchea, Wolfgang Grandegger, Eric Dumazet,
Jakub Kicinski, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Damir Shaikhutdinov, linux-kernel, linux-can, netdev,
virtualization, development
On Tue, Oct 21, 2025 at 3:15 PM Matias Ezequiel Vara Larsen
<mvaralar@redhat.com> wrote:
>
> On Tue, Oct 21, 2025 at 02:08:35PM +0200, Francesco Valla wrote:
> > On Tuesday, 21 October 2025 at 11:40:07 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > On Mon, Oct 20, 2025 at 11:24:15PM +0200, Francesco Valla wrote:
> > > > On Monday, 20 October 2025 at 16:56:08 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > > On Tue, Oct 14, 2025 at 06:01:07PM +0200, Francesco Valla wrote:
> > > > > > On Tuesday, 14 October 2025 at 12:15:12 Matias Ezequiel Vara Larsen <mvaralar@redhat.com> wrote:
> > > > > > > On Thu, Sep 11, 2025 at 10:59:40PM +0200, Francesco Valla wrote:
> > > > > > > > Hello Mikhail, Harald,
> > > > > > > >
> > > > > > > > hoping there will be a v6 of this patch soon, a few comments:
> > > > > > > >
> > > > > > > > On Monday, 8 January 2024 at 14:10:35 Mikhail Golubev-Ciuchea <Mikhail.Golubev-Ciuchea@opensynergy.com> wrote:
> > > > > > > >
> > > > > > > > [...]
> > > > > > > > > +
> > > > > > > > > +/* Compare with m_can.c/m_can_echo_tx_event() */
> > > > > > > > > +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 long flags;
> > > > > > > > > + unsigned int len;
> > > > > > > > > + u8 result;
> > > > > > > > > +
> > > > > > > > > + stats = &dev->stats;
> > > > > > > > > +
> > > > > > > > > + /* Protect list and virtio queue operations */
> > > > > > > > > + spin_lock_irqsave(&can_priv->tx_lock, flags);
> > > > > > > > > +
> > > > > > > > > + can_tx_msg = virtqueue_get_buf(vq, &len);
> > > > > > > > > + if (!can_tx_msg) {
> > > > > > > > > + spin_unlock_irqrestore(&can_priv->tx_lock, flags);
> > > > > > > > > + return 0; /* No more data */
> > > > > > > > > + }
> > > > > > > > > +
> > > > > > > > > + 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) {
> > > > > > > > > + /* Here also frames with result != VIRTIO_CAN_RESULT_OK are
> > > > > > > > > + * echoed. Intentional to bring a waiting process in an upper
> > > > > > > > > + * layer to an end.
> > > > > > > > > + * TODO: Any better means to indicate a problem here?
> > > > > > > > > + */
> > > > > > > > > + if (result != VIRTIO_CAN_RESULT_OK)
> > > > > > > > > + netdev_warn(dev, "TX ACK: Result = %u\n", result);
> > > > > > > >
> > > > > > > > Maybe an error frame reporting CAN_ERR_CRTL_UNSPEC would be better?
> > > > > > > >
> > > > > > > I am not sure. In xilinx_can.c, CAN_ERR_CRTL_UNSPEC is indicated during
> > > > > > > a problem in the rx path and this is the tx path. I think the comment
> > > > > > > refers to improving the way the driver informs this error to the user
> > > > > > > but I may be wrong.
> > > > > > >
> > > > > >
> > > > > > Since we have no detail of what went wrong here, I suggested
> > > > > > CAN_ERR_CRTL_UNSPEC as it is "unspecified error", to be coupled with a
> > > > > > controller error with id CAN_ERR_CRTL; however, a different error might be
> > > > > > more appropriate.
> > > > > >
> > > > > > For sure, at least in my experience, having a warn printed to kmsg is *not*
> > > > > > enough, as the application sending the message(s) would not be able to detect
> > > > > > the error.
> > > > > >
> > > > > >
> > > > > > > > For sure, counting the known errors as valid tx_packets and tx_bytes
> > > > > > > > is misleading.
> > > > > > > >
> > > > > > >
> > > > > > > I'll remove the counters below.
> > > > > > >
> > > > > >
> > > > > > We don't really know what's wrong here - the packet might have been sent and
> > > > > > and then not ACK'ed, as well as any other error condition (as it happens in the
> > > > > > reference implementation from the original authors [1]). Echoing the packet
> > > > > > only "to bring a waiting process in an upper layer to an end" and incrementing
> > > > > > counters feels wrong, but maybe someone more expert than me can advise better
> > > > > > here.
> > > > > >
> > > > > >
> > > > >
> > > > > I agree. IIUC, in case there has been a problem during transmission, I
> > > > > should 1) indicate this by injecting a CAN_ERR_CRTL_UNSPEC package with
> > > > > netif_rx() and 2) use can_free_echo_skb() and increment the tx_error
> > > > > stats. Is this correct?
> > > > >
> > > > > Matias
> > > > >
> > > > >
> > > >
> > > > That's my understanding too! stats->tx_dropped should be the right value to
> > > > increment (see for example [1]).
> > > >
> > > > [1] https://elixir.bootlin.com/linux/v6.17.3/source/drivers/net/can/ctucanfd/ctucanfd_base.c#L1035
> > > >
> > >
> > > I think the counter to increment would be stats->tx_errors in this case ...
> > >
> >
> > I don't fully agree. tx_errors is for CAN frames that got transmitted but then
> > lead to an error (e.g.: no ACK), while here we might be dealing with frames
> > that didn't even manage to reach the transmission queue [1].
> >
> Let's use tx_dropped then, I honestly do not have an strong opinion
> about it. We can change that later if we are not happy.
>
> Matias
Just sent v6 in https://lore.kernel.org/all/aQJRnX7OpFRY%2F1+H@fedora/
Matias
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2025-10-31 12:21 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-08 13:10 [PATCH v5] can: virtio: Initial virtio CAN driver Mikhail Golubev-Ciuchea
2024-01-08 19:34 ` Christophe JAILLET
2024-02-01 18:57 ` Harald Mommer
2025-03-12 10:31 ` Matias Ezequiel Vara Larsen
2025-03-12 10:41 ` Marc Kleine-Budde
2025-03-12 13:28 ` Matias Ezequiel Vara Larsen
2025-03-12 13:36 ` Marc Kleine-Budde
2025-03-12 15:11 ` Matias Ezequiel Vara Larsen
2025-03-13 0:26 ` Jason Wang
2025-03-13 18:48 ` Matias Ezequiel Vara Larsen
2025-09-11 20:59 ` Francesco Valla
2025-10-01 15:23 ` Matias Ezequiel Vara Larsen
2025-10-10 15:46 ` Matias Ezequiel Vara Larsen
2025-10-10 21:20 ` Francesco Valla
2025-10-13 9:52 ` Matias Ezequiel Vara Larsen
2025-10-13 16:29 ` Francesco Valla
2025-10-13 18:07 ` Matias Ezequiel Vara Larsen
2025-10-13 14:15 ` Matias Ezequiel Vara Larsen
2025-10-13 16:35 ` Matias Ezequiel Vara Larsen
2025-10-14 6:54 ` Francesco Valla
2025-10-14 10:42 ` Matias Ezequiel Vara Larsen
2025-10-14 10:15 ` Matias Ezequiel Vara Larsen
2025-10-14 16:01 ` Francesco Valla
2025-10-20 14:56 ` Matias Ezequiel Vara Larsen
2025-10-20 21:24 ` Francesco Valla
2025-10-21 9:40 ` Matias Ezequiel Vara Larsen
2025-10-21 12:08 ` Francesco Valla
2025-10-21 13:15 ` Matias Ezequiel Vara Larsen
2025-10-31 12:21 ` Matias Ezequiel Vara Larsen
2025-10-14 10:25 ` Matias Ezequiel Vara Larsen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).