For peripheral devices, m_can sent skbs directly from a threaded irq instead of from a softirq context. This patch transitions the driver to use the rx-offload helper, ensuring the skbs are sent from the correct context, with h/w timestamping to ensure correct ordering. Signed-off-by: Torin Cooper-Bennun <torin@xxxxxxxxxxxxxxxxxx> --- drivers/net/can/m_can/m_can.c | 50 ++++++++++++++++++++++++++--------- drivers/net/can/m_can/m_can.h | 2 ++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 83a673417e7c..ebdec9c6c0b5 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -467,7 +467,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) struct m_can_classdev *cdev = netdev_priv(dev); struct canfd_frame *cf; struct sk_buff *skb; - u32 id, fgi, dlc; + u32 id, fgi, dlc, timestamp; int i; /* calculate the fifo get index for where to read data */ @@ -516,7 +516,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) stats->rx_packets++; stats->rx_bytes += cf->len; - netif_receive_skb(skb); + timestamp = ((dlc & RX_BUF_RXTS_MASK) >> RX_BUF_RXTS_SHIFT) << 16; + + can_rx_offload_queue_sorted(&cdev->offload, skb, timestamp); } static int m_can_do_rx_poll(struct net_device *dev, int quota) @@ -547,6 +549,7 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) static int m_can_handle_lost_msg(struct net_device *dev) { + struct m_can_classdev *cdev = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; struct sk_buff *skb; struct can_frame *frame; @@ -563,7 +566,8 @@ static int m_can_handle_lost_msg(struct net_device *dev) frame->can_id |= CAN_ERR_CRTL; frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; - netif_receive_skb(skb); + can_rx_offload_queue_sorted(&cdev->offload, skb, + m_can_get_timestamp(cdev)); return 1; } @@ -620,7 +624,8 @@ static int m_can_handle_lec_err(struct net_device *dev, stats->rx_packets++; stats->rx_bytes += cf->len; - netif_receive_skb(skb); + can_rx_offload_queue_sorted(&cdev->offload, skb, + m_can_get_timestamp(cdev)); return 1; } @@ -739,7 +744,8 @@ static int m_can_handle_state_change(struct net_device *dev, stats->rx_packets++; stats->rx_bytes += cf->len; - netif_receive_skb(skb); + can_rx_offload_queue_sorted(&cdev->offload, skb, + m_can_get_timestamp(cdev)); return 1; } @@ -825,7 +831,8 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus) netdev_dbg(dev, "allocation of skb failed\n"); return 0; } - netif_receive_skb(skb); + can_rx_offload_queue_sorted(&cdev->offload, skb, + m_can_get_timestamp(cdev)); return 1; } @@ -926,6 +933,19 @@ static int m_can_poll(struct napi_struct *napi, int quota) return work_done; } +static void m_can_tx_update_stats(struct m_can_classdev *cdev, + unsigned int msg_mark) +{ + struct net_device_stats *stats = &cdev->net->stats; + + stats->tx_bytes += + can_rx_offload_get_echo_skb(&cdev->offload, + msg_mark, + m_can_get_timestamp(cdev), + NULL); + stats->tx_packets++; +} + static void m_can_echo_tx_event(struct net_device *dev) { u32 txe_count = 0; @@ -935,7 +955,6 @@ static void m_can_echo_tx_event(struct net_device *dev) unsigned int msg_mark; struct m_can_classdev *cdev = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; /* read tx event fifo status */ m_can_txefs = m_can_read(cdev, M_CAN_TXEFS); @@ -958,8 +977,7 @@ static void m_can_echo_tx_event(struct net_device *dev) (fgi << TXEFA_EFAI_SHIFT))); /* update stats */ - stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); - stats->tx_packets++; + m_can_tx_update_stats(cdev, msg_mark); } } @@ -967,7 +985,6 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct m_can_classdev *cdev = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; u32 ir; if (pm_runtime_suspended(cdev->dev)) @@ -1000,8 +1017,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (cdev->version == 30) { if (ir & IR_TC) { /* Transmission Complete Interrupt*/ - stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); - stats->tx_packets++; + m_can_tx_update_stats(cdev, 0); can_led_event(dev, CAN_LED_EVENT_TX); netif_wake_queue(dev); } @@ -1463,6 +1479,7 @@ static int m_can_close(struct net_device *dev) cdev->tx_wq = NULL; } + can_rx_offload_disable(&cdev->offload); close_candev(dev); can_led_event(dev, CAN_LED_EVENT_STOP); @@ -1661,6 +1678,8 @@ static int m_can_open(struct net_device *dev) goto exit_disable_clks; } + can_rx_offload_enable(&cdev->offload); + /* register interrupt handler */ if (cdev->is_peripheral) { cdev->tx_skb = NULL; @@ -1702,6 +1721,7 @@ static int m_can_open(struct net_device *dev) if (cdev->is_peripheral) destroy_workqueue(cdev->tx_wq); out_wq_fail: + can_rx_offload_disable(&cdev->offload); close_candev(dev); exit_disable_clks: m_can_clk_stop(cdev); @@ -1855,6 +1875,11 @@ int m_can_class_register(struct m_can_classdev *cdev) return ret; } + ret = can_rx_offload_add_manual(cdev->net, &cdev->offload, + M_CAN_NAPI_WEIGHT); + if (ret) + goto clk_disable; + ret = m_can_dev_setup(cdev); if (ret) goto clk_disable; @@ -1885,6 +1910,7 @@ EXPORT_SYMBOL_GPL(m_can_class_register); void m_can_class_unregister(struct m_can_classdev *cdev) { + can_rx_offload_del(&cdev->offload); unregister_candev(cdev->net); } EXPORT_SYMBOL_GPL(m_can_class_unregister); diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index 3fda84cef351..ace071c3e58c 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -8,6 +8,7 @@ #include <linux/can/core.h> #include <linux/can/led.h> +#include <linux/can/rx-offload.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/dma-mapping.h> @@ -71,6 +72,7 @@ struct m_can_ops { struct m_can_classdev { struct can_priv can; + struct can_rx_offload offload; struct napi_struct napi; struct net_device *net; struct device *dev; -- 2.30.1