summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch')
-rw-r--r--debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch291
1 files changed, 291 insertions, 0 deletions
diff --git a/debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch b/debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch
new file mode 100644
index 000000000..faeeff296
--- /dev/null
+++ b/debian/patches-rt/net-arcnet-Fix-RESET-flag-handling.patch
@@ -0,0 +1,291 @@
+From: "Ahmed S. Darwish" <a.darwish@linutronix.de>
+Date: Thu, 28 Jan 2021 20:48:02 +0100
+Subject: [PATCH] net: arcnet: Fix RESET flag handling
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.10/older/patches-5.10.21-rt34.tar.xz
+
+The main arcnet interrupt handler calls arcnet_close() then
+arcnet_open(), if the RESET status flag is encountered.
+
+This is invalid:
+
+ 1) In general, interrupt handlers should never call ->ndo_stop() and
+ ->ndo_open() functions. They are usually full of blocking calls and
+ other methods that are expected to be called only from drivers
+ init and exit code paths.
+
+ 2) arcnet_close() contains a del_timer_sync(). If the irq handler
+ interrupts the to-be-deleted timer, del_timer_sync() will just loop
+ forever.
+
+ 3) arcnet_close() also calls tasklet_kill(), which has a warning if
+ called from irq context.
+
+ 4) For device reset, the sequence "arcnet_close(); arcnet_open();" is
+ not complete. Some children arcnet drivers have special init/exit
+ code sequences, which then embed a call to arcnet_open() and
+ arcnet_close() accordingly. Check drivers/net/arcnet/com20020.c.
+
+Run the device RESET sequence from a scheduled workqueue instead.
+
+Signed-off-by: Ahmed S. Darwish <a.darwish@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://lore.kernel.org/r/20210128194802.727770-1-a.darwish@linutronix.de
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ drivers/net/arcnet/arc-rimi.c | 4 +-
+ drivers/net/arcnet/arcdevice.h | 6 +++
+ drivers/net/arcnet/arcnet.c | 66 +++++++++++++++++++++++++++++++++++---
+ drivers/net/arcnet/com20020-isa.c | 4 +-
+ drivers/net/arcnet/com20020-pci.c | 2 -
+ drivers/net/arcnet/com20020_cs.c | 2 -
+ drivers/net/arcnet/com90io.c | 4 +-
+ drivers/net/arcnet/com90xx.c | 4 +-
+ 8 files changed, 78 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/arcnet/arc-rimi.c
++++ b/drivers/net/arcnet/arc-rimi.c
+@@ -332,7 +332,7 @@ static int __init arc_rimi_init(void)
+ dev->irq = 9;
+
+ if (arcrimi_probe(dev)) {
+- free_netdev(dev);
++ free_arcdev(dev);
+ return -EIO;
+ }
+
+@@ -349,7 +349,7 @@ static void __exit arc_rimi_exit(void)
+ iounmap(lp->mem_start);
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ free_irq(dev->irq, dev);
+- free_netdev(dev);
++ free_arcdev(dev);
+ }
+
+ #ifndef MODULE
+--- a/drivers/net/arcnet/arcdevice.h
++++ b/drivers/net/arcnet/arcdevice.h
+@@ -298,6 +298,10 @@ struct arcnet_local {
+
+ int excnak_pending; /* We just got an excesive nak interrupt */
+
++ /* RESET flag handling */
++ int reset_in_progress;
++ struct work_struct reset_work;
++
+ struct {
+ uint16_t sequence; /* sequence number (incs with each packet) */
+ __be16 aborted_seq;
+@@ -350,7 +354,9 @@ void arcnet_dump_skb(struct net_device *
+
+ void arcnet_unregister_proto(struct ArcProto *proto);
+ irqreturn_t arcnet_interrupt(int irq, void *dev_id);
++
+ struct net_device *alloc_arcdev(const char *name);
++void free_arcdev(struct net_device *dev);
+
+ int arcnet_open(struct net_device *dev);
+ int arcnet_close(struct net_device *dev);
+--- a/drivers/net/arcnet/arcnet.c
++++ b/drivers/net/arcnet/arcnet.c
+@@ -387,10 +387,44 @@ static void arcnet_timer(struct timer_li
+ struct arcnet_local *lp = from_timer(lp, t, timer);
+ struct net_device *dev = lp->dev;
+
+- if (!netif_carrier_ok(dev)) {
++ spin_lock_irq(&lp->lock);
++
++ if (!lp->reset_in_progress && !netif_carrier_ok(dev)) {
+ netif_carrier_on(dev);
+ netdev_info(dev, "link up\n");
+ }
++
++ spin_unlock_irq(&lp->lock);
++}
++
++static void reset_device_work(struct work_struct *work)
++{
++ struct arcnet_local *lp;
++ struct net_device *dev;
++
++ lp = container_of(work, struct arcnet_local, reset_work);
++ dev = lp->dev;
++
++ /* Do not bring the network interface back up if an ifdown
++ * was already done.
++ */
++ if (!netif_running(dev) || !lp->reset_in_progress)
++ return;
++
++ rtnl_lock();
++
++ /* Do another check, in case of an ifdown that was triggered in
++ * the small race window between the exit condition above and
++ * acquiring RTNL.
++ */
++ if (!netif_running(dev) || !lp->reset_in_progress)
++ goto out;
++
++ dev_close(dev);
++ dev_open(dev, NULL);
++
++out:
++ rtnl_unlock();
+ }
+
+ static void arcnet_reply_tasklet(unsigned long data)
+@@ -452,12 +486,25 @@ struct net_device *alloc_arcdev(const ch
+ lp->dev = dev;
+ spin_lock_init(&lp->lock);
+ timer_setup(&lp->timer, arcnet_timer, 0);
++ INIT_WORK(&lp->reset_work, reset_device_work);
+ }
+
+ return dev;
+ }
+ EXPORT_SYMBOL(alloc_arcdev);
+
++void free_arcdev(struct net_device *dev)
++{
++ struct arcnet_local *lp = netdev_priv(dev);
++
++ /* Do not cancel this at ->ndo_close(), as the workqueue itself
++ * indirectly calls the ifdown path through dev_close().
++ */
++ cancel_work_sync(&lp->reset_work);
++ free_netdev(dev);
++}
++EXPORT_SYMBOL(free_arcdev);
++
+ /* Open/initialize the board. This is called sometime after booting when
+ * the 'ifconfig' program is run.
+ *
+@@ -587,6 +634,10 @@ int arcnet_close(struct net_device *dev)
+
+ /* shut down the card */
+ lp->hw.close(dev);
++
++ /* reset counters */
++ lp->reset_in_progress = 0;
++
+ module_put(lp->hw.owner);
+ return 0;
+ }
+@@ -820,6 +871,9 @@ irqreturn_t arcnet_interrupt(int irq, vo
+
+ spin_lock_irqsave(&lp->lock, flags);
+
++ if (lp->reset_in_progress)
++ goto out;
++
+ /* RESET flag was enabled - if device is not running, we must
+ * clear it right away (but nothing else).
+ */
+@@ -852,11 +906,14 @@ irqreturn_t arcnet_interrupt(int irq, vo
+ if (status & RESETflag) {
+ arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n",
+ status);
+- arcnet_close(dev);
+- arcnet_open(dev);
++
++ lp->reset_in_progress = 1;
++ netif_stop_queue(dev);
++ netif_carrier_off(dev);
++ schedule_work(&lp->reset_work);
+
+ /* get out of the interrupt handler! */
+- break;
++ goto out;
+ }
+ /* RX is inhibited - we must have received something.
+ * Prepare to receive into the next buffer.
+@@ -1052,6 +1109,7 @@ irqreturn_t arcnet_interrupt(int irq, vo
+ udelay(1);
+ lp->hw.intmask(dev, lp->intmask);
+
++out:
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return retval;
+ }
+--- a/drivers/net/arcnet/com20020-isa.c
++++ b/drivers/net/arcnet/com20020-isa.c
+@@ -169,7 +169,7 @@ static int __init com20020_init(void)
+ dev->irq = 9;
+
+ if (com20020isa_probe(dev)) {
+- free_netdev(dev);
++ free_arcdev(dev);
+ return -EIO;
+ }
+
+@@ -182,7 +182,7 @@ static void __exit com20020_exit(void)
+ unregister_netdev(my_dev);
+ free_irq(my_dev->irq, my_dev);
+ release_region(my_dev->base_addr, ARCNET_TOTAL_SIZE);
+- free_netdev(my_dev);
++ free_arcdev(my_dev);
+ }
+
+ #ifndef MODULE
+--- a/drivers/net/arcnet/com20020-pci.c
++++ b/drivers/net/arcnet/com20020-pci.c
+@@ -291,7 +291,7 @@ static void com20020pci_remove(struct pc
+
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+- free_netdev(dev);
++ free_arcdev(dev);
+ }
+ }
+
+--- a/drivers/net/arcnet/com20020_cs.c
++++ b/drivers/net/arcnet/com20020_cs.c
+@@ -177,7 +177,7 @@ static void com20020_detach(struct pcmci
+ dev = info->dev;
+ if (dev) {
+ dev_dbg(&link->dev, "kfree...\n");
+- free_netdev(dev);
++ free_arcdev(dev);
+ }
+ dev_dbg(&link->dev, "kfree2...\n");
+ kfree(info);
+--- a/drivers/net/arcnet/com90io.c
++++ b/drivers/net/arcnet/com90io.c
+@@ -396,7 +396,7 @@ static int __init com90io_init(void)
+ err = com90io_probe(dev);
+
+ if (err) {
+- free_netdev(dev);
++ free_arcdev(dev);
+ return err;
+ }
+
+@@ -419,7 +419,7 @@ static void __exit com90io_exit(void)
+
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+- free_netdev(dev);
++ free_arcdev(dev);
+ }
+
+ module_init(com90io_init)
+--- a/drivers/net/arcnet/com90xx.c
++++ b/drivers/net/arcnet/com90xx.c
+@@ -554,7 +554,7 @@ static int __init com90xx_found(int ioad
+ err_release_mem:
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ err_free_dev:
+- free_netdev(dev);
++ free_arcdev(dev);
+ return -EIO;
+ }
+
+@@ -672,7 +672,7 @@ static void __exit com90xx_exit(void)
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+ release_mem_region(dev->mem_start,
+ dev->mem_end - dev->mem_start + 1);
+- free_netdev(dev);
++ free_arcdev(dev);
+ }
+ }
+