Commit 1be37d3b authored by Torin Cooper-Bennun's avatar Torin Cooper-Bennun Committed by Marc Kleine-Budde

can: m_can: fix periph RX path: use rx-offload to ensure skbs are sent from softirq context

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.

Link: https://lore.kernel.org/r/20210308102427.63916-4-torin@maxiluxsystems.comSigned-off-by: default avatarTorin Cooper-Bennun <torin@maxiluxsystems.com>
[mkl: m_can_class_register(): update error handling]
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent df06fd67
...@@ -457,6 +457,21 @@ static void m_can_clean(struct net_device *net) ...@@ -457,6 +457,21 @@ 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.
*/
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) static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
{ {
struct net_device_stats *stats = &dev->stats; struct net_device_stats *stats = &dev->stats;
...@@ -464,6 +479,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -464,6 +479,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
struct canfd_frame *cf; struct canfd_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
u32 id, fgi, dlc; u32 id, fgi, dlc;
u32 timestamp = 0;
int i; int i;
/* calculate the fifo get index for where to read data */ /* calculate the fifo get index for where to read data */
...@@ -512,7 +528,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) ...@@ -512,7 +528,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->len; 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) static int m_can_do_rx_poll(struct net_device *dev, int quota)
...@@ -543,9 +561,11 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota) ...@@ -543,9 +561,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) 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 net_device_stats *stats = &dev->stats;
struct sk_buff *skb; struct sk_buff *skb;
struct can_frame *frame; struct can_frame *frame;
u32 timestamp = 0;
netdev_err(dev, "msg lost in rxf0\n"); netdev_err(dev, "msg lost in rxf0\n");
...@@ -559,7 +579,10 @@ static int m_can_handle_lost_msg(struct net_device *dev) ...@@ -559,7 +579,10 @@ static int m_can_handle_lost_msg(struct net_device *dev)
frame->can_id |= CAN_ERR_CRTL; frame->can_id |= CAN_ERR_CRTL;
frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 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; return 1;
} }
...@@ -571,6 +594,7 @@ static int m_can_handle_lec_err(struct net_device *dev, ...@@ -571,6 +594,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
struct net_device_stats *stats = &dev->stats; struct net_device_stats *stats = &dev->stats;
struct can_frame *cf; struct can_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
u32 timestamp = 0;
cdev->can.can_stats.bus_error++; cdev->can.can_stats.bus_error++;
stats->rx_errors++; stats->rx_errors++;
...@@ -616,7 +640,11 @@ static int m_can_handle_lec_err(struct net_device *dev, ...@@ -616,7 +640,11 @@ static int m_can_handle_lec_err(struct net_device *dev,
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->len; 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; return 1;
} }
...@@ -674,6 +702,7 @@ static int m_can_handle_state_change(struct net_device *dev, ...@@ -674,6 +702,7 @@ static int m_can_handle_state_change(struct net_device *dev,
struct sk_buff *skb; struct sk_buff *skb;
struct can_berr_counter bec; struct can_berr_counter bec;
unsigned int ecr; unsigned int ecr;
u32 timestamp = 0;
switch (new_state) { switch (new_state) {
case CAN_STATE_ERROR_WARNING: case CAN_STATE_ERROR_WARNING:
...@@ -735,7 +764,11 @@ static int m_can_handle_state_change(struct net_device *dev, ...@@ -735,7 +764,11 @@ static int m_can_handle_state_change(struct net_device *dev,
stats->rx_packets++; stats->rx_packets++;
stats->rx_bytes += cf->len; 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; return 1;
} }
...@@ -800,6 +833,7 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus) ...@@ -800,6 +833,7 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus)
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
struct can_frame *cf; struct can_frame *cf;
struct sk_buff *skb; struct sk_buff *skb;
u32 timestamp = 0;
/* propagate the error condition to the CAN stack */ /* propagate the error condition to the CAN stack */
skb = alloc_can_err_skb(dev, &cf); skb = alloc_can_err_skb(dev, &cf);
...@@ -821,7 +855,11 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus) ...@@ -821,7 +855,11 @@ static int m_can_handle_protocol_error(struct net_device *dev, u32 irqstatus)
netdev_dbg(dev, "allocation of skb failed\n"); netdev_dbg(dev, "allocation of skb failed\n");
return 0; 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; return 1;
} }
...@@ -922,6 +960,29 @@ static int m_can_poll(struct napi_struct *napi, int quota) ...@@ -922,6 +960,29 @@ static int m_can_poll(struct napi_struct *napi, int quota)
return work_done; 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.
*/
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) static void m_can_echo_tx_event(struct net_device *dev)
{ {
u32 txe_count = 0; u32 txe_count = 0;
...@@ -931,7 +992,6 @@ static void m_can_echo_tx_event(struct net_device *dev) ...@@ -931,7 +992,6 @@ static void m_can_echo_tx_event(struct net_device *dev)
unsigned int msg_mark; unsigned int msg_mark;
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
/* read tx event fifo status */ /* read tx event fifo status */
m_can_txefs = m_can_read(cdev, M_CAN_TXEFS); m_can_txefs = m_can_read(cdev, M_CAN_TXEFS);
...@@ -941,21 +1001,23 @@ static void m_can_echo_tx_event(struct net_device *dev) ...@@ -941,21 +1001,23 @@ static void m_can_echo_tx_event(struct net_device *dev)
/* Get and process all sent elements */ /* Get and process all sent elements */
for (i = 0; i < txe_count; i++) { for (i = 0; i < txe_count; i++) {
u32 txe, timestamp = 0;
/* retrieve get index */ /* retrieve get index */
fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) >> fgi = (m_can_read(cdev, M_CAN_TXEFS) & TXEFS_EFGI_MASK) >>
TXEFS_EFGI_SHIFT; TXEFS_EFGI_SHIFT;
/* get message marker */ /* get message marker, timestamp */
msg_mark = (m_can_txe_fifo_read(cdev, fgi, 4) & txe = m_can_txe_fifo_read(cdev, fgi, 4);
TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; msg_mark = (txe & TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT;
timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe);
/* ack txe element */ /* ack txe element */
m_can_write(cdev, M_CAN_TXEFA, (TXEFA_EFAI_MASK & m_can_write(cdev, M_CAN_TXEFA, (TXEFA_EFAI_MASK &
(fgi << TXEFA_EFAI_SHIFT))); (fgi << TXEFA_EFAI_SHIFT)));
/* update stats */ /* update stats */
stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); m_can_tx_update_stats(cdev, msg_mark, timestamp);
stats->tx_packets++;
} }
} }
...@@ -963,7 +1025,6 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -963,7 +1025,6 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
{ {
struct net_device *dev = (struct net_device *)dev_id; struct net_device *dev = (struct net_device *)dev_id;
struct m_can_classdev *cdev = netdev_priv(dev); struct m_can_classdev *cdev = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
u32 ir; u32 ir;
if (pm_runtime_suspended(cdev->dev)) if (pm_runtime_suspended(cdev->dev))
...@@ -996,8 +1057,12 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -996,8 +1057,12 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
if (cdev->version == 30) { if (cdev->version == 30) {
if (ir & IR_TC) { if (ir & IR_TC) {
/* Transmission Complete Interrupt*/ /* Transmission Complete Interrupt*/
stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); u32 timestamp = 0;
stats->tx_packets++;
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); can_led_event(dev, CAN_LED_EVENT_TX);
netif_wake_queue(dev); netif_wake_queue(dev);
} }
...@@ -1458,6 +1523,9 @@ static int m_can_close(struct net_device *dev) ...@@ -1458,6 +1523,9 @@ static int m_can_close(struct net_device *dev)
cdev->tx_wq = NULL; cdev->tx_wq = NULL;
} }
if (cdev->is_peripheral)
can_rx_offload_disable(&cdev->offload);
close_candev(dev); close_candev(dev);
can_led_event(dev, CAN_LED_EVENT_STOP); can_led_event(dev, CAN_LED_EVENT_STOP);
...@@ -1656,6 +1724,9 @@ static int m_can_open(struct net_device *dev) ...@@ -1656,6 +1724,9 @@ static int m_can_open(struct net_device *dev)
goto exit_disable_clks; goto exit_disable_clks;
} }
if (cdev->is_peripheral)
can_rx_offload_enable(&cdev->offload);
/* register interrupt handler */ /* register interrupt handler */
if (cdev->is_peripheral) { if (cdev->is_peripheral) {
cdev->tx_skb = NULL; cdev->tx_skb = NULL;
...@@ -1697,6 +1768,8 @@ static int m_can_open(struct net_device *dev) ...@@ -1697,6 +1768,8 @@ static int m_can_open(struct net_device *dev)
if (cdev->is_peripheral) if (cdev->is_peripheral)
destroy_workqueue(cdev->tx_wq); destroy_workqueue(cdev->tx_wq);
out_wq_fail: out_wq_fail:
if (cdev->is_peripheral)
can_rx_offload_disable(&cdev->offload);
close_candev(dev); close_candev(dev);
exit_disable_clks: exit_disable_clks:
m_can_clk_stop(cdev); m_can_clk_stop(cdev);
...@@ -1845,15 +1918,22 @@ int m_can_class_register(struct m_can_classdev *cdev) ...@@ -1845,15 +1918,22 @@ int m_can_class_register(struct m_can_classdev *cdev)
return ret; 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); ret = m_can_dev_setup(cdev);
if (ret) if (ret)
goto clk_disable; goto rx_offload_del;
ret = register_m_can_dev(cdev->net); ret = register_m_can_dev(cdev->net);
if (ret) { if (ret) {
dev_err(cdev->dev, "registering %s failed (err=%d)\n", dev_err(cdev->dev, "registering %s failed (err=%d)\n",
cdev->net->name, ret); cdev->net->name, ret);
goto clk_disable; goto rx_offload_del;
} }
devm_can_led_init(cdev->net); devm_can_led_init(cdev->net);
...@@ -1866,6 +1946,13 @@ int m_can_class_register(struct m_can_classdev *cdev) ...@@ -1866,6 +1946,13 @@ int m_can_class_register(struct m_can_classdev *cdev)
/* Probe finished /* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened * Stop clocks. They will be reactivated once the M_CAN device is opened
*/ */
m_can_clk_stop(cdev);
return 0;
rx_offload_del:
if (cdev->is_peripheral)
can_rx_offload_del(&cdev->offload);
clk_disable: clk_disable:
m_can_clk_stop(cdev); m_can_clk_stop(cdev);
...@@ -1875,6 +1962,8 @@ EXPORT_SYMBOL_GPL(m_can_class_register); ...@@ -1875,6 +1962,8 @@ EXPORT_SYMBOL_GPL(m_can_class_register);
void m_can_class_unregister(struct m_can_classdev *cdev) void m_can_class_unregister(struct m_can_classdev *cdev)
{ {
if (cdev->is_peripheral)
can_rx_offload_del(&cdev->offload);
unregister_candev(cdev->net); unregister_candev(cdev->net);
} }
EXPORT_SYMBOL_GPL(m_can_class_unregister); EXPORT_SYMBOL_GPL(m_can_class_unregister);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/can/core.h> #include <linux/can/core.h>
#include <linux/can/led.h> #include <linux/can/led.h>
#include <linux/can/rx-offload.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
...@@ -71,6 +72,7 @@ struct m_can_ops { ...@@ -71,6 +72,7 @@ struct m_can_ops {
struct m_can_classdev { struct m_can_classdev {
struct can_priv can; struct can_priv can;
struct can_rx_offload offload;
struct napi_struct napi; struct napi_struct napi;
struct net_device *net; struct net_device *net;
struct device *dev; struct device *dev;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment