Commit 646f4968 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'linux-can-fixes-for-6.11-20240830' of...

Merge tag 'linux-can-fixes-for-6.11-20240830' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can

Marc Kleine-Budde says:

====================
pull-request: can 2024-08-30

The first patch is by Kuniyuki Iwashima for the CAN BCM protocol that
adds a missing proc entry removal when a device unregistered.

Simon Horman fixes the cleanup in the error cleanup path of the m_can
driver's open function.

Markus Schneider-Pargmann contributes 7 fixes for the m_can driver,
all related to the recently added IRQ coalescing support.

The next 2 patches are by me, target the mcp251xfd driver and fix ring
and coalescing configuration problems when switching from CAN-CC to
CAN-FD mode.

Simon Arlott's patch fixes a possible deadlock in the mcp251x driver.

The last patch is by Martin Jocic for the kvaser_pciefd driver and
fixes a problem with lost IRQs, which result in starvation, under high
load situations.

* tag 'linux-can-fixes-for-6.11-20240830' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can:
  can: kvaser_pciefd: Use a single write when releasing RX buffers
  can: mcp251x: fix deadlock if an interrupt occurs during mcp251x_open
  can: mcp251xfd: mcp251xfd_ring_init(): check TX-coalescing configuration
  can: mcp251xfd: fix ring configuration when switching from CAN-CC to CAN-FD mode
  can: m_can: Limit coalescing to peripheral instances
  can: m_can: Reset cached active_interrupts on start
  can: m_can: disable_all_interrupts, not clear active_interrupts
  can: m_can: Do not cancel timer from within timer
  can: m_can: Remove m_can_rx_peripheral indirection
  can: m_can: Remove coalesing disable in isr during suspend
  can: m_can: Reset coalescing during suspend/resume
  can: m_can: Release irq on error in m_can_open
  can: bcm: Remove proc entry when dev is unregistered.
====================

