On 08.03.2021 10:24:28, Torin Cooper-Bennun wrote: > For peripheral devices, m_can sent skbs directly from a threaded irq > instead of from a softirq context, breaking the tcan4x5x peripheral > driver completely. This patch transitions the driver to use the > rx-offload helper for peripherals, ensuring the skbs are sent from the > correct context, with h/w timestamping to ensure correct ordering. > > Signed-off-by: Torin Cooper-Bennun > --- > drivers/net/can/m_can/m_can.c | 109 +++++++++++++++++++++++++++++----- > drivers/net/can/m_can/m_can.h | 2 + > 2 files changed, 97 insertions(+), 14 deletions(-) > > diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c > index e61b7e186d80..80519b5cec31 100644 > --- a/drivers/net/can/m_can/m_can.c > +++ b/drivers/net/can/m_can/m_can.c > @@ -457,6 +457,20 @@ static void m_can_clean(struct net_device *net) > } > } > > +/* For peripherals, pass skb to rx-offload, which will push skb from napi. For > + * non-peripherals, RX is done in napi already, so push directly. timestamp is > + * used to ensure good skb ordering in rx-offload and is ignored > + * for non-peripherals */ nitpick: networking subsystem multiline comments have the closing "*/" in a separate line. fixed. > +static void m_can_receive_skb(struct m_can_classdev *cdev, > + struct sk_buff *skb, > + u32 timestamp) > +{ > + if (cdev->is_peripheral) > + can_rx_offload_queue_sorted(&cdev->offload, skb, timestamp); > + else > + netif_receive_skb(skb); > +} > + > static void m_can_read_fifo(struct net_device *dev, u32 rxfs) > { > struct net_device_stats *stats = &dev->stats; > @@ -464,6 +478,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) > struct canfd_frame *cf; > struct sk_buff *skb; > u32 id, fgi, dlc; > + u32 timestamp = 0; > int i; > > /* calculate the fifo get index for where to read data */ > @@ -512,7 +527,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 = FIELD_GET(RX_BUF_RXTS_MASK, dlc); > + > + m_can_receive_skb(cdev, skb, timestamp); > } > > static int m_can_do_rx_poll(struct net_device *dev, int quota) > @@ -546,9 +563,11 @@ 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; > + u32 timestamp = 0; > > netdev_err(dev, "msg lost in rxf0\n"); > > @@ -562,7 +581,10 @@ 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); > + if (cdev->is_peripheral) > + timestamp = m_can_get_timestamp(cdev); > + > + m_can_receive_skb(cdev, skb, timestamp); > > return 1; > } > @@ -574,6 +596,7 @@ static int m_can_handle_lec_err(struct net_device *dev, > struct net_device_stats *stats = &dev->stats; > struct can_frame *cf; > struct sk_buff *skb; > + u32 timestamp = 0; > > cdev->can.can_stats.bus_error++; > stats->rx_errors++; > @@ -619,7 +642,11 @@ static int m_can_handle_lec_err(struct net_device *dev, > > stats->rx_packets++; > stats->rx_bytes += cf->len; > - netif_receive_skb(skb); > + > + if (cdev->is_peripheral) > + timestamp = m_can_get_timestamp(cdev); > + > + m_can_receive_skb(cdev, skb, timestamp); > > return 1; > } > @@ -677,6 +704,7 @@ static int m_can_handle_state_change(struct net_device *dev, > struct sk_buff *skb; > struct can_berr_counter bec; > unsigned int ecr; > + u32 timestamp; Better initialize = 0 here. > > switch (new_state) { > case CAN_STATE_ERROR_WARNING: > @@ -738,7 +766,11 @@ static int m_can_handle_state_change(struct net_device *dev, > > stats->rx_packets++; > stats->rx_bytes += cf->len; > - netif_receive_skb(skb); > + > + if (cdev->is_peripheral) > + timestamp = m_can_get_timestamp(cdev); > + > + m_can_receive_skb(cdev, skb, timestamp); > > return 1; > } > @@ -803,6 +835,7 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus) > struct m_can_classdev *cdev = netdev_priv(dev); > struct can_frame *cf; > struct sk_buff *skb; > + u32 timestamp; Here, too. > > /* propagate the error condition to the CAN stack */ > skb = alloc_can_err_skb(dev, &cf); > @@ -824,7 +857,11 @@ 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); > + > + if (cdev->is_peripheral) > + timestamp = m_can_get_timestamp(cdev); > + > + m_can_receive_skb(cdev, skb, timestamp); > > return 1; > } > @@ -925,6 +962,28 @@ static int m_can_poll(struct napi_struct *napi, int quota) > return work_done; > } > > +/* Echo tx skb and update net stats. Peripherals use rx-offload for echo. > + * timestamp is used for peripherals to ensure correct ordering by rx-offload, > + * and is ignored for non-peripherals */ nitpick about net comment style :) > +static void m_can_tx_update_stats(struct m_can_classdev *cdev, > + unsigned int msg_mark, > + u32 timestamp) > +{ > + struct net_device *dev = cdev->net; > + struct net_device_stats *stats = &dev->stats; > + > + if (cdev->is_peripheral) > + stats->tx_bytes += > + can_rx_offload_get_echo_skb(&cdev->offload, > + msg_mark, > + timestamp, > + NULL); > + else > + stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); > + > + stats->tx_packets++; > +} > + > static void m_can_echo_tx_event(struct net_device *dev) > { > u32 txe_count = 0; > @@ -934,7 +993,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); > @@ -944,21 +1002,24 @@ static void m_can_echo_tx_event(struct net_device *dev) > > /* Get and process all sent elements */ > for (i = 0; i < txe_count; i++) { > + u32 txe; > + u32 timestamp; Here, too. > + > /* retrieve get index */ > fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) >> > TXEFS_EFGI_SHIFT; > > - /* get message marker */ > - msg_mark = (m_can_txe_fifo_read(cdev, fgi, 4) & > - TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; > + /* get message marker, timestamp */ > + txe = m_can_txe_fifo_read(cdev, fgi, 4); > + msg_mark = (txe & TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; > + timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe); > > /* ack txe element */ > m_can_write(cdev, M_CAN_TXEFA, (TXEFA_EFAI_MASK & > (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, timestamp); > } > } > > @@ -966,7 +1027,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)) > @@ -999,8 +1059,12 @@ 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++; > + u32 timestamp = 0; > + > + if (cdev->is_peripheral) > + timestamp = m_can_get_timestamp(cdev); > + m_can_tx_update_stats(cdev, 0, timestamp); > + > can_led_event(dev, CAN_LED_EVENT_TX); > netif_wake_queue(dev); > } > @@ -1461,6 +1525,9 @@ static int m_can_close(struct net_device *dev) > cdev->tx_wq = NULL; > } > > + if (cdev->is_peripheral) > + can_rx_offload_disable(&cdev->offload); > + > close_candev(dev); > can_led_event(dev, CAN_LED_EVENT_STOP); > > @@ -1659,6 +1726,9 @@ static int m_can_open(struct net_device *dev) > goto exit_disable_clks; > } > > + if (cdev->is_peripheral) > + can_rx_offload_enable(&cdev->offload); > + > /* register interrupt handler */ > if (cdev->is_peripheral) { > cdev->tx_skb = NULL; > @@ -1700,6 +1770,8 @@ static int m_can_open(struct net_device *dev) > if (cdev->is_peripheral) > destroy_workqueue(cdev->tx_wq); > out_wq_fail: > + if (cdev->is_peripheral) > + can_rx_offload_disable(&cdev->offload); > close_candev(dev); > exit_disable_clks: > m_can_clk_stop(cdev); > @@ -1853,6 +1925,13 @@ int m_can_class_register(struct m_can_classdev *cdev) > return ret; > } > > + if (cdev->is_peripheral) { > + 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; You need to update the error handling here. I've done this while applying. > @@ -1883,6 +1962,8 @@ EXPORT_SYMBOL_GPL(m_can_class_register); > > void m_can_class_unregister(struct m_can_classdev *cdev) > { > + if (cdev->is_peripheral) > + 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 > #include > +#include > #include > #include > #include > @@ -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 I'll send a v3. regards, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung West/Dortmund | Phone: +49-231-2826-924 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |