From: Greg Kroah-Hartman Date: Tue, 28 Jan 2020 08:53:06 +0000 (+0100) Subject: 5.4-stable patches X-Git-Tag: v4.4.212~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=521cee2c4369ce2b6da342da5db85818e7411f17;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches 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 --- 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 index 00000000000..ab07e40576f --- /dev/null +++ b/queue-5.4/libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch @@ -0,0 +1,68 @@ +From e5e884b42639c74b5b57dc277909915c0aefc8bb Mon Sep 17 00:00:00 2001 +From: Wen Huang +Date: Thu, 28 Nov 2019 18:51:04 +0800 +Subject: libertas: Fix two buffer overflows at parsing bss descriptor + +From: Wen Huang + +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 +Signed-off-by: Wen Huang +Signed-off-by: Kalle Valo +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..8f85c58e2ab --- /dev/null +++ b/queue-5.4/media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch @@ -0,0 +1,108 @@ +From ee8951e56c0f960b9621636603a822811cef3158 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil +Date: Sun, 10 Nov 2019 07:27:04 +0100 +Subject: media: v4l2-ioctl.c: zero reserved fields for S/TRY_FMT + +From: Hans Verkuil + +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 +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..6eaf2a16d06 --- /dev/null +++ b/queue-5.4/net-sonic-add-mutual-exclusion-for-accessing-shared-state.patch @@ -0,0 +1,152 @@ +From 865ad2f2201dc18685ba2686f13217f8b3a9c52c Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Add mutual exclusion for accessing shared state + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..2ada4a11ab1 --- /dev/null +++ b/queue-5.4/net-sonic-avoid-needless-receive-descriptor-eol-flag-updates.patch @@ -0,0 +1,71 @@ +From eaabfd19b2c787bbe88dc32424b9a43d67293422 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Avoid needless receive descriptor EOL flag updates + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..a51eddf2823 --- /dev/null +++ b/queue-5.4/net-sonic-clear-interrupt-flags-immediately.patch @@ -0,0 +1,108 @@ +From 5fedabf5a70be26b19d7520f09f12a62274317c6 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Clear interrupt flags immediately + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..d3c199dffec --- /dev/null +++ b/queue-5.4/net-sonic-fix-cam-initialization.patch @@ -0,0 +1,89 @@ +From 772f66421d5aa0b9f256056f513bbc38ac132271 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Fix CAM initialization + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..ea5413d18c2 --- /dev/null +++ b/queue-5.4/net-sonic-fix-command-register-usage.patch @@ -0,0 +1,88 @@ +From 27e0c31c5f27c1d1a1d9d135c123069f60dcf97b Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Fix command register usage + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..6ff2212b779 --- /dev/null +++ b/queue-5.4/net-sonic-fix-interface-error-stats-collection.patch @@ -0,0 +1,107 @@ +From 427db97df1ee721c20bdc9a66db8a9e1da719855 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Fix interface error stats collection + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..9513ad4d9a7 --- /dev/null +++ b/queue-5.4/net-sonic-fix-receive-buffer-handling.patch @@ -0,0 +1,110 @@ +From 9e311820f67e740f4fb8dcb82b4c4b5b05bdd1a5 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Fix receive buffer handling + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..9b34b8529ea --- /dev/null +++ b/queue-5.4/net-sonic-fix-receive-buffer-replenishment.patch @@ -0,0 +1,264 @@ +From 89ba879e95582d3bba55081e45b5409e883312ca Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Fix receive buffer replenishment + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..4b52aeab151 --- /dev/null +++ b/queue-5.4/net-sonic-improve-receive-descriptor-status-flag-check.patch @@ -0,0 +1,79 @@ +From 94b166349503957079ef5e7d6f667f157aea014a Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Improve receive descriptor status flag check + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..73c78c641ba --- /dev/null +++ b/queue-5.4/net-sonic-prevent-tx-watchdog-timeout.patch @@ -0,0 +1,62 @@ +From 686f85d71d095f1d26b807e23b0f0bfd22042c45 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Prevent tx watchdog timeout + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..1b4c9289d79 --- /dev/null +++ b/queue-5.4/net-sonic-quiesce-sonic-before-re-initializing-descriptor-memory.patch @@ -0,0 +1,91 @@ +From 3f4b7e6a2be982fd8820a2b54d46dd9c351db899 Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Quiesce SONIC before re-initializing descriptor memory + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..e158bf5a6af --- /dev/null +++ b/queue-5.4/net-sonic-use-mmio-accessors.patch @@ -0,0 +1,65 @@ +From e3885f576196ddfc670b3d53e745de96ffcb49ab Mon Sep 17 00:00:00 2001 +From: Finn Thain +Date: Thu, 23 Jan 2020 09:07:26 +1100 +Subject: net/sonic: Use MMIO accessors + +From: Finn Thain + +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 +Signed-off-by: Finn Thain +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + 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 */ diff --git a/queue-5.4/series b/queue-5.4/series index 2640ce2dc49..36ac0844fb5 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -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