Link: https://patch.msgid.link/20240830215914.1610393-1-mkl@pengutronix.deSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ef4a99a0 dd885d90
...@@ -1686,6 +1686,7 @@ static irqreturn_t kvaser_pciefd_irq_handler(int irq, void *dev) ...@@ -1686,6 +1686,7 @@ static irqreturn_t kvaser_pciefd_irq_handler(int irq, void *dev)
const struct kvaser_pciefd_irq_mask *irq_mask = pcie->driver_data->irq_mask; const struct kvaser_pciefd_irq_mask *irq_mask = pcie->driver_data->irq_mask;
u32 pci_irq = ioread32(KVASER_PCIEFD_PCI_IRQ_ADDR(pcie)); u32 pci_irq = ioread32(KVASER_PCIEFD_PCI_IRQ_ADDR(pcie));
u32 srb_irq = 0; u32 srb_irq = 0;
u32 srb_release = 0;
int i; int i;
if (!(pci_irq & irq_mask->all)) if (!(pci_irq & irq_mask->all))
...@@ -1699,17 +1700,14 @@ static irqreturn_t kvaser_pciefd_irq_handler(int irq, void *dev) ...@@ -1699,17 +1700,14 @@ static irqreturn_t kvaser_pciefd_irq_handler(int irq, void *dev)
kvaser_pciefd_transmit_irq(pcie->can[i]); kvaser_pciefd_transmit_irq(pcie->can[i]);
} }
if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD0) { if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD0)
/* Reset DMA buffer 0, may trigger new interrupt */ srb_release |= KVASER_PCIEFD_SRB_CMD_RDB0;
iowrite32(KVASER_PCIEFD_SRB_CMD_RDB0,
KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG);
}
if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD1) { if (srb_irq & KVASER_PCIEFD_SRB_IRQ_DPD1)
/* Reset DMA buffer 1, may trigger new interrupt */ srb_release |= KVASER_PCIEFD_SRB_CMD_RDB1;
iowrite32(KVASER_PCIEFD_SRB_CMD_RDB1,
KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG); if (srb_release)
} iowrite32(srb_release, KVASER_PCIEFD_SRB_ADDR(pcie) + KVASER_PCIEFD_SRB_CMD_REG);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
...@@ -483,11 +483,10 @@ static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev) ...@@ -483,11 +483,10 @@ static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev)
{ {
m_can_coalescing_disable(cdev); m_can_coalescing_disable(cdev);
m_can_write(cdev, M_CAN_ILE, 0x0); m_can_write(cdev, M_CAN_ILE, 0x0);
cdev->active_interrupts = 0x0;
if (!cdev->net->irq) { if (!cdev->net->irq) {
dev_dbg(cdev->dev, "Stop hrtimer\n"); dev_dbg(cdev->dev, "Stop hrtimer\n");
hrtimer_cancel(&cdev->hrtimer); hrtimer_try_to_cancel(&cdev->hrtimer);
} }
} }
...@@ -1037,22 +1036,6 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus) ...@@ -1037,22 +1036,6 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus)
return work_done; return work_done;
} }
static int m_can_rx_peripheral(struct net_device *dev, u32 irqstatus)
{
struct m_can_classdev *cdev = netdev_priv(dev);
int work_done;
work_done = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, irqstatus);
/* Don't re-enable interrupts if the driver had a fatal error
* (e.g., FIFO read failure).
*/
if (work_done < 0)
m_can_disable_all_interrupts(cdev);
return work_done;
}
static int m_can_poll(struct napi_struct *napi, int quota) static int m_can_poll(struct napi_struct *napi, int quota)
{ {
struct net_device *dev = napi->dev; struct net_device *dev = napi->dev;
...@@ -1217,16 +1200,18 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir) ...@@ -1217,16 +1200,18 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
} }
static irqreturn_t m_can_isr(int irq, void *dev_id) /* This interrupt handler is called either from the interrupt thread or a
* hrtimer. This has implications like cancelling a timer won't be possible
* blocking.
*/
static int m_can_interrupt_handler(struct m_can_classdev *cdev)
{ {
struct net_device *dev = (struct net_device *)dev_id; struct net_device *dev = cdev->net;
struct m_can_classdev *cdev = netdev_priv(dev);
u32 ir; u32 ir;
int ret;
if (pm_runtime_suspended(cdev->dev)) { if (pm_runtime_suspended(cdev->dev))
m_can_coalescing_disable(cdev);
return IRQ_NONE; return IRQ_NONE;
}
ir = m_can_read(cdev, M_CAN_IR); ir = m_can_read(cdev, M_CAN_IR);
m_can_coalescing_update(cdev, ir); m_can_coalescing_update(cdev, ir);
...@@ -1250,11 +1235,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -1250,11 +1235,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
m_can_disable_all_interrupts(cdev); m_can_disable_all_interrupts(cdev);
napi_schedule(&cdev->napi); napi_schedule(&cdev->napi);
} else { } else {
int pkts; ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
if (ret < 0)
pkts = m_can_rx_peripheral(dev, ir); return ret;
if (pkts < 0)
goto out_fail;
} }
} }
...@@ -1272,8 +1255,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -1272,8 +1255,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
} else { } else {
if (ir & (IR_TEFN | IR_TEFW)) { if (ir & (IR_TEFN | IR_TEFW)) {
/* New TX FIFO Element arrived */ /* New TX FIFO Element arrived */
if (m_can_echo_tx_event(dev) != 0) ret = m_can_echo_tx_event(dev);
goto out_fail; if (ret != 0)
return ret;
} }
} }
...@@ -1281,16 +1265,31 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) ...@@ -1281,16 +1265,31 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
can_rx_offload_threaded_irq_finish(&cdev->offload); can_rx_offload_threaded_irq_finish(&cdev->offload);
return IRQ_HANDLED; return IRQ_HANDLED;
}
out_fail: static irqreturn_t m_can_isr(int irq, void *dev_id)
m_can_disable_all_interrupts(cdev); {
return IRQ_HANDLED; struct net_device *dev = (struct net_device *)dev_id;
struct m_can_classdev *cdev = netdev_priv(dev);
int ret;
ret = m_can_interrupt_handler(cdev);
if (ret < 0) {
m_can_disable_all_interrupts(cdev);
return IRQ_HANDLED;
}
return ret;
} }
static enum hrtimer_restart m_can_coalescing_timer(struct hrtimer *timer) static enum hrtimer_restart m_can_coalescing_timer(struct hrtimer *timer)
{ {
struct m_can_classdev *cdev = container_of(timer, struct m_can_classdev, hrtimer); struct m_can_classdev *cdev = container_of(timer, struct m_can_classdev, hrtimer);
if (cdev->can.state == CAN_STATE_BUS_OFF ||
cdev->can.state == CAN_STATE_STOPPED)
return HRTIMER_NORESTART;
irq_wake_thread(cdev->net->irq, cdev->net); irq_wake_thread(cdev->net->irq, cdev->net);
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
...@@ -1542,6 +1541,7 @@ static int m_can_chip_config(struct net_device *dev) ...@@ -1542,6 +1541,7 @@ static int m_can_chip_config(struct net_device *dev)
else else
interrupts &= ~(IR_ERR_LEC_31X); interrupts &= ~(IR_ERR_LEC_31X);
} }
cdev->active_interrupts = 0;
m_can_interrupt_enable(cdev, interrupts); m_can_interrupt_enable(cdev, interrupts);
/* route all interrupts to INT0 */ /* route all interrupts to INT0 */
...@@ -1991,8 +1991,17 @@ static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer) ...@@ -1991,8 +1991,17 @@ static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
{ {
struct m_can_classdev *cdev = container_of(timer, struct struct m_can_classdev *cdev = container_of(timer, struct
m_can_classdev, hrtimer); m_can_classdev, hrtimer);
int ret;
if (cdev->can.state == CAN_STATE_BUS_OFF ||
cdev->can.state == CAN_STATE_STOPPED)
return HRTIMER_NORESTART;
m_can_isr(0, cdev->net); ret = m_can_interrupt_handler(cdev);
/* On error or if napi is scheduled to read, stop the timer */
if (ret < 0 || napi_is_scheduled(&cdev->napi))
return HRTIMER_NORESTART;
hrtimer_forward_now(timer, ms_to_ktime(HRTIMER_POLL_INTERVAL_MS)); hrtimer_forward_now(timer, ms_to_ktime(HRTIMER_POLL_INTERVAL_MS));
...@@ -2052,7 +2061,7 @@ static int m_can_open(struct net_device *dev) ...@@ -2052,7 +2061,7 @@ static int m_can_open(struct net_device *dev)
/* start the m_can controller */ /* start the m_can controller */
err = m_can_start(dev); err = m_can_start(dev);
if (err) if (err)
goto exit_irq_fail; goto exit_start_fail;
if (!cdev->is_peripheral) if (!cdev->is_peripheral)
napi_enable(&cdev->napi); napi_enable(&cdev->napi);
...@@ -2061,6 +2070,9 @@ static int m_can_open(struct net_device *dev) ...@@ -2061,6 +2070,9 @@ static int m_can_open(struct net_device *dev)
return 0; return 0;
exit_start_fail:
if (cdev->is_peripheral || dev->irq)
free_irq(dev->irq, dev);
exit_irq_fail: exit_irq_fail:
if (cdev->is_peripheral) if (cdev->is_peripheral)
destroy_workqueue(cdev->tx_wq); destroy_workqueue(cdev->tx_wq);
...@@ -2172,7 +2184,7 @@ static int m_can_set_coalesce(struct net_device *dev, ...@@ -2172,7 +2184,7 @@ static int m_can_set_coalesce(struct net_device *dev,
return 0; return 0;
} }
static const struct ethtool_ops m_can_ethtool_ops = { static const struct ethtool_ops m_can_ethtool_ops_coalescing = {
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
ETHTOOL_COALESCE_TX_USECS_IRQ | ETHTOOL_COALESCE_TX_USECS_IRQ |
...@@ -2183,18 +2195,20 @@ static const struct ethtool_ops m_can_ethtool_ops = { ...@@ -2183,18 +2195,20 @@ static const struct ethtool_ops m_can_ethtool_ops = {
.set_coalesce = m_can_set_coalesce, .set_coalesce = m_can_set_coalesce,
}; };
static const struct ethtool_ops m_can_ethtool_ops_polling = { static const struct ethtool_ops m_can_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info, .get_ts_info = ethtool_op_get_ts_info,
}; };
static int register_m_can_dev(struct net_device *dev) static int register_m_can_dev(struct m_can_classdev *cdev)
{ {
struct net_device *dev = cdev->net;
dev->flags |= IFF_ECHO; /* we support local echo */ dev->flags |= IFF_ECHO; /* we support local echo */
dev->netdev_ops = &m_can_netdev_ops; dev->netdev_ops = &m_can_netdev_ops;
if (dev->irq) if (dev->irq && cdev->is_peripheral)
dev->ethtool_ops = &m_can_ethtool_ops; dev->ethtool_ops = &m_can_ethtool_ops_coalescing;
else else
dev->ethtool_ops = &m_can_ethtool_ops_polling; dev->ethtool_ops = &m_can_ethtool_ops;
return register_candev(dev); return register_candev(dev);
} }
...@@ -2380,7 +2394,7 @@ int m_can_class_register(struct m_can_classdev *cdev) ...@@ -2380,7 +2394,7 @@ int m_can_class_register(struct m_can_classdev *cdev)
if (ret) if (ret)
goto rx_offload_del; goto rx_offload_del;
ret = register_m_can_dev(cdev->net); ret = register_m_can_dev(cdev);
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);
...@@ -2427,12 +2441,15 @@ int m_can_class_suspend(struct device *dev) ...@@ -2427,12 +2441,15 @@ int m_can_class_suspend(struct device *dev)
netif_device_detach(ndev); netif_device_detach(ndev);
/* leave the chip running with rx interrupt enabled if it is /* leave the chip running with rx interrupt enabled if it is
* used as a wake-up source. * used as a wake-up source. Coalescing needs to be reset then,
* the timer is cancelled here, interrupts are done in resume.
*/ */
if (cdev->pm_wake_source) if (cdev->pm_wake_source) {
hrtimer_cancel(&cdev->hrtimer);
m_can_write(cdev, M_CAN_IE, IR_RF0N); m_can_write(cdev, M_CAN_IE, IR_RF0N);
else } else {
m_can_stop(ndev); m_can_stop(ndev);
}
m_can_clk_stop(cdev); m_can_clk_stop(cdev);
} }
...@@ -2462,6 +2479,13 @@ int m_can_class_resume(struct device *dev) ...@@ -2462,6 +2479,13 @@ int m_can_class_resume(struct device *dev)
return ret; return ret;
if (cdev->pm_wake_source) { if (cdev->pm_wake_source) {
/* Restore active interrupts but disable coalescing as
* we may have missed important waterlevel interrupts
* between suspend and resume. Timers are already
* stopped in suspend. Here we enable all interrupts
* again.
*/
cdev->active_interrupts |= IR_RF0N | IR_TEFN;
m_can_write(cdev, M_CAN_IE, cdev->active_interrupts); m_can_write(cdev, M_CAN_IE, cdev->active_interrupts);
} else { } else {
ret = m_can_start(ndev); ret = m_can_start(ndev);
......
...@@ -752,7 +752,7 @@ static int mcp251x_hw_wake(struct spi_device *spi) ...@@ -752,7 +752,7 @@ static int mcp251x_hw_wake(struct spi_device *spi)
int ret; int ret;
/* Force wakeup interrupt to wake device, but don't execute IST */ /* Force wakeup interrupt to wake device, but don't execute IST */
disable_irq(spi->irq); disable_irq_nosync(spi->irq);
mcp251x_write_2regs(spi, CANINTE, CANINTE_WAKIE, CANINTF_WAKIF); mcp251x_write_2regs(spi, CANINTE, CANINTE_WAKIE, CANINTF_WAKIF);
/* Wait for oscillator startup timer after wake up */ /* Wait for oscillator startup timer after wake up */
......
...@@ -97,7 +97,16 @@ void can_ram_get_layout(struct can_ram_layout *layout, ...@@ -97,7 +97,16 @@ void can_ram_get_layout(struct can_ram_layout *layout,
if (ring) { if (ring) {
u8 num_rx_coalesce = 0, num_tx_coalesce = 0; u8 num_rx_coalesce = 0, num_tx_coalesce = 0;
num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending); /* If the ring parameters have been configured in
* CAN-CC mode, but and we are in CAN-FD mode now,
* they might be to big. Use the default CAN-FD values
* in this case.
*/
num_rx = ring->rx_pending;
if (num_rx > layout->max_rx)
num_rx = layout->default_rx;
num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
/* The ethtool doc says: /* The ethtool doc says:
* To disable coalescing, set usecs = 0 and max_frames = 1. * To disable coalescing, set usecs = 0 and max_frames = 1.
......
...@@ -290,7 +290,7 @@ int mcp251xfd_ring_init(struct mcp251xfd_priv *priv) ...@@ -290,7 +290,7 @@ int mcp251xfd_ring_init(struct mcp251xfd_priv *priv)
const struct mcp251xfd_rx_ring *rx_ring; const struct mcp251xfd_rx_ring *rx_ring;
u16 base = 0, ram_used; u16 base = 0, ram_used;
u8 fifo_nr = 1; u8 fifo_nr = 1;
int i; int err = 0, i;
netdev_reset_queue(priv->ndev); netdev_reset_queue(priv->ndev);
...@@ -386,10 +386,18 @@ int mcp251xfd_ring_init(struct mcp251xfd_priv *priv) ...@@ -386,10 +386,18 @@ int mcp251xfd_ring_init(struct mcp251xfd_priv *priv)
netdev_err(priv->ndev, netdev_err(priv->ndev,
"Error during ring configuration, using more RAM (%u bytes) than available (%u bytes).\n", "Error during ring configuration, using more RAM (%u bytes) than available (%u bytes).\n",
ram_used, MCP251XFD_RAM_SIZE); ram_used, MCP251XFD_RAM_SIZE);
return -ENOMEM; err = -ENOMEM;
} }
return 0; if (priv->tx_obj_num_coalesce_irq &&
priv->tx_obj_num_coalesce_irq * 2 != priv->tx->obj_num) {
netdev_err(priv->ndev,
"Error during ring configuration, number of TEF coalescing buffers (%u) must be half of TEF buffers (%u).\n",
priv->tx_obj_num_coalesce_irq, priv->tx->obj_num);
err = -EINVAL;
}
return err;
} }
void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) void mcp251xfd_ring_free(struct mcp251xfd_priv *priv)
...@@ -469,11 +477,25 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) ...@@ -469,11 +477,25 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv)
/* switching from CAN-2.0 to CAN-FD mode or vice versa */ /* switching from CAN-2.0 to CAN-FD mode or vice versa */
if (fd_mode != test_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags)) { if (fd_mode != test_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags)) {
const struct ethtool_ringparam ring = {
.rx_pending = priv->rx_obj_num,
.tx_pending = priv->tx->obj_num,
};
const struct ethtool_coalesce ec = {
.rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq,
.rx_max_coalesced_frames_irq = priv->rx_obj_num_coalesce_irq,
.tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq,
.tx_max_coalesced_frames_irq = priv->tx_obj_num_coalesce_irq,
};
struct can_ram_layout layout; struct can_ram_layout layout;
can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, &ec, fd_mode);
priv->rx_obj_num = layout.default_rx;
tx_ring->obj_num = layout.default_tx; priv->rx_obj_num = layout.cur_rx;
priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
tx_ring->obj_num = layout.cur_tx;
priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
} }
if (fd_mode) { if (fd_mode) {
......
...@@ -1470,6 +1470,10 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg, ...@@ -1470,6 +1470,10 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
/* remove device reference, if this is our bound device */ /* remove device reference, if this is our bound device */
if (bo->bound && bo->ifindex == dev->ifindex) { if (bo->bound && bo->ifindex == dev->ifindex) {
#if IS_ENABLED(CONFIG_PROC_FS)
if (sock_net(sk)->can.bcmproc_dir && bo->bcm_proc_read)
remove_proc_entry(bo->procname, sock_net(sk)->can.bcmproc_dir);
#endif
bo->bound = 0; bo->bound = 0;
bo->ifindex = 0; bo->ifindex = 0;
notify_enodev = 1; notify_enodev = 1;
......
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