]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Jan 2020 08:53:06 +0000 (09:53 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Jan 2020 08:53:06 +0000 (09:53 +0100)
added patches:
libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch
media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch
net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch
net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch
net-sonic-clear-interrupt-flags-immediately.patch
net-sonic-fix-cam-initialization.patch
net-sonic-fix-command-register-usage.patch
net-sonic-fix-interface-error-stats-collection.patch
net-sonic-fix-receive-buffer-handling.patch
net-sonic-fix-receive-buffer-replenishment.patch
net-sonic-improve-receive-descriptor-status-flag-check.patch
net-sonic-prevent-tx-watchdog-timeout.patch
net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch
net-sonic-use-mmio-accessors.patch

15 files changed:
queue-5.4/libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch [new file with mode: 0644]
queue-5.4/media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch [new file with mode: 0644]
queue-5.4/net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch [new file with mode: 0644]
queue-5.4/net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch [new file with mode: 0644]
queue-5.4/net-sonic-clear-interrupt-flags-immediately.patch [new file with mode: 0644]
queue-5.4/net-sonic-fix-cam-initialization.patch [new file with mode: 0644]
queue-5.4/net-sonic-fix-command-register-usage.patch [new file with mode: 0644]
queue-5.4/net-sonic-fix-interface-error-stats-collection.patch [new file with mode: 0644]
queue-5.4/net-sonic-fix-receive-buffer-handling.patch [new file with mode: 0644]
queue-5.4/net-sonic-fix-receive-buffer-replenishment.patch [new file with mode: 0644]
queue-5.4/net-sonic-improve-receive-descriptor-status-flag-check.patch [new file with mode: 0644]
queue-5.4/net-sonic-prevent-tx-watchdog-timeout.patch [new file with mode: 0644]
queue-5.4/net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch [new file with mode: 0644]
queue-5.4/net-sonic-use-mmio-accessors.patch [new file with mode: 0644]
queue-5.4/series

diff --git a/queue-5.4/libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch b/queue-5.4/libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch
new file mode 100644 (file)
index 0000000..ab07e40
--- /dev/null
@@ -0,0 +1,68 @@
+From e5e884b42639c74b5b57dc277909915c0aefc8bb Mon Sep 17 00:00:00 2001
+From: Wen Huang <huangwenabc@gmail.com>
+Date: Thu, 28 Nov 2019 18:51:04 +0800
+Subject: libertas: Fix two buffer overflows at parsing bss descriptor
+
+From: Wen Huang <huangwenabc@gmail.com>
+
+commit e5e884b42639c74b5b57dc277909915c0aefc8bb upstream.
+
+add_ie_rates() copys rates without checking the length
+in bss descriptor from remote AP.when victim connects to
+remote attacker, this may trigger buffer overflow.
+lbs_ibss_join_existing() copys rates without checking the length
+in bss descriptor from remote IBSS node.when victim connects to
+remote attacker, this may trigger buffer overflow.
+Fix them by putting the length check before performing copy.
+
+This fix addresses CVE-2019-14896 and CVE-2019-14897.
+This also fix build warning of mixed declarations and code.
+
+Reported-by: kbuild test robot <lkp@intel.com>
+Signed-off-by: Wen Huang <huangwenabc@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/marvell/libertas/cfg.c |   16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/wireless/marvell/libertas/cfg.c
++++ b/drivers/net/wireless/marvell/libertas/cfg.c
+@@ -273,6 +273,10 @@ add_ie_rates(u8 *tlv, const u8 *ie, int
+       int hw, ap, ap_max = ie[1];
+       u8 hw_rate;
++      if (ap_max > MAX_RATES) {
++              lbs_deb_assoc("invalid rates\n");
++              return tlv;
++      }
+       /* Advance past IE header */
+       ie += 2;
+@@ -1717,6 +1721,9 @@ static int lbs_ibss_join_existing(struct
+       struct cmd_ds_802_11_ad_hoc_join cmd;
+       u8 preamble = RADIO_PREAMBLE_SHORT;
+       int ret = 0;
++      int hw, i;
++      u8 rates_max;
++      u8 *rates;
+       /* TODO: set preamble based on scan result */
+       ret = lbs_set_radio(priv, preamble, 1);
+@@ -1775,9 +1782,12 @@ static int lbs_ibss_join_existing(struct
+       if (!rates_eid) {
+               lbs_add_rates(cmd.bss.rates);
+       } else {
+-              int hw, i;
+-              u8 rates_max = rates_eid[1];
+-              u8 *rates = cmd.bss.rates;
++              rates_max = rates_eid[1];
++              if (rates_max > MAX_RATES) {
++                      lbs_deb_join("invalid rates");
++                      goto out;
++              }
++              rates = cmd.bss.rates;
+               for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+                       u8 hw_rate = lbs_rates[hw].bitrate / 5;
+                       for (i = 0; i < rates_max; i++) {
diff --git a/queue-5.4/media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch b/queue-5.4/media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch
new file mode 100644 (file)
index 0000000..8f85c58
--- /dev/null
@@ -0,0 +1,108 @@
+From ee8951e56c0f960b9621636603a822811cef3158 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Sun, 10 Nov 2019 07:27:04 +0100
+Subject: media: v4l2-ioctl.c: zero reserved fields for S/TRY_FMT
+
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+
+commit ee8951e56c0f960b9621636603a822811cef3158 upstream.
+
+v4l2_vbi_format, v4l2_sliced_vbi_format and v4l2_sdr_format
+have a reserved array at the end that should be zeroed by drivers
+as per the V4L2 spec. Older drivers often do not do this, so just
+handle this in the core.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c |   24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1605,12 +1605,12 @@ static int v4l_s_fmt(const struct v4l2_i
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.vbi);
++              CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
+               return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sliced);
++              CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
+               return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vid_out))
+@@ -1636,22 +1636,22 @@ static int v4l_s_fmt(const struct v4l2_i
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_vbi_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.vbi);
++              CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
+               return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sliced);
++              CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
+               return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SDR_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sdr);
++              CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
+               return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SDR_OUTPUT:
+               if (unlikely(!ops->vidioc_s_fmt_sdr_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sdr);
++              CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
+               return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
+       case V4L2_BUF_TYPE_META_CAPTURE:
+               if (unlikely(!ops->vidioc_s_fmt_meta_cap))
+@@ -1707,12 +1707,12 @@ static int v4l_try_fmt(const struct v4l2
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.vbi);
++              CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
+               return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sliced);
++              CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
+               return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vid_out))
+@@ -1738,22 +1738,22 @@ static int v4l_try_fmt(const struct v4l2
+       case V4L2_BUF_TYPE_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_vbi_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.vbi);
++              CLEAR_AFTER_FIELD(p, fmt.vbi.flags);
+               return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sliced);
++              CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
+               return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+       case V4L2_BUF_TYPE_SDR_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sdr);
++              CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
+               return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
+       case V4L2_BUF_TYPE_SDR_OUTPUT:
+               if (unlikely(!ops->vidioc_try_fmt_sdr_out))
+                       break;
+-              CLEAR_AFTER_FIELD(p, fmt.sdr);
++              CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize);
+               return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
+       case V4L2_BUF_TYPE_META_CAPTURE:
+               if (unlikely(!ops->vidioc_try_fmt_meta_cap))
diff --git a/queue-5.4/net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch b/queue-5.4/net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch
new file mode 100644 (file)
index 0000000..6eaf2a1
--- /dev/null
@@ -0,0 +1,152 @@
+From 865ad2f2201dc18685ba2686f13217f8b3a9c52c Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Add mutual exclusion for accessing shared state
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 865ad2f2201dc18685ba2686f13217f8b3a9c52c upstream.
+
+The netif_stop_queue() call in sonic_send_packet() races with the
+netif_wake_queue() call in sonic_interrupt(). This causes issues
+like "NETDEV WATCHDOG: eth0 (macsonic): transmit queue 0 timed out".
+Fix this by disabling interrupts when accessing tx_skb[] and next_tx.
+Update a comment to clarify the synchronization properties.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   49 +++++++++++++++++++++++++----------
+ drivers/net/ethernet/natsemi/sonic.h |    1 
+ 2 files changed, 36 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -64,6 +64,8 @@ static int sonic_open(struct net_device
+       netif_dbg(lp, ifup, dev, "%s: initializing sonic driver\n", __func__);
++      spin_lock_init(&lp->lock);
++
+       for (i = 0; i < SONIC_NUM_RRS; i++) {
+               struct sk_buff *skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
+               if (skb == NULL) {
+@@ -206,8 +208,6 @@ static void sonic_tx_timeout(struct net_
+  *   wake the tx queue
+  * Concurrently with all of this, the SONIC is potentially writing to
+  * the status flags of the TDs.
+- * Until some mutual exclusion is added, this code will not work with SMP. However,
+- * MIPS Jazz machines and m68k Macs were all uni-processor machines.
+  */
+ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev)
+@@ -215,7 +215,8 @@ static int sonic_send_packet(struct sk_b
+       struct sonic_local *lp = netdev_priv(dev);
+       dma_addr_t laddr;
+       int length;
+-      int entry = lp->next_tx;
++      int entry;
++      unsigned long flags;
+       netif_dbg(lp, tx_queued, dev, "%s: skb=%p\n", __func__, skb);
+@@ -237,6 +238,10 @@ static int sonic_send_packet(struct sk_b
+               return NETDEV_TX_OK;
+       }
++      spin_lock_irqsave(&lp->lock, flags);
++
++      entry = lp->next_tx;
++
+       sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0);       /* clear status */
+       sonic_tda_put(dev, entry, SONIC_TD_FRAG_COUNT, 1);   /* single fragment */
+       sonic_tda_put(dev, entry, SONIC_TD_PKTSIZE, length); /* length of packet */
+@@ -246,10 +251,6 @@ static int sonic_send_packet(struct sk_b
+       sonic_tda_put(dev, entry, SONIC_TD_LINK,
+               sonic_tda_get(dev, entry, SONIC_TD_LINK) | SONIC_EOL);
+-      /*
+-       * Must set tx_skb[entry] only after clearing status, and
+-       * before clearing EOL and before stopping queue
+-       */
+       wmb();
+       lp->tx_len[entry] = length;
+       lp->tx_laddr[entry] = laddr;
+@@ -272,6 +273,8 @@ static int sonic_send_packet(struct sk_b
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
++      spin_unlock_irqrestore(&lp->lock, flags);
++
+       return NETDEV_TX_OK;
+ }
+@@ -284,9 +287,21 @@ static irqreturn_t sonic_interrupt(int i
+       struct net_device *dev = dev_id;
+       struct sonic_local *lp = netdev_priv(dev);
+       int status;
++      unsigned long flags;
++
++      /* The lock has two purposes. Firstly, it synchronizes sonic_interrupt()
++       * with sonic_send_packet() so that the two functions can share state.
++       * Secondly, it makes sonic_interrupt() re-entrant, as that is required
++       * by macsonic which must use two IRQs with different priority levels.
++       */
++      spin_lock_irqsave(&lp->lock, flags);
++
++      status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
++      if (!status) {
++              spin_unlock_irqrestore(&lp->lock, flags);
+-      if (!(status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT))
+               return IRQ_NONE;
++      }
+       do {
+               if (status & SONIC_INT_PKTRX) {
+@@ -300,11 +315,12 @@ static irqreturn_t sonic_interrupt(int i
+                       int td_status;
+                       int freed_some = 0;
+-                      /* At this point, cur_tx is the index of a TD that is one of:
+-                       *   unallocated/freed                          (status set   & tx_skb[entry] clear)
+-                       *   allocated and sent                         (status set   & tx_skb[entry] set  )
+-                       *   allocated and not yet sent                 (status clear & tx_skb[entry] set  )
+-                       *   still being allocated by sonic_send_packet (status clear & tx_skb[entry] clear)
++                      /* The state of a Transmit Descriptor may be inferred
++                       * from { tx_skb[entry], td_status } as follows.
++                       * { clear, clear } => the TD has never been used
++                       * { set,   clear } => the TD was handed to SONIC
++                       * { set,   set   } => the TD was handed back
++                       * { clear, set   } => the TD is available for re-use
+                        */
+                       netif_dbg(lp, intr, dev, "%s: tx done\n", __func__);
+@@ -406,7 +422,12 @@ static irqreturn_t sonic_interrupt(int i
+               /* load CAM done */
+               if (status & SONIC_INT_LCD)
+                       SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */
+-      } while((status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT));
++
++              status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
++      } while (status);
++
++      spin_unlock_irqrestore(&lp->lock, flags);
++
+       return IRQ_HANDLED;
+ }
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -322,6 +322,7 @@ struct sonic_local {
+       int msg_enable;
+       struct device *device;         /* generic device */
+       struct net_device_stats stats;
++      spinlock_t lock;
+ };
+ #define TX_TIMEOUT (3 * HZ)
diff --git a/queue-5.4/net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch b/queue-5.4/net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch
new file mode 100644 (file)
index 0000000..2ada4a1
--- /dev/null
@@ -0,0 +1,71 @@
+From eaabfd19b2c787bbe88dc32424b9a43d67293422 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Avoid needless receive descriptor EOL flag updates
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit eaabfd19b2c787bbe88dc32424b9a43d67293422 upstream.
+
+The while loop in sonic_rx() traverses the rx descriptor ring. It stops
+when it reaches a descriptor that the SONIC has not used. Each iteration
+advances the EOL flag so the SONIC can keep using more descriptors.
+Therefore, the while loop has no definite termination condition.
+
+The algorithm described in the National Semiconductor literature is quite
+different. It consumes descriptors up to the one with its EOL flag set
+(which will also have its "in use" flag set). All freed descriptors are
+then returned to the ring at once, by adjusting the EOL flags (and link
+pointers).
+
+Adopt the algorithm from datasheet as it's simpler, terminates quickly
+and avoids a lot of pointless descriptor EOL flag changes.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   21 +++++++++++++++------
+ 1 file changed, 15 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -436,6 +436,7 @@ static void sonic_rx(struct net_device *
+       struct sonic_local *lp = netdev_priv(dev);
+       int status;
+       int entry = lp->cur_rx;
++      int prev_entry = lp->eol_rx;
+       while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) {
+               struct sk_buff *used_skb;
+@@ -516,13 +517,21 @@ static void sonic_rx(struct net_device *
+               /*
+                * give back the descriptor
+                */
+-              sonic_rda_put(dev, entry, SONIC_RD_LINK,
+-                      sonic_rda_get(dev, entry, SONIC_RD_LINK) | SONIC_EOL);
+               sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1);
+-              sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK,
+-                      sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK) & ~SONIC_EOL);
+-              lp->eol_rx = entry;
+-              lp->cur_rx = entry = (entry + 1) & SONIC_RDS_MASK;
++
++              prev_entry = entry;
++              entry = (entry + 1) & SONIC_RDS_MASK;
++      }
++
++      lp->cur_rx = entry;
++
++      if (prev_entry != lp->eol_rx) {
++              /* Advance the EOL flag to put descriptors back into service */
++              sonic_rda_put(dev, prev_entry, SONIC_RD_LINK, SONIC_EOL |
++                            sonic_rda_get(dev, prev_entry, SONIC_RD_LINK));
++              sonic_rda_put(dev, lp->eol_rx, SONIC_RD_LINK, ~SONIC_EOL &
++                            sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK));
++              lp->eol_rx = prev_entry;
+       }
+       /*
+        * If any worth-while packets have been received, netif_rx()
diff --git a/queue-5.4/net-sonic-clear-interrupt-flags-immediately.patch b/queue-5.4/net-sonic-clear-interrupt-flags-immediately.patch
new file mode 100644 (file)
index 0000000..a51eddf
--- /dev/null
@@ -0,0 +1,108 @@
+From 5fedabf5a70be26b19d7520f09f12a62274317c6 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Clear interrupt flags immediately
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 5fedabf5a70be26b19d7520f09f12a62274317c6 upstream.
+
+The chip can change a packet's descriptor status flags at any time.
+However, an active interrupt flag gets cleared rather late. This
+allows a race condition that could theoretically lose an interrupt.
+Fix this by clearing asserted interrupt flags immediately.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   28 ++++++----------------------
+ 1 file changed, 6 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -304,10 +304,11 @@ static irqreturn_t sonic_interrupt(int i
+       }
+       do {
++              SONIC_WRITE(SONIC_ISR, status); /* clear the interrupt(s) */
++
+               if (status & SONIC_INT_PKTRX) {
+                       netif_dbg(lp, intr, dev, "%s: packet rx\n", __func__);
+                       sonic_rx(dev);  /* got packet(s) */
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_PKTRX); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_TXDN) {
+@@ -362,7 +363,6 @@ static irqreturn_t sonic_interrupt(int i
+                       if (freed_some || lp->tx_skb[entry] == NULL)
+                               netif_wake_queue(dev);  /* The ring is no longer full */
+                       lp->cur_tx = entry;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_TXDN); /* clear the interrupt */
+               }
+               /*
+@@ -372,42 +372,31 @@ static irqreturn_t sonic_interrupt(int i
+                       netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n",
+                                 __func__);
+                       lp->stats.rx_fifo_errors++;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_RFO); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_RDE) {
+                       netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n",
+                                 __func__);
+                       lp->stats.rx_dropped++;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_RDE); /* clear the interrupt */
+               }
+               if (status & SONIC_INT_RBAE) {
+                       netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n",
+                                 __func__);
+                       lp->stats.rx_dropped++;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_RBAE); /* clear the interrupt */
+               }
+               /* counter overruns; all counters are 16bit wide */
+-              if (status & SONIC_INT_FAE) {
++              if (status & SONIC_INT_FAE)
+                       lp->stats.rx_frame_errors += 65536;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_FAE); /* clear the interrupt */
+-              }
+-              if (status & SONIC_INT_CRC) {
++              if (status & SONIC_INT_CRC)
+                       lp->stats.rx_crc_errors += 65536;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_CRC); /* clear the interrupt */
+-              }
+-              if (status & SONIC_INT_MP) {
++              if (status & SONIC_INT_MP)
+                       lp->stats.rx_missed_errors += 65536;
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_MP); /* clear the interrupt */
+-              }
+               /* transmit error */
+-              if (status & SONIC_INT_TXER) {
++              if (status & SONIC_INT_TXER)
+                       if (SONIC_READ(SONIC_TCR) & SONIC_TCR_FU)
+                               netif_dbg(lp, tx_err, dev, "%s: tx fifo underrun\n",
+                                         __func__);
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_TXER); /* clear the interrupt */
+-              }
+               /* bus retry */
+               if (status & SONIC_INT_BR) {
+@@ -416,13 +405,8 @@ static irqreturn_t sonic_interrupt(int i
+                       /* ... to help debug DMA problems causing endless interrupts. */
+                       /* Bounce the eth interface to turn on the interrupt again. */
+                       SONIC_WRITE(SONIC_IMR, 0);
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_BR); /* clear the interrupt */
+               }
+-              /* load CAM done */
+-              if (status & SONIC_INT_LCD)
+-                      SONIC_WRITE(SONIC_ISR, SONIC_INT_LCD); /* clear the interrupt */
+-
+               status = SONIC_READ(SONIC_ISR) & SONIC_IMR_DEFAULT;
+       } while (status);
diff --git a/queue-5.4/net-sonic-fix-cam-initialization.patch b/queue-5.4/net-sonic-fix-cam-initialization.patch
new file mode 100644 (file)
index 0000000..d3c199d
--- /dev/null
@@ -0,0 +1,89 @@
+From 772f66421d5aa0b9f256056f513bbc38ac132271 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Fix CAM initialization
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 772f66421d5aa0b9f256056f513bbc38ac132271 upstream.
+
+Section 4.3.1 of the datasheet says,
+
+    This bit [TXP] must not be set if a Load CAM operation is in
+    progress (LCAM is set). The SONIC will lock up if both bits are
+    set simultaneously.
+
+Testing has shown that the driver sometimes attempts to set LCAM
+while TXP is set. Avoid this by waiting for command completion
+before and after giving the LCAM command.
+
+After issuing the Load CAM command, poll for !SONIC_CR_LCAM rather than
+SONIC_INT_LCD, because the SONIC_CR_TXP bit can't be used until
+!SONIC_CR_LCAM.
+
+When in reset mode, take the opportunity to reset the CAM Enable
+register.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -634,6 +634,8 @@ static void sonic_multicast_list(struct
+                   (netdev_mc_count(dev) > 15)) {
+                       rcr |= SONIC_RCR_AMC;
+               } else {
++                      unsigned long flags;
++
+                       netif_dbg(lp, ifup, dev, "%s: mc_count %d\n", __func__,
+                                 netdev_mc_count(dev));
+                       sonic_set_cam_enable(dev, 1);  /* always enable our own address */
+@@ -647,9 +649,14 @@ static void sonic_multicast_list(struct
+                               i++;
+                       }
+                       SONIC_WRITE(SONIC_CDC, 16);
+-                      /* issue Load CAM command */
+                       SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff);
++
++                      /* LCAM and TXP commands can't be used simultaneously */
++                      spin_lock_irqsave(&lp->lock, flags);
++                      sonic_quiesce(dev, SONIC_CR_TXP);
+                       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
++                      sonic_quiesce(dev, SONIC_CR_LCAM);
++                      spin_unlock_irqrestore(&lp->lock, flags);
+               }
+       }
+@@ -675,6 +682,9 @@ static int sonic_init(struct net_device
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
++      /* While in reset mode, clear CAM Enable register */
++      SONIC_WRITE(SONIC_CE, 0);
++
+       /*
+        * clear software reset flag, disable receiver, clear and
+        * enable interrupts, then completely initialize the SONIC
+@@ -785,14 +795,7 @@ static int sonic_init(struct net_device
+        * load the CAM
+        */
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
+-
+-      i = 0;
+-      while (i++ < 100) {
+-              if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD)
+-                      break;
+-      }
+-      netif_dbg(lp, ifup, dev, "%s: CMD=%x, ISR=%x, i=%d\n", __func__,
+-                SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
++      sonic_quiesce(dev, SONIC_CR_LCAM);
+       /*
+        * enable receiver, disable loopback
diff --git a/queue-5.4/net-sonic-fix-command-register-usage.patch b/queue-5.4/net-sonic-fix-command-register-usage.patch
new file mode 100644 (file)
index 0000000..ea5413d
--- /dev/null
@@ -0,0 +1,88 @@
+From 27e0c31c5f27c1d1a1d9d135c123069f60dcf97b Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Fix command register usage
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 27e0c31c5f27c1d1a1d9d135c123069f60dcf97b upstream.
+
+There are several issues relating to command register usage during
+chip initialization.
+
+Firstly, the SONIC sometimes comes out of software reset with the
+Start Timer bit set. This gets logged as,
+
+    macsonic macsonic eth0: sonic_init: status=24, i=101
+
+Avoid this by giving the Stop Timer command earlier than later.
+
+Secondly, the loop that waits for the Read RRA command to complete has
+the break condition inverted. That's why the for loop iterates until
+its termination condition. Call the helper for this instead.
+
+Finally, give the Receiver Enable command after clearing interrupts,
+not before, to avoid the possibility of losing an interrupt.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   18 +++---------------
+ 1 file changed, 3 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -664,7 +664,6 @@ static void sonic_multicast_list(struct
+  */
+ static int sonic_init(struct net_device *dev)
+ {
+-      unsigned int cmd;
+       struct sonic_local *lp = netdev_priv(dev);
+       int i;
+@@ -681,7 +680,7 @@ static int sonic_init(struct net_device
+        * enable interrupts, then completely initialize the SONIC
+        */
+       SONIC_WRITE(SONIC_CMD, 0);
+-      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
++      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS | SONIC_CR_STP);
+       sonic_quiesce(dev, SONIC_CR_ALL);
+       /*
+@@ -711,14 +710,7 @@ static int sonic_init(struct net_device
+       netif_dbg(lp, ifup, dev, "%s: issuing RRRA command\n", __func__);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RRRA);
+-      i = 0;
+-      while (i++ < 100) {
+-              if (SONIC_READ(SONIC_CMD) & SONIC_CR_RRRA)
+-                      break;
+-      }
+-
+-      netif_dbg(lp, ifup, dev, "%s: status=%x, i=%d\n", __func__,
+-                SONIC_READ(SONIC_CMD), i);
++      sonic_quiesce(dev, SONIC_CR_RRRA);
+       /*
+        * Initialize the receive descriptors so that they
+@@ -806,15 +798,11 @@ static int sonic_init(struct net_device
+        * enable receiver, disable loopback
+        * and enable all interrupts
+        */
+-      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN | SONIC_CR_STP);
+       SONIC_WRITE(SONIC_RCR, SONIC_RCR_DEFAULT);
+       SONIC_WRITE(SONIC_TCR, SONIC_TCR_DEFAULT);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_IMR, SONIC_IMR_DEFAULT);
+-
+-      cmd = SONIC_READ(SONIC_CMD);
+-      if ((cmd & SONIC_CR_RXEN) == 0 || (cmd & SONIC_CR_STP) == 0)
+-              printk(KERN_ERR "sonic_init: failed, status=%x\n", cmd);
++      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXEN);
+       netif_dbg(lp, ifup, dev, "%s: new status=%x\n", __func__,
+                 SONIC_READ(SONIC_CMD));
diff --git a/queue-5.4/net-sonic-fix-interface-error-stats-collection.patch b/queue-5.4/net-sonic-fix-interface-error-stats-collection.patch
new file mode 100644 (file)
index 0000000..6ff2212
--- /dev/null
@@ -0,0 +1,107 @@
+From 427db97df1ee721c20bdc9a66db8a9e1da719855 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Fix interface error stats collection
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 427db97df1ee721c20bdc9a66db8a9e1da719855 upstream.
+
+The tx_aborted_errors statistic should count packets flagged with EXD,
+EXC, FU, or BCM bits because those bits denote an aborted transmission.
+That corresponds to the bitmask 0x0446, not 0x0642. Use macros for these
+constants to avoid mistakes. Better to leave out FIFO Underruns (FU) as
+there's a separate counter for that purpose.
+
+Don't lump all these errors in with the general tx_errors counter as
+that's used for tx timeout events.
+
+On the rx side, don't count RDE and RBAE interrupts as dropped packets.
+These interrupts don't indicate a lost packet, just a lack of resources.
+When a lack of resources results in a lost packet, this gets reported
+in the rx_missed_errors counter (along with RFO events).
+
+Don't double-count rx_frame_errors and rx_crc_errors.
+
+Don't use the general rx_errors counter for events that already have
+special counters.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   21 +++++++--------------
+ drivers/net/ethernet/natsemi/sonic.h |    1 +
+ 2 files changed, 8 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -330,18 +330,19 @@ static irqreturn_t sonic_interrupt(int i
+                               if ((td_status = sonic_tda_get(dev, entry, SONIC_TD_STATUS)) == 0)
+                                       break;
+-                              if (td_status & 0x0001) {
++                              if (td_status & SONIC_TCR_PTX) {
+                                       lp->stats.tx_packets++;
+                                       lp->stats.tx_bytes += sonic_tda_get(dev, entry, SONIC_TD_PKTSIZE);
+                               } else {
+-                                      lp->stats.tx_errors++;
+-                                      if (td_status & 0x0642)
++                                      if (td_status & (SONIC_TCR_EXD |
++                                          SONIC_TCR_EXC | SONIC_TCR_BCM))
+                                               lp->stats.tx_aborted_errors++;
+-                                      if (td_status & 0x0180)
++                                      if (td_status &
++                                          (SONIC_TCR_NCRS | SONIC_TCR_CRLS))
+                                               lp->stats.tx_carrier_errors++;
+-                                      if (td_status & 0x0020)
++                                      if (td_status & SONIC_TCR_OWC)
+                                               lp->stats.tx_window_errors++;
+-                                      if (td_status & 0x0004)
++                                      if (td_status & SONIC_TCR_FU)
+                                               lp->stats.tx_fifo_errors++;
+                               }
+@@ -371,17 +372,14 @@ static irqreturn_t sonic_interrupt(int i
+               if (status & SONIC_INT_RFO) {
+                       netif_dbg(lp, rx_err, dev, "%s: rx fifo overrun\n",
+                                 __func__);
+-                      lp->stats.rx_fifo_errors++;
+               }
+               if (status & SONIC_INT_RDE) {
+                       netif_dbg(lp, rx_err, dev, "%s: rx descriptors exhausted\n",
+                                 __func__);
+-                      lp->stats.rx_dropped++;
+               }
+               if (status & SONIC_INT_RBAE) {
+                       netif_dbg(lp, rx_err, dev, "%s: rx buffer area exceeded\n",
+                                 __func__);
+-                      lp->stats.rx_dropped++;
+               }
+               /* counter overruns; all counters are 16bit wide */
+@@ -473,11 +471,6 @@ static void sonic_rx(struct net_device *
+                       sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h);
+               } else {
+                       /* This should only happen, if we enable accepting broken packets. */
+-                      lp->stats.rx_errors++;
+-                      if (status & SONIC_RCR_FAER)
+-                              lp->stats.rx_frame_errors++;
+-                      if (status & SONIC_RCR_CRCR)
+-                              lp->stats.rx_crc_errors++;
+               }
+               if (status & SONIC_RCR_LPKT) {
+                       /*
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -175,6 +175,7 @@
+ #define SONIC_TCR_NCRS          0x0100
+ #define SONIC_TCR_CRLS          0x0080
+ #define SONIC_TCR_EXC           0x0040
++#define SONIC_TCR_OWC           0x0020
+ #define SONIC_TCR_PMB           0x0008
+ #define SONIC_TCR_FU            0x0004
+ #define SONIC_TCR_BCM           0x0002
diff --git a/queue-5.4/net-sonic-fix-receive-buffer-handling.patch b/queue-5.4/net-sonic-fix-receive-buffer-handling.patch
new file mode 100644 (file)
index 0000000..9513ad4
--- /dev/null
@@ -0,0 +1,110 @@
+From 9e311820f67e740f4fb8dcb82b4c4b5b05bdd1a5 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Fix receive buffer handling
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 9e311820f67e740f4fb8dcb82b4c4b5b05bdd1a5 upstream.
+
+The SONIC can sometimes advance its rx buffer pointer (RRP register)
+without advancing its rx descriptor pointer (CRDA register). As a result
+the index of the current rx descriptor may not equal that of the current
+rx buffer. The driver mistakenly assumes that they are always equal.
+This assumption leads to incorrect packet lengths and possible packet
+duplication. Avoid this by calling a new function to locate the buffer
+corresponding to a given descriptor.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   35 ++++++++++++++++++++++++++++++-----
+ drivers/net/ethernet/natsemi/sonic.h |    5 +++--
+ 2 files changed, 33 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -413,6 +413,21 @@ static irqreturn_t sonic_interrupt(int i
+       return IRQ_HANDLED;
+ }
++/* Return the array index corresponding to a given Receive Buffer pointer. */
++static int index_from_addr(struct sonic_local *lp, dma_addr_t addr,
++                         unsigned int last)
++{
++      unsigned int i = last;
++
++      do {
++              i = (i + 1) & SONIC_RRS_MASK;
++              if (addr == lp->rx_laddr[i])
++                      return i;
++      } while (i != last);
++
++      return -ENOENT;
++}
++
+ /*
+  * We have a good packet(s), pass it/them up the network stack.
+  */
+@@ -432,6 +447,16 @@ static void sonic_rx(struct net_device *
+               status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
+               if (status & SONIC_RCR_PRX) {
++                      u32 addr = (sonic_rda_get(dev, entry,
++                                                SONIC_RD_PKTPTR_H) << 16) |
++                                 sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L);
++                      int i = index_from_addr(lp, addr, entry);
++
++                      if (i < 0) {
++                              WARN_ONCE(1, "failed to find buffer!\n");
++                              break;
++                      }
++
+                       /* Malloc up new buffer. */
+                       new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
+                       if (new_skb == NULL) {
+@@ -453,7 +478,7 @@ static void sonic_rx(struct net_device *
+                       /* now we have a new skb to replace it, pass the used one up the stack */
+                       dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE);
+-                      used_skb = lp->rx_skb[entry];
++                      used_skb = lp->rx_skb[i];
+                       pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN);
+                       skb_trim(used_skb, pkt_len);
+                       used_skb->protocol = eth_type_trans(used_skb, dev);
+@@ -462,13 +487,13 @@ static void sonic_rx(struct net_device *
+                       lp->stats.rx_bytes += pkt_len;
+                       /* and insert the new skb */
+-                      lp->rx_laddr[entry] = new_laddr;
+-                      lp->rx_skb[entry] = new_skb;
++                      lp->rx_laddr[i] = new_laddr;
++                      lp->rx_skb[i] = new_skb;
+                       bufadr_l = (unsigned long)new_laddr & 0xffff;
+                       bufadr_h = (unsigned long)new_laddr >> 16;
+-                      sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, bufadr_l);
+-                      sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h);
++                      sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
++                      sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
+               } else {
+                       /* This should only happen, if we enable accepting broken packets. */
+               }
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -275,8 +275,9 @@
+ #define SONIC_NUM_RDS   SONIC_NUM_RRS /* number of receive descriptors */
+ #define SONIC_NUM_TDS   16            /* number of transmit descriptors */
+-#define SONIC_RDS_MASK  (SONIC_NUM_RDS-1)
+-#define SONIC_TDS_MASK  (SONIC_NUM_TDS-1)
++#define SONIC_RRS_MASK  (SONIC_NUM_RRS - 1)
++#define SONIC_RDS_MASK  (SONIC_NUM_RDS - 1)
++#define SONIC_TDS_MASK  (SONIC_NUM_TDS - 1)
+ #define SONIC_RBSIZE  1520          /* size of one resource buffer */
diff --git a/queue-5.4/net-sonic-fix-receive-buffer-replenishment.patch b/queue-5.4/net-sonic-fix-receive-buffer-replenishment.patch
new file mode 100644 (file)
index 0000000..9b34b85
--- /dev/null
@@ -0,0 +1,264 @@
+From 89ba879e95582d3bba55081e45b5409e883312ca Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Fix receive buffer replenishment
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 89ba879e95582d3bba55081e45b5409e883312ca upstream.
+
+As soon as the driver is finished with a receive buffer it allocs a new
+one and overwrites the corresponding RRA entry with a new buffer pointer.
+
+Problem is, the buffer pointer is split across two word-sized registers.
+It can't be updated in one atomic store. So this operation races with the
+chip while it stores received packets and advances its RRP register.
+This could result in memory corruption by a DMA write.
+
+Avoid this problem by adding buffers only at the location given by the
+RWP register, in accordance with the National Semiconductor datasheet.
+
+Re-factor this code into separate functions to calculate a RRA pointer
+and to update the RWP.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |  150 ++++++++++++++++++++---------------
+ drivers/net/ethernet/natsemi/sonic.h |   18 +++-
+ 2 files changed, 105 insertions(+), 63 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -428,6 +428,59 @@ static int index_from_addr(struct sonic_
+       return -ENOENT;
+ }
++/* Allocate and map a new skb to be used as a receive buffer. */
++static bool sonic_alloc_rb(struct net_device *dev, struct sonic_local *lp,
++                         struct sk_buff **new_skb, dma_addr_t *new_addr)
++{
++      *new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
++      if (!*new_skb)
++              return false;
++
++      if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
++              skb_reserve(*new_skb, 2);
++
++      *new_addr = dma_map_single(lp->device, skb_put(*new_skb, SONIC_RBSIZE),
++                                 SONIC_RBSIZE, DMA_FROM_DEVICE);
++      if (!*new_addr) {
++              dev_kfree_skb(*new_skb);
++              *new_skb = NULL;
++              return false;
++      }
++
++      return true;
++}
++
++/* Place a new receive resource in the Receive Resource Area and update RWP. */
++static void sonic_update_rra(struct net_device *dev, struct sonic_local *lp,
++                           dma_addr_t old_addr, dma_addr_t new_addr)
++{
++      unsigned int entry = sonic_rr_entry(dev, SONIC_READ(SONIC_RWP));
++      unsigned int end = sonic_rr_entry(dev, SONIC_READ(SONIC_RRP));
++      u32 buf;
++
++      /* The resources in the range [RRP, RWP) belong to the SONIC. This loop
++       * scans the other resources in the RRA, those in the range [RWP, RRP).
++       */
++      do {
++              buf = (sonic_rra_get(dev, entry, SONIC_RR_BUFADR_H) << 16) |
++                    sonic_rra_get(dev, entry, SONIC_RR_BUFADR_L);
++
++              if (buf == old_addr)
++                      break;
++
++              entry = (entry + 1) & SONIC_RRS_MASK;
++      } while (entry != end);
++
++      WARN_ONCE(buf != old_addr, "failed to find resource!\n");
++
++      sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, new_addr >> 16);
++      sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, new_addr & 0xffff);
++
++      entry = (entry + 1) & SONIC_RRS_MASK;
++
++      SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, entry));
++}
++
+ /*
+  * We have a good packet(s), pass it/them up the network stack.
+  */
+@@ -436,18 +489,15 @@ static void sonic_rx(struct net_device *
+       struct sonic_local *lp = netdev_priv(dev);
+       int entry = lp->cur_rx;
+       int prev_entry = lp->eol_rx;
++      bool rbe = false;
+       while (sonic_rda_get(dev, entry, SONIC_RD_IN_USE) == 0) {
+-              struct sk_buff *used_skb;
+-              struct sk_buff *new_skb;
+-              dma_addr_t new_laddr;
+-              u16 bufadr_l;
+-              u16 bufadr_h;
+-              int pkt_len;
+               u16 status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
+               /* If the RD has LPKT set, the chip has finished with the RB */
+               if ((status & SONIC_RCR_PRX) && (status & SONIC_RCR_LPKT)) {
++                      struct sk_buff *new_skb;
++                      dma_addr_t new_laddr;
+                       u32 addr = (sonic_rda_get(dev, entry,
+                                                 SONIC_RD_PKTPTR_H) << 16) |
+                                  sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L);
+@@ -458,55 +508,35 @@ static void sonic_rx(struct net_device *
+                               break;
+                       }
+-                      /* Malloc up new buffer. */
+-                      new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2);
+-                      if (new_skb == NULL) {
++                      if (sonic_alloc_rb(dev, lp, &new_skb, &new_laddr)) {
++                              struct sk_buff *used_skb = lp->rx_skb[i];
++                              int pkt_len;
++
++                              /* Pass the used buffer up the stack */
++                              dma_unmap_single(lp->device, addr, SONIC_RBSIZE,
++                                               DMA_FROM_DEVICE);
++
++                              pkt_len = sonic_rda_get(dev, entry,
++                                                      SONIC_RD_PKTLEN);
++                              skb_trim(used_skb, pkt_len);
++                              used_skb->protocol = eth_type_trans(used_skb,
++                                                                  dev);
++                              netif_rx(used_skb);
++                              lp->stats.rx_packets++;
++                              lp->stats.rx_bytes += pkt_len;
++
++                              lp->rx_skb[i] = new_skb;
++                              lp->rx_laddr[i] = new_laddr;
++                      } else {
++                              /* Failed to obtain a new buffer so re-use it */
++                              new_laddr = addr;
+                               lp->stats.rx_dropped++;
+-                              break;
+                       }
+-                      /* provide 16 byte IP header alignment unless DMA requires otherwise */
+-                      if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2)
+-                              skb_reserve(new_skb, 2);
+-
+-                      new_laddr = dma_map_single(lp->device, skb_put(new_skb, SONIC_RBSIZE),
+-                                             SONIC_RBSIZE, DMA_FROM_DEVICE);
+-                      if (!new_laddr) {
+-                              dev_kfree_skb(new_skb);
+-                              printk(KERN_ERR "%s: Failed to map rx buffer, dropping packet.\n", dev->name);
+-                              lp->stats.rx_dropped++;
+-                              break;
+-                      }
+-
+-                      /* now we have a new skb to replace it, pass the used one up the stack */
+-                      dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE);
+-                      used_skb = lp->rx_skb[i];
+-                      pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN);
+-                      skb_trim(used_skb, pkt_len);
+-                      used_skb->protocol = eth_type_trans(used_skb, dev);
+-                      netif_rx(used_skb);
+-                      lp->stats.rx_packets++;
+-                      lp->stats.rx_bytes += pkt_len;
+-
+-                      /* and insert the new skb */
+-                      lp->rx_laddr[i] = new_laddr;
+-                      lp->rx_skb[i] = new_skb;
+-
+-                      bufadr_l = (unsigned long)new_laddr & 0xffff;
+-                      bufadr_h = (unsigned long)new_laddr >> 16;
+-                      sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
+-                      sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
+-                      /*
+-                       * this was the last packet out of the current receive buffer
+-                       * give the buffer back to the SONIC
++                      /* If RBE is already asserted when RWP advances then
++                       * it's safe to clear RBE after processing this packet.
+                        */
+-                      lp->cur_rwp += SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode);
+-                      if (lp->cur_rwp >= lp->rra_end) lp->cur_rwp = lp->rra_laddr & 0xffff;
+-                      SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
+-                      if (SONIC_READ(SONIC_ISR) & SONIC_INT_RBE) {
+-                              netif_dbg(lp, rx_err, dev, "%s: rx buffer exhausted\n",
+-                                        __func__);
+-                              SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */
+-                      }
++                      rbe = rbe || SONIC_READ(SONIC_ISR) & SONIC_INT_RBE;
++                      sonic_update_rra(dev, lp, addr, new_laddr);
+               }
+               /*
+                * give back the descriptor
+@@ -528,6 +558,9 @@ static void sonic_rx(struct net_device *
+                             sonic_rda_get(dev, lp->eol_rx, SONIC_RD_LINK));
+               lp->eol_rx = prev_entry;
+       }
++
++      if (rbe)
++              SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE);
+       /*
+        * If any worth-while packets have been received, netif_rx()
+        * has done a mark_bh(NET_BH) for us and will work on them
+@@ -642,15 +675,10 @@ static int sonic_init(struct net_device
+       }
+       /* initialize all RRA registers */
+-      lp->rra_end = (lp->rra_laddr + SONIC_NUM_RRS * SIZEOF_SONIC_RR *
+-                                      SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
+-      lp->cur_rwp = (lp->rra_laddr + (SONIC_NUM_RRS - 1) * SIZEOF_SONIC_RR *
+-                                      SONIC_BUS_SCALE(lp->dma_bitmode)) & 0xffff;
+-
+-      SONIC_WRITE(SONIC_RSA, lp->rra_laddr & 0xffff);
+-      SONIC_WRITE(SONIC_REA, lp->rra_end);
+-      SONIC_WRITE(SONIC_RRP, lp->rra_laddr & 0xffff);
+-      SONIC_WRITE(SONIC_RWP, lp->cur_rwp);
++      SONIC_WRITE(SONIC_RSA, sonic_rr_addr(dev, 0));
++      SONIC_WRITE(SONIC_REA, sonic_rr_addr(dev, SONIC_NUM_RRS));
++      SONIC_WRITE(SONIC_RRP, sonic_rr_addr(dev, 0));
++      SONIC_WRITE(SONIC_RWP, sonic_rr_addr(dev, SONIC_NUM_RRS - 1));
+       SONIC_WRITE(SONIC_URRA, lp->rra_laddr >> 16);
+       SONIC_WRITE(SONIC_EOBC, (SONIC_RBSIZE >> 1) - (lp->dma_bitmode ? 2 : 1));
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -314,8 +314,6 @@ struct sonic_local {
+       u32 rda_laddr;              /* logical DMA address of RDA */
+       dma_addr_t rx_laddr[SONIC_NUM_RRS]; /* logical DMA addresses of rx skbuffs */
+       dma_addr_t tx_laddr[SONIC_NUM_TDS]; /* logical DMA addresses of tx skbuffs */
+-      unsigned int rra_end;
+-      unsigned int cur_rwp;
+       unsigned int cur_rx;
+       unsigned int cur_tx;           /* first unacked transmit packet */
+       unsigned int eol_rx;
+@@ -450,6 +448,22 @@ static inline __u16 sonic_rra_get(struct
+                            (entry * SIZEOF_SONIC_RR) + offset);
+ }
++static inline u16 sonic_rr_addr(struct net_device *dev, int entry)
++{
++      struct sonic_local *lp = netdev_priv(dev);
++
++      return lp->rra_laddr +
++             entry * SIZEOF_SONIC_RR * SONIC_BUS_SCALE(lp->dma_bitmode);
++}
++
++static inline u16 sonic_rr_entry(struct net_device *dev, u16 addr)
++{
++      struct sonic_local *lp = netdev_priv(dev);
++
++      return (addr - (u16)lp->rra_laddr) / (SIZEOF_SONIC_RR *
++                                            SONIC_BUS_SCALE(lp->dma_bitmode));
++}
++
+ static const char version[] =
+     "sonic.c:v0.92 20.9.98 tsbogend@alpha.franken.de\n";
diff --git a/queue-5.4/net-sonic-improve-receive-descriptor-status-flag-check.patch b/queue-5.4/net-sonic-improve-receive-descriptor-status-flag-check.patch
new file mode 100644 (file)
index 0000000..4b52aea
--- /dev/null
@@ -0,0 +1,79 @@
+From 94b166349503957079ef5e7d6f667f157aea014a Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Improve receive descriptor status flag check
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 94b166349503957079ef5e7d6f667f157aea014a upstream.
+
+After sonic_tx_timeout() calls sonic_init(), it can happen that
+sonic_rx() will subsequently encounter a receive descriptor with no
+flags set. Remove the comment that says that this can't happen.
+
+When giving a receive descriptor to the SONIC, clear the descriptor
+status field. That way, any rx descriptor with flags set can only be
+a newly received packet.
+
+Don't process a descriptor without the LPKT bit set. The buffer is
+still in use by the SONIC.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   15 +++++----------
+ 1 file changed, 5 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -434,7 +434,6 @@ static int index_from_addr(struct sonic_
+ static void sonic_rx(struct net_device *dev)
+ {
+       struct sonic_local *lp = netdev_priv(dev);
+-      int status;
+       int entry = lp->cur_rx;
+       int prev_entry = lp->eol_rx;
+@@ -445,9 +444,10 @@ static void sonic_rx(struct net_device *
+               u16 bufadr_l;
+               u16 bufadr_h;
+               int pkt_len;
++              u16 status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
+-              status = sonic_rda_get(dev, entry, SONIC_RD_STATUS);
+-              if (status & SONIC_RCR_PRX) {
++              /* If the RD has LPKT set, the chip has finished with the RB */
++              if ((status & SONIC_RCR_PRX) && (status & SONIC_RCR_LPKT)) {
+                       u32 addr = (sonic_rda_get(dev, entry,
+                                                 SONIC_RD_PKTPTR_H) << 16) |
+                                  sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L);
+@@ -495,10 +495,6 @@ static void sonic_rx(struct net_device *
+                       bufadr_h = (unsigned long)new_laddr >> 16;
+                       sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l);
+                       sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h);
+-              } else {
+-                      /* This should only happen, if we enable accepting broken packets. */
+-              }
+-              if (status & SONIC_RCR_LPKT) {
+                       /*
+                        * this was the last packet out of the current receive buffer
+                        * give the buffer back to the SONIC
+@@ -511,12 +507,11 @@ static void sonic_rx(struct net_device *
+                                         __func__);
+                               SONIC_WRITE(SONIC_ISR, SONIC_INT_RBE); /* clear the flag */
+                       }
+-              } else
+-                      printk(KERN_ERR "%s: rx desc without RCR_LPKT. Shouldn't happen !?\n",
+-                           dev->name);
++              }
+               /*
+                * give back the descriptor
+                */
++              sonic_rda_put(dev, entry, SONIC_RD_STATUS, 0);
+               sonic_rda_put(dev, entry, SONIC_RD_IN_USE, 1);
+               prev_entry = entry;
diff --git a/queue-5.4/net-sonic-prevent-tx-watchdog-timeout.patch b/queue-5.4/net-sonic-prevent-tx-watchdog-timeout.patch
new file mode 100644 (file)
index 0000000..73c78c6
--- /dev/null
@@ -0,0 +1,62 @@
+From 686f85d71d095f1d26b807e23b0f0bfd22042c45 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Prevent tx watchdog timeout
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 686f85d71d095f1d26b807e23b0f0bfd22042c45 upstream.
+
+Section 5.5.3.2 of the datasheet says,
+
+    If FIFO Underrun, Byte Count Mismatch, Excessive Collision, or
+    Excessive Deferral (if enabled) errors occur, transmission ceases.
+
+In this situation, the chip asserts a TXER interrupt rather than TXDN.
+But the handler for the TXDN is the only way that the transmit queue
+gets restarted. Hence, an aborted transmission can result in a watchdog
+timeout.
+
+This problem can be reproduced on congested link, as that can result in
+excessive transmitter collisions. Another way to reproduce this is with
+a FIFO Underrun, which may be caused by DMA latency.
+
+In event of a TXER interrupt, prevent a watchdog timeout by restarting
+transmission.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -415,10 +415,19 @@ static irqreturn_t sonic_interrupt(int i
+                       lp->stats.rx_missed_errors += 65536;
+               /* transmit error */
+-              if (status & SONIC_INT_TXER)
+-                      if (SONIC_READ(SONIC_TCR) & SONIC_TCR_FU)
+-                              netif_dbg(lp, tx_err, dev, "%s: tx fifo underrun\n",
+-                                        __func__);
++              if (status & SONIC_INT_TXER) {
++                      u16 tcr = SONIC_READ(SONIC_TCR);
++
++                      netif_dbg(lp, tx_err, dev, "%s: TXER intr, TCR %04x\n",
++                                __func__, tcr);
++
++                      if (tcr & (SONIC_TCR_EXD | SONIC_TCR_EXC |
++                                 SONIC_TCR_FU | SONIC_TCR_BCM)) {
++                              /* Aborted transmission. Try again. */
++                              netif_stop_queue(dev);
++                              SONIC_WRITE(SONIC_CMD, SONIC_CR_TXP);
++                      }
++              }
+               /* bus retry */
+               if (status & SONIC_INT_BR) {
diff --git a/queue-5.4/net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch b/queue-5.4/net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch
new file mode 100644 (file)
index 0000000..1b4c928
--- /dev/null
@@ -0,0 +1,91 @@
+From 3f4b7e6a2be982fd8820a2b54d46dd9c351db899 Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Quiesce SONIC before re-initializing descriptor memory
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit 3f4b7e6a2be982fd8820a2b54d46dd9c351db899 upstream.
+
+Make sure the SONIC's DMA engine is idle before altering the transmit
+and receive descriptors. Add a helper for this as it will be needed
+again.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.c |   25 +++++++++++++++++++++++++
+ drivers/net/ethernet/natsemi/sonic.h |    3 +++
+ 2 files changed, 28 insertions(+)
+
+--- a/drivers/net/ethernet/natsemi/sonic.c
++++ b/drivers/net/ethernet/natsemi/sonic.c
+@@ -116,6 +116,24 @@ static int sonic_open(struct net_device
+       return 0;
+ }
++/* Wait for the SONIC to become idle. */
++static void sonic_quiesce(struct net_device *dev, u16 mask)
++{
++      struct sonic_local * __maybe_unused lp = netdev_priv(dev);
++      int i;
++      u16 bits;
++
++      for (i = 0; i < 1000; ++i) {
++              bits = SONIC_READ(SONIC_CMD) & mask;
++              if (!bits)
++                      return;
++              if (irqs_disabled() || in_interrupt())
++                      udelay(20);
++              else
++                      usleep_range(100, 200);
++      }
++      WARN_ONCE(1, "command deadline expired! 0x%04x\n", bits);
++}
+ /*
+  * Close the SONIC device
+@@ -132,6 +150,9 @@ static int sonic_close(struct net_device
+       /*
+        * stop the SONIC, disable interrupts
+        */
++      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
++      sonic_quiesce(dev, SONIC_CR_ALL);
++
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+@@ -171,6 +192,9 @@ static void sonic_tx_timeout(struct net_
+        * put the Sonic into software-reset mode and
+        * disable all interrupts before releasing DMA buffers
+        */
++      SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
++      sonic_quiesce(dev, SONIC_CR_ALL);
++
+       SONIC_WRITE(SONIC_IMR, 0);
+       SONIC_WRITE(SONIC_ISR, 0x7fff);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RST);
+@@ -658,6 +682,7 @@ static int sonic_init(struct net_device
+        */
+       SONIC_WRITE(SONIC_CMD, 0);
+       SONIC_WRITE(SONIC_CMD, SONIC_CR_RXDIS);
++      sonic_quiesce(dev, SONIC_CR_ALL);
+       /*
+        * initialize the receive resource area
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -110,6 +110,9 @@
+ #define SONIC_CR_TXP            0x0002
+ #define SONIC_CR_HTX            0x0001
++#define SONIC_CR_ALL (SONIC_CR_LCAM | SONIC_CR_RRRA | \
++                    SONIC_CR_RXEN | SONIC_CR_TXP)
++
+ /*
+  * SONIC data configuration bits
+  */
diff --git a/queue-5.4/net-sonic-use-mmio-accessors.patch b/queue-5.4/net-sonic-use-mmio-accessors.patch
new file mode 100644 (file)
index 0000000..e158bf5
--- /dev/null
@@ -0,0 +1,65 @@
+From e3885f576196ddfc670b3d53e745de96ffcb49ab Mon Sep 17 00:00:00 2001
+From: Finn Thain <fthain@telegraphics.com.au>
+Date: Thu, 23 Jan 2020 09:07:26 +1100
+Subject: net/sonic: Use MMIO accessors
+
+From: Finn Thain <fthain@telegraphics.com.au>
+
+commit e3885f576196ddfc670b3d53e745de96ffcb49ab upstream.
+
+The driver accesses descriptor memory which is simultaneously accessed by
+the chip, so the compiler must not be allowed to re-order CPU accesses.
+sonic_buf_get() used 'volatile' to prevent that. sonic_buf_put() should
+have done so too but was overlooked.
+
+Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update")
+Tested-by: Stan Johnson <userm57@yahoo.com>
+Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/ethernet/natsemi/sonic.h |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/ethernet/natsemi/sonic.h
++++ b/drivers/net/ethernet/natsemi/sonic.h
+@@ -345,30 +345,30 @@ static void sonic_msg_init(struct net_de
+    as far as we can tell. */
+ /* OpenBSD calls this "SWO".  I'd like to think that sonic_buf_put()
+    is a much better name. */
+-static inline void sonic_buf_put(void* base, int bitmode,
++static inline void sonic_buf_put(u16 *base, int bitmode,
+                                int offset, __u16 val)
+ {
+       if (bitmode)
+ #ifdef __BIG_ENDIAN
+-              ((__u16 *) base + (offset*2))[1] = val;
++              __raw_writew(val, base + (offset * 2) + 1);
+ #else
+-              ((__u16 *) base + (offset*2))[0] = val;
++              __raw_writew(val, base + (offset * 2) + 0);
+ #endif
+       else
+-              ((__u16 *) base)[offset] = val;
++              __raw_writew(val, base + (offset * 1) + 0);
+ }
+-static inline __u16 sonic_buf_get(void* base, int bitmode,
++static inline __u16 sonic_buf_get(u16 *base, int bitmode,
+                                 int offset)
+ {
+       if (bitmode)
+ #ifdef __BIG_ENDIAN
+-              return ((volatile __u16 *) base + (offset*2))[1];
++              return __raw_readw(base + (offset * 2) + 1);
+ #else
+-              return ((volatile __u16 *) base + (offset*2))[0];
++              return __raw_readw(base + (offset * 2) + 0);
+ #endif
+       else
+-              return ((volatile __u16 *) base)[offset];
++              return __raw_readw(base + (offset * 1) + 0);
+ }
+ /* Inlines that you should actually use for reading/writing DMA buffers */
index 2640ce2dc49d21f9dcbc0e7ec5f2540f624b37ce..36ac0844fb53853cc1ef79ce79fd11a4a2ffa2d5 100644 (file)
@@ -84,3 +84,17 @@ xfrm-support-output_mark-for-offload-esp-packets.patch
 net-sk_msg-don-t-check-if-sock-is-locked-when-tearing-down-psock.patch
 do_last-fetch-directory-i_mode-and-i_uid-before-it-s-too-late.patch
 readdir-be-more-conservative-with-directory-entry-names.patch
+net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch
+net-sonic-clear-interrupt-flags-immediately.patch
+net-sonic-use-mmio-accessors.patch
+net-sonic-fix-interface-error-stats-collection.patch
+net-sonic-fix-receive-buffer-handling.patch
+net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch
+net-sonic-improve-receive-descriptor-status-flag-check.patch
+net-sonic-fix-receive-buffer-replenishment.patch
+net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch
+net-sonic-fix-command-register-usage.patch
+net-sonic-fix-cam-initialization.patch
+net-sonic-prevent-tx-watchdog-timeout.patch
+libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch
+media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch