diff options
author | David S. Miller <davem@davemloft.net> | 2019-03-31 17:00:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-03-31 17:00:59 -0400 |
commit | d3de85a51a4b4eaeb62541dfdbf5159f3b210260 (patch) | |
tree | 21df898b0676583247063786206b24d7d00f7d11 | |
parent | 288ac524cf70a8e7ed851a61ed2a9744039dae8d (diff) | |
parent | 057a0c5642a2ff2db7c421cdcde34294a23bf37b (diff) |
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>
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/descs_com.h | 22 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 22 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 34 |
7 files changed, 59 insertions, 37 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h index 40d6356a7e73..3dfb07a78952 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h | |||
@@ -29,11 +29,13 @@ | |||
29 | /* Specific functions used for Ring mode */ | 29 | /* Specific functions used for Ring mode */ |
30 | 30 | ||
31 | /* Enhanced descriptors */ | 31 | /* Enhanced descriptors */ |
32 | static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end) | 32 | static inline void ehn_desc_rx_set_on_ring(struct dma_desc *p, int end, |
33 | int bfsize) | ||
33 | { | 34 | { |
34 | p->des1 |= cpu_to_le32((BUF_SIZE_8KiB | 35 | if (bfsize == BUF_SIZE_16KiB) |
35 | << ERDES1_BUFFER2_SIZE_SHIFT) | 36 | p->des1 |= cpu_to_le32((BUF_SIZE_8KiB |
36 | & ERDES1_BUFFER2_SIZE_MASK); | 37 | << ERDES1_BUFFER2_SIZE_SHIFT) |
38 | & ERDES1_BUFFER2_SIZE_MASK); | ||
37 | 39 | ||
38 | if (end) | 40 | if (end) |
39 | p->des1 |= cpu_to_le32(ERDES1_END_RING); | 41 | 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) | |||
59 | } | 61 | } |
60 | 62 | ||
61 | /* Normal descriptors */ | 63 | /* Normal descriptors */ |
62 | static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end) | 64 | static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end, int bfsize) |
63 | { | 65 | { |
64 | p->des1 |= cpu_to_le32(((BUF_SIZE_2KiB - 1) | 66 | if (bfsize >= BUF_SIZE_2KiB) { |
65 | << RDES1_BUFFER2_SIZE_SHIFT) | 67 | int bfsize2; |
66 | & RDES1_BUFFER2_SIZE_MASK); | 68 | |
69 | bfsize2 = min(bfsize - BUF_SIZE_2KiB + 1, BUF_SIZE_2KiB - 1); | ||
70 | p->des1 |= cpu_to_le32((bfsize2 << RDES1_BUFFER2_SIZE_SHIFT) | ||
71 | & RDES1_BUFFER2_SIZE_MASK); | ||
72 | } | ||
67 | 73 | ||
68 | if (end) | 74 | if (end) |
69 | p->des1 |= cpu_to_le32(RDES1_END_RING); | 75 | p->des1 |= cpu_to_le32(RDES1_END_RING); |
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c index 7fbb6a4dbf51..e061e9f5fad7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | |||
@@ -296,7 +296,7 @@ exit: | |||
296 | } | 296 | } |
297 | 297 | ||
298 | static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic, | 298 | static void dwmac4_rd_init_rx_desc(struct dma_desc *p, int disable_rx_ic, |
299 | int mode, int end) | 299 | int mode, int end, int bfsize) |
300 | { | 300 | { |
301 | dwmac4_set_rx_owner(p, disable_rx_ic); | 301 | dwmac4_set_rx_owner(p, disable_rx_ic); |
302 | } | 302 | } |
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c index 1d858fdec997..98fa471da7c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c | |||
@@ -123,7 +123,7 @@ static int dwxgmac2_get_rx_timestamp_status(void *desc, void *next_desc, | |||
123 | } | 123 | } |
124 | 124 | ||
125 | static void dwxgmac2_init_rx_desc(struct dma_desc *p, int disable_rx_ic, | 125 | static void dwxgmac2_init_rx_desc(struct dma_desc *p, int disable_rx_ic, |
126 | int mode, int end) | 126 | int mode, int end, int bfsize) |
127 | { | 127 | { |
128 | dwxgmac2_set_rx_owner(p, disable_rx_ic); | 128 | dwxgmac2_set_rx_owner(p, disable_rx_ic); |
129 | } | 129 | } |
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 5ef91a790f9d..5202d6ad7919 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c | |||
@@ -201,6 +201,11 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, | |||
201 | if (unlikely(rdes0 & RDES0_OWN)) | 201 | if (unlikely(rdes0 & RDES0_OWN)) |
202 | return dma_own; | 202 | return dma_own; |
203 | 203 | ||
204 | if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { | ||
205 | stats->rx_length_errors++; | ||
206 | return discard_frame; | ||
207 | } | ||
208 | |||
204 | if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { | 209 | if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { |
205 | if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { | 210 | if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { |
206 | x->rx_desc++; | 211 | x->rx_desc++; |
@@ -231,9 +236,10 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, | |||
231 | * It doesn't match with the information reported into the databook. | 236 | * It doesn't match with the information reported into the databook. |
232 | * At any rate, we need to understand if the CSUM hw computation is ok | 237 | * At any rate, we need to understand if the CSUM hw computation is ok |
233 | * and report this info to the upper layers. */ | 238 | * and report this info to the upper layers. */ |
234 | ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR), | 239 | if (likely(ret == good_frame)) |
235 | !!(rdes0 & RDES0_FRAME_TYPE), | 240 | ret = enh_desc_coe_rdes0(!!(rdes0 & RDES0_IPC_CSUM_ERROR), |
236 | !!(rdes0 & ERDES0_RX_MAC_ADDR)); | 241 | !!(rdes0 & RDES0_FRAME_TYPE), |
242 | !!(rdes0 & ERDES0_RX_MAC_ADDR)); | ||
237 | 243 | ||
238 | if (unlikely(rdes0 & RDES0_DRIBBLING)) | 244 | if (unlikely(rdes0 & RDES0_DRIBBLING)) |
239 | x->dribbling_bit++; | 245 | x->dribbling_bit++; |
@@ -259,15 +265,19 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, | |||
259 | } | 265 | } |
260 | 266 | ||
261 | static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, | 267 | static void enh_desc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, |
262 | int mode, int end) | 268 | int mode, int end, int bfsize) |
263 | { | 269 | { |
270 | int bfsize1; | ||
271 | |||
264 | p->des0 |= cpu_to_le32(RDES0_OWN); | 272 | p->des0 |= cpu_to_le32(RDES0_OWN); |
265 | p->des1 |= cpu_to_le32(BUF_SIZE_8KiB & ERDES1_BUFFER1_SIZE_MASK); | 273 | |
274 | bfsize1 = min(bfsize, BUF_SIZE_8KiB); | ||
275 | p->des1 |= cpu_to_le32(bfsize1 & ERDES1_BUFFER1_SIZE_MASK); | ||
266 | 276 | ||
267 | if (mode == STMMAC_CHAIN_MODE) | 277 | if (mode == STMMAC_CHAIN_MODE) |
268 | ehn_desc_rx_set_on_chain(p); | 278 | ehn_desc_rx_set_on_chain(p); |
269 | else | 279 | else |
270 | ehn_desc_rx_set_on_ring(p, end); | 280 | ehn_desc_rx_set_on_ring(p, end, bfsize); |
271 | 281 | ||
272 | if (disable_rx_ic) | 282 | if (disable_rx_ic) |
273 | p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC); | 283 | p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC); |
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 92b8944f26e3..5bb00234d961 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h | |||
@@ -33,7 +33,7 @@ struct dma_extended_desc; | |||
33 | struct stmmac_desc_ops { | 33 | struct stmmac_desc_ops { |
34 | /* DMA RX descriptor ring initialization */ | 34 | /* DMA RX descriptor ring initialization */ |
35 | void (*init_rx_desc)(struct dma_desc *p, int disable_rx_ic, int mode, | 35 | void (*init_rx_desc)(struct dma_desc *p, int disable_rx_ic, int mode, |
36 | int end); | 36 | int end, int bfsize); |
37 | /* DMA TX descriptor ring initialization */ | 37 | /* DMA TX descriptor ring initialization */ |
38 | void (*init_tx_desc)(struct dma_desc *p, int mode, int end); | 38 | void (*init_tx_desc)(struct dma_desc *p, int mode, int end); |
39 | /* Invoked by the xmit function to prepare the tx descriptor */ | 39 | /* Invoked by the xmit function to prepare the tx descriptor */ |
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index de65bb29feba..b7dd4e3c760d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c | |||
@@ -91,8 +91,6 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, | |||
91 | return dma_own; | 91 | return dma_own; |
92 | 92 | ||
93 | if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { | 93 | if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { |
94 | pr_warn("%s: Oversized frame spanned multiple buffers\n", | ||
95 | __func__); | ||
96 | stats->rx_length_errors++; | 94 | stats->rx_length_errors++; |
97 | return discard_frame; | 95 | return discard_frame; |
98 | } | 96 | } |
@@ -135,15 +133,19 @@ static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, | |||
135 | } | 133 | } |
136 | 134 | ||
137 | static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, | 135 | static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, |
138 | int end) | 136 | int end, int bfsize) |
139 | { | 137 | { |
138 | int bfsize1; | ||
139 | |||
140 | p->des0 |= cpu_to_le32(RDES0_OWN); | 140 | p->des0 |= cpu_to_le32(RDES0_OWN); |
141 | p->des1 |= cpu_to_le32((BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK); | 141 | |
142 | bfsize1 = min(bfsize, BUF_SIZE_2KiB - 1); | ||
143 | p->des1 |= cpu_to_le32(bfsize & RDES1_BUFFER1_SIZE_MASK); | ||
142 | 144 | ||
143 | if (mode == STMMAC_CHAIN_MODE) | 145 | if (mode == STMMAC_CHAIN_MODE) |
144 | ndesc_rx_set_on_chain(p, end); | 146 | ndesc_rx_set_on_chain(p, end); |
145 | else | 147 | else |
146 | ndesc_rx_set_on_ring(p, end); | 148 | ndesc_rx_set_on_ring(p, end, bfsize); |
147 | 149 | ||
148 | if (disable_rx_ic) | 150 | if (disable_rx_ic) |
149 | p->des1 |= cpu_to_le32(RDES1_DISABLE_IC); | 151 | p->des1 |= cpu_to_le32(RDES1_DISABLE_IC); |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 6a2e1031a62a..a26e36dbb5df 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | |||
@@ -1136,11 +1136,13 @@ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) | |||
1136 | if (priv->extend_desc) | 1136 | if (priv->extend_desc) |
1137 | stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, | 1137 | stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, |
1138 | priv->use_riwt, priv->mode, | 1138 | priv->use_riwt, priv->mode, |
1139 | (i == DMA_RX_SIZE - 1)); | 1139 | (i == DMA_RX_SIZE - 1), |
1140 | priv->dma_buf_sz); | ||
1140 | else | 1141 | else |
1141 | stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], | 1142 | stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], |
1142 | priv->use_riwt, priv->mode, | 1143 | priv->use_riwt, priv->mode, |
1143 | (i == DMA_RX_SIZE - 1)); | 1144 | (i == DMA_RX_SIZE - 1), |
1145 | priv->dma_buf_sz); | ||
1144 | } | 1146 | } |
1145 | 1147 | ||
1146 | /** | 1148 | /** |
@@ -3352,9 +3354,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) | |||
3352 | { | 3354 | { |
3353 | struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; | 3355 | struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; |
3354 | struct stmmac_channel *ch = &priv->channel[queue]; | 3356 | struct stmmac_channel *ch = &priv->channel[queue]; |
3355 | unsigned int entry = rx_q->cur_rx; | 3357 | unsigned int next_entry = rx_q->cur_rx; |
3356 | int coe = priv->hw->rx_csum; | 3358 | int coe = priv->hw->rx_csum; |
3357 | unsigned int next_entry; | ||
3358 | unsigned int count = 0; | 3359 | unsigned int count = 0; |
3359 | bool xmac; | 3360 | bool xmac; |
3360 | 3361 | ||
@@ -3372,10 +3373,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) | |||
3372 | stmmac_display_ring(priv, rx_head, DMA_RX_SIZE, true); | 3373 | stmmac_display_ring(priv, rx_head, DMA_RX_SIZE, true); |
3373 | } | 3374 | } |
3374 | while (count < limit) { | 3375 | while (count < limit) { |
3375 | int status; | 3376 | int entry, status; |
3376 | struct dma_desc *p; | 3377 | struct dma_desc *p; |
3377 | struct dma_desc *np; | 3378 | struct dma_desc *np; |
3378 | 3379 | ||
3380 | entry = next_entry; | ||
3381 | |||
3379 | if (priv->extend_desc) | 3382 | if (priv->extend_desc) |
3380 | p = (struct dma_desc *)(rx_q->dma_erx + entry); | 3383 | p = (struct dma_desc *)(rx_q->dma_erx + entry); |
3381 | else | 3384 | else |
@@ -3431,11 +3434,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) | |||
3431 | * ignored | 3434 | * ignored |
3432 | */ | 3435 | */ |
3433 | if (frame_len > priv->dma_buf_sz) { | 3436 | if (frame_len > priv->dma_buf_sz) { |
3434 | netdev_err(priv->dev, | 3437 | if (net_ratelimit()) |
3435 | "len %d larger than size (%d)\n", | 3438 | netdev_err(priv->dev, |
3436 | frame_len, priv->dma_buf_sz); | 3439 | "len %d larger than size (%d)\n", |
3440 | frame_len, priv->dma_buf_sz); | ||
3437 | priv->dev->stats.rx_length_errors++; | 3441 | priv->dev->stats.rx_length_errors++; |
3438 | break; | 3442 | continue; |
3439 | } | 3443 | } |
3440 | 3444 | ||
3441 | /* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3 | 3445 | /* 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) | |||
3470 | dev_warn(priv->device, | 3474 | dev_warn(priv->device, |
3471 | "packet dropped\n"); | 3475 | "packet dropped\n"); |
3472 | priv->dev->stats.rx_dropped++; | 3476 | priv->dev->stats.rx_dropped++; |
3473 | break; | 3477 | continue; |
3474 | } | 3478 | } |
3475 | 3479 | ||
3476 | dma_sync_single_for_cpu(priv->device, | 3480 | dma_sync_single_for_cpu(priv->device, |
@@ -3490,11 +3494,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) | |||
3490 | } else { | 3494 | } else { |
3491 | skb = rx_q->rx_skbuff[entry]; | 3495 | skb = rx_q->rx_skbuff[entry]; |
3492 | if (unlikely(!skb)) { | 3496 | if (unlikely(!skb)) { |
3493 | netdev_err(priv->dev, | 3497 | if (net_ratelimit()) |
3494 | "%s: Inconsistent Rx chain\n", | 3498 | netdev_err(priv->dev, |
3495 | priv->dev->name); | 3499 | "%s: Inconsistent Rx chain\n", |
3500 | priv->dev->name); | ||
3496 | priv->dev->stats.rx_dropped++; | 3501 | priv->dev->stats.rx_dropped++; |
3497 | break; | 3502 | continue; |
3498 | } | 3503 | } |
3499 | prefetch(skb->data - NET_IP_ALIGN); | 3504 | prefetch(skb->data - NET_IP_ALIGN); |
3500 | rx_q->rx_skbuff[entry] = NULL; | 3505 | rx_q->rx_skbuff[entry] = NULL; |
@@ -3529,7 +3534,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) | |||
3529 | priv->dev->stats.rx_packets++; | 3534 | priv->dev->stats.rx_packets++; |
3530 | priv->dev->stats.rx_bytes += frame_len; | 3535 | priv->dev->stats.rx_bytes += frame_len; |
3531 | } | 3536 | } |
3532 | entry = next_entry; | ||
3533 | } | 3537 | } |
3534 | 3538 | ||
3535 | stmmac_rx_refill(priv, queue); | 3539 | stmmac_rx_refill(priv, queue); |