mirror of
https://github.com/torvalds/linux.git
synced 2024-12-21 10:31:54 +00:00
Merge branch 'net-stmmac-fix-handling-of-oversized-frames'
Aaro Koskinen says: ==================== net: stmmac: fix handling of oversized frames I accidentally had MTU size mismatch (9000 vs. 1500) in my network, and I noticed I could kill a system using stmmac & 1500 MTU simply by pinging it with "ping -s 2000 ...". While testing a fix I encountered also some other issues that need fixing. I have tested these only with enhanced descriptors, so the normal descriptor changes need a careful review. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
d3de85a51a
@ -29,11 +29,13 @@
|
||||
/* Specific functions used for Ring mode */
|
||||
|
||||
/* Enhanced descriptors */
|
||||
static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end)
|
||||
static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end,
|
||||
int bfsize)
|
||||
{
|
||||
p->des1 |= cpu_to_le32((BUF_SIZE_8KiB
|
||||
<< ERDES1_BUFFER2_SIZE_SHIFT)
|
||||
& ERDES1_BUFFER2_SIZE_MASK);
|
||||
if (bfsize == BUF_SIZE_16KiB)
|
||||
p->des1 |= cpu_to_le32((BUF_SIZE_8KiB
|
||||
<< ERDES1_BUFFER2_SIZE_SHIFT)
|
||||
& ERDES1_BUFFER2_SIZE_MASK);
|
||||
|
||||
if (end)
|
||||
p->des1 |= cpu_to_le32(ERDES1_END_RING);
|
||||
@ -59,11 +61,15 @@ static inline void enh_set_tx_desc_len_on_ring(struct dma_desc *p, int len)
|
||||
}
|
||||
|
||||
/* Normal descriptors */
|
||||
static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end)
|
||||
static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end, int bfsize)
|
||||
{
|
||||
p->des1 |= cpu_to_le32(((BUF_SIZE_2KiB - 1)
|
||||
<< RDES1_BUFFER2_SIZE_SHIFT)
|
||||
& RDES1_BUFFER2_SIZE_MASK);
|
||||
if (bfsize >= BUF_SIZE_2KiB) {
|
||||
int bfsize2;
|
||||
|
||||
bfsize2 = min(bfsize - BUF_SIZE_2KiB + 1, BUF_SIZE_2KiB - 1);
|
||||
p->des1 |= cpu_to_le32((bfsize2 << RDES1_BUFFER2_SIZE_SHIFT)
|
||||
& RDES1_BUFFER2_SIZE_MASK);
|
||||
}
|
||||
|
||||
if (end)
|
||||
p->des1 |= cpu_to_le32(RDES1_END_RING);
|
||||
|
@ -296,7 +296,7 @@ exit:
|
||||
}
|
||||
|
||||
static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
|
||||
int mode, int end)
|
||||
int mode, int end, int bfsize)
|
||||
{
|
||||
dwmac4_set_rx_owner(p, disable_rx_ic);
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ static int dwxgmac2_get_rx_timestamp_status(void *desc, void *next_desc,
|
||||
}
|
||||
|
||||
static void dwxgmac2_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
|
||||
int mode, int end)
|
||||
int mode, int end, int bfsize)
|
||||
{
|
||||
dwxgmac2_set_rx_owner(p, disable_rx_ic);
|
||||
}
|
||||
|
@ -201,6 +201,11 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
|
||||
if (unlikely(rdes0 & RDES0_OWN))
|
||||
return dma_own;
|
||||
|
||||
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
|
||||
stats->rx_length_errors++;
|
||||
return discard_frame;
|
||||
}
|
||||
|
||||
if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
|
||||
if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) {
|
||||
x->rx_desc++;
|
||||
@ -231,9 +236,10 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
|
||||
* It doesn't match with the information reported into the databook.
|
||||
* At any rate, we need to understand if the CSUM hw computation is ok
|
||||
* and report this info to the upper layers. */
|
||||
ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR),
|
||||
!!(rdes0 & RDES0_FRAME_TYPE),
|
||||
!!(rdes0 & ERDES0_RX_MAC_ADDR));
|
||||
if (likely(ret == good_frame))
|
||||
ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR),
|
||||
!!(rdes0 & RDES0_FRAME_TYPE),
|
||||
!!(rdes0 & ERDES0_RX_MAC_ADDR));
|
||||
|
||||
if (unlikely(rdes0 & RDES0_DRIBBLING))
|
||||
x->dribbling_bit++;
|
||||
@ -259,15 +265,19 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x,
|
||||
}
|
||||
|
||||
static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic,
|
||||
int mode, int end)
|
||||
int mode, int end, int bfsize)
|
||||
{
|
||||
int bfsize1;
|
||||
|
||||
p->des0 |= cpu_to_le32(RDES0_OWN);
|
||||
p->des1 |= cpu_to_le32(BUF_SIZE_8KiB & ERDES1_BUFFER1_SIZE_MASK);
|
||||
|
||||
bfsize1 = min(bfsize, BUF_SIZE_8KiB);
|
||||
p->des1 |= cpu_to_le32(bfsize1 & ERDES1_BUFFER1_SIZE_MASK);
|
||||
|
||||
if (mode == STMMAC_CHAIN_MODE)
|
||||
ehn_desc_rx_set_on_chain(p);
|
||||
else
|
||||
ehn_desc_rx_set_on_ring(p, end);
|
||||
ehn_desc_rx_set_on_ring(p, end, bfsize);
|
||||
|
||||
if (disable_rx_ic)
|
||||
p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC);
|
||||
|
@ -33,7 +33,7 @@ struct dma_extended_desc;
|
||||
struct stmmac_desc_ops {
|
||||
/* DMA RX descriptor ring initialization */
|
||||
void (*init_rx_desc)(struct dma_desc *p, int disable_rx_ic, int mode,
|
||||
int end);
|
||||
int end, int bfsize);
|
||||
/* DMA TX descriptor ring initialization */
|
||||
void (*init_tx_desc)(struct dma_desc *p, int mode, int end);
|
||||
/* Invoked by the xmit function to prepare the tx descriptor */
|
||||
|
@ -91,8 +91,6 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
|
||||
return dma_own;
|
||||
|
||||
if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
|
||||
pr_warn("%s: Oversized frame spanned multiple buffers\n",
|
||||
__func__);
|
||||
stats->rx_length_errors++;
|
||||
return discard_frame;
|
||||
}
|
||||
@ -135,15 +133,19 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x,
|
||||
}
|
||||
|
||||
static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
|
||||
int end)
|
||||
int end, int bfsize)
|
||||
{
|
||||
int bfsize1;
|
||||
|
||||
p->des0 |= cpu_to_le32(RDES0_OWN);
|
||||
p->des1 |= cpu_to_le32((BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK);
|
||||
|
||||
bfsize1 = min(bfsize, BUF_SIZE_2KiB - 1);
|
||||
p->des1 |= cpu_to_le32(bfsize & RDES1_BUFFER1_SIZE_MASK);
|
||||
|
||||
if (mode == STMMAC_CHAIN_MODE)
|
||||
ndesc_rx_set_on_chain(p, end);
|
||||
else
|
||||
ndesc_rx_set_on_ring(p, end);
|
||||
ndesc_rx_set_on_ring(p, end, bfsize);
|
||||
|
||||
if (disable_rx_ic)
|
||||
p->des1 |= cpu_to_le32(RDES1_DISABLE_IC);
|
||||
|
@ -1136,11 +1136,13 @@ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue)
|
||||
if (priv->extend_desc)
|
||||
stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic,
|
||||
priv->use_riwt, priv->mode,
|
||||
(i == DMA_RX_SIZE - 1));
|
||||
(i == DMA_RX_SIZE - 1),
|
||||
priv->dma_buf_sz);
|
||||
else
|
||||
stmmac_init_rx_desc(priv, &rx_q->dma_rx[i],
|
||||
priv->use_riwt, priv->mode,
|
||||
(i == DMA_RX_SIZE - 1));
|
||||
(i == DMA_RX_SIZE - 1),
|
||||
priv->dma_buf_sz);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3352,9 +3354,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
{
|
||||
struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
|
||||
struct stmmac_channel *ch = &priv->channel[queue];
|
||||
unsigned int entry = rx_q->cur_rx;
|
||||
unsigned int next_entry = rx_q->cur_rx;
|
||||
int coe = priv->hw->rx_csum;
|
||||
unsigned int next_entry;
|
||||
unsigned int count = 0;
|
||||
bool xmac;
|
||||
|
||||
@ -3372,10 +3373,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
stmmac_display_ring(priv, rx_head, DMA_RX_SIZE, true);
|
||||
}
|
||||
while (count < limit) {
|
||||
int status;
|
||||
int entry, status;
|
||||
struct dma_desc *p;
|
||||
struct dma_desc *np;
|
||||
|
||||
entry = next_entry;
|
||||
|
||||
if (priv->extend_desc)
|
||||
p = (struct dma_desc *)(rx_q->dma_erx + entry);
|
||||
else
|
||||
@ -3431,11 +3434,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
* ignored
|
||||
*/
|
||||
if (frame_len > priv->dma_buf_sz) {
|
||||
netdev_err(priv->dev,
|
||||
"len %d larger than size (%d)\n",
|
||||
frame_len, priv->dma_buf_sz);
|
||||
if (net_ratelimit())
|
||||
netdev_err(priv->dev,
|
||||
"len %d larger than size (%d)\n",
|
||||
frame_len, priv->dma_buf_sz);
|
||||
priv->dev->stats.rx_length_errors++;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3
|
||||
@ -3470,7 +3474,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
dev_warn(priv->device,
|
||||
"packet dropped\n");
|
||||
priv->dev->stats.rx_dropped++;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
dma_sync_single_for_cpu(priv->device,
|
||||
@ -3490,11 +3494,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
} else {
|
||||
skb = rx_q->rx_skbuff[entry];
|
||||
if (unlikely(!skb)) {
|
||||
netdev_err(priv->dev,
|
||||
"%s: Inconsistent Rx chain\n",
|
||||
priv->dev->name);
|
||||
if (net_ratelimit())
|
||||
netdev_err(priv->dev,
|
||||
"%s: Inconsistent Rx chain\n",
|
||||
priv->dev->name);
|
||||
priv->dev->stats.rx_dropped++;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
prefetch(skb->data - NET_IP_ALIGN);
|
||||
rx_q->rx_skbuff[entry] = NULL;
|
||||
@ -3529,7 +3534,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
|
||||
priv->dev->stats.rx_packets++;
|
||||
priv->dev->stats.rx_bytes += frame_len;
|
||||
}
|
||||
entry = next_entry;
|
||||
}
|
||||
|
||||
stmmac_rx_refill(priv, queue);
|
||||
|
Loading…
Reference in New Issue
Block a user