diff options
| author | Serge Semin <fancer.lancer@gmail.com> | 2025-07-14 20:04:28 +0300 |
|---|---|---|
| committer | Serge Semin <fancer.lancer@gmail.com> | 2026-08-13 20:31:43 +0300 |
| commit | f5e4e87c3c9c9c1de2a2cd17ce9ecc03d63b1775 (patch) | |
| tree | a36305ac669b8d890b27ce9fdad8d0032599e264 | |
| parent | 36658042c830defcf2cc5b3b535827a7742b082b (diff) | |
| download | linux-f5e4e87c3c9c9c1de2a2cd17ce9ecc03d63b1775.tar.gz linux-f5e4e87c3c9c9c1de2a2cd17ce9ecc03d63b1775.zip | |
net: stmmac: dwmac1000: Fix inappropriate Rx buffer setup
Currently the Rx-buffer is selected based on the interface MTU and is
limited to one of the next sizes in the stmmac_set_bfsize() method:
BUF_SIZE_16KiB 16368 (0x3FF0)
BUF_SIZE_8KiB 8188 (0x1FFC)
BUF_SIZE_4KiB 4096 (0x1000)
BUF_SIZE_2KiB 2048 (0x800)
DEFAULT_BUFSIZE 1536 (0x600)
In the meantime the MTU can be no greater than 16368, 9000 or PAGE_SIZE.
There are several problems in that.
First of all the 8K and 16K sizes are too big for the DW GMAC with Normal
descriptor and with Chained Enhanced descriptors. Here is the max size of
the Rx buffers depending on the DW MAC version:
DW GMAC Normal Rx descriptor: 2K (chain), 2x2K (ring)
DW GMAC Enhanced Rx descriptor: 8K (chain), 2x8K (ring)
DW QoS Ethernet: 16K (ring)
DW XGMAC/XLGMAC: 16k (ring)
This has been like that for the Normal descriptors since the initial
driver commit and for the Chained Enhanced Descriptors since the
b2f3a481c4cd ("net: stmmac: Enable 16KB buffer size"). Moreover the later
commit made the stmmac_desc_ops::set_16kib_bfsize() callbacks useless
since stmmac_set_bfsize() may setup the 16KiB buffer size anyway.
Second is more series problem. The buffer size is determined purely based
on MTU with no Ethernet header taken into account. That will definitely
cause problems should MTU be specified close to the threshold values listed
above. There will be no room for the entire MTU-size frames and they will
be just discarded.
Thirdly the buffer size alignment is kind of random. It's 16 bytes for the
BUF_SIZE_16KiB buffer size, 4 bytes - for the BUF_SIZE_8KiB buffer size,
and 4K/2K/1536 bytes - for the rest of the cases. This is also problematic
since based on the databooks the "Rx buffer size must be a multiple of 4,
8 or 16 depending on the bus widths, otherwise the transfer might result
into undefined behavior." As you can see currently the requirement isn't
fully fulfilled since the bus-width isn't taken into account in the driver
at all, and the BUF_SIZE_16KiB size only align to the widest data-bus
width. Moreover the BUF_SIZE_4KiB and BUF_SIZE_2KiB buf sizes exceed the
Normal Descriptor buf size constraint and being converted to just 1 byte
aligned sizes, which definitely violate the requirement denoted earlier.
So let's fix all the problems above at once since each of them concern the
same part of the code in the driver and fixing one would require fixing
another. For that to be properly done the set_16kib_bfsize() method
must be replaced with stmmac_desc_ops::get_rx_len() since the buffer size
depends on the descriptor format and the descriptors linkage mode. Then
to be on a safe side the strictest alignment must be applied during the
buffer size calculation which must also take into account the Ethernet
header size.
Note as a side-effect of this change the MTU alignment will be no longer
needed, which basically means to revert a part of the commit eaf4fac47807
("net: stmmac: Do not accept invalid MTU values"). Not that it has been
proper implemented there anyway since the alignment requirement is
applicable to the Rx buffer size only (as noted above in the log message).
Fixes: b2f3a481c4cd ("net: stmmac: Enable 16KB buffer size")
Fixes: 47dd7a540b8a ("net: add support for STMicroelectronics Ethernet controllers.")
Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/common.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/descs_com.h | 59 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 20 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 20 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.c | 5 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 17 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 9 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 65 |
11 files changed, 125 insertions, 85 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 82ae61ccfd78..c41e15124472 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -532,6 +532,8 @@ struct dma_features { #define BUF_SIZE_8KiB 8188 #define BUF_SIZE_4KiB 4096 #define BUF_SIZE_2KiB 2048 +#define STMMAC_RX_BUF_ALIGN(x) ALIGN_DOWN(x, 16) +#define STMMAC_RX_BUF_ADJUST(x) ALIGN(x, 16) /* Power Down and WOL */ #define PMT_NOT_SUPPORTED 0 diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h index bedb18538d51..6e71832f5a90 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h @@ -19,17 +19,29 @@ /* 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 enh_desc_rx_set_on_ring(struct dma_desc *p, int end, int bfsize) { - if (bfsize == BUF_SIZE_16KiB) - p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER2_SIZE_MASK, - BUF_SIZE_8KiB)); + if (bfsize > STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK)) { + u32 buf1_len = STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK); + u32 buf2_len = FIELD_PREP(ERDES1_BUFFER2_SIZE_MASK, + bfsize - buf1_len); + + p->des1 |= cpu_to_le32(buf1_len | buf2_len); + } else { + p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER1_SIZE_MASK, bfsize)); + } if (end) p->des1 |= cpu_to_le32(ERDES1_END_RING); } +static inline unsigned int enh_desc_rx_desc_len_on_ring(void) +{ + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER1_SIZE_MASK)) + + STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER2_SIZE_MASK)); +} + static inline void enh_desc_end_tx_desc_on_ring(struct dma_desc *p, int end) { if (end) @@ -60,18 +72,25 @@ static inline unsigned int enh_desc_tx_desc_len_on_ring(void) /* Normal descriptors */ static inline void ndesc_rx_set_on_ring(struct dma_desc *p, int end, int bfsize) { - if (bfsize >= BUF_SIZE_2KiB) { - int bfsize2; + if (bfsize > STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK)) { + u32 buf1_len = STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK); + u32 buf2_len = FIELD_PREP(RDES1_BUFFER2_SIZE_MASK, bfsize - buf1_len); - bfsize2 = min(bfsize - BUF_SIZE_2KiB + 1, BUF_SIZE_2KiB - 1); - p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER2_SIZE_MASK, - bfsize2)); + p->des1 |= cpu_to_le32(buf1_len | buf2_len); + } else { + p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER1_SIZE_MASK, bfsize)); } if (end) p->des1 |= cpu_to_le32(RDES1_END_RING); } +static inline unsigned int ndesc_rx_desc_len_on_ring(void) +{ + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER1_SIZE_MASK)) + + STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER2_SIZE_MASK)); +} + static inline void ndesc_end_tx_desc_on_ring(struct dma_desc *p, int end) { if (end) @@ -102,9 +121,16 @@ static inline unsigned int ndesc_tx_desc_len_on_ring(void) /* Specific functions used for Chain mode */ /* Enhanced descriptors */ -static inline void ehn_desc_rx_set_on_chain(struct dma_desc *p) +static inline void enh_desc_rx_set_on_chain(struct dma_desc *p, int bfsize) +{ + bfsize = umin(bfsize, STMMAC_RX_BUF_ALIGN(ERDES1_BUFFER1_SIZE_MASK)); + p->des1 |= cpu_to_le32(FIELD_PREP(ERDES1_BUFFER1_SIZE_MASK, bfsize) | + ERDES1_SECOND_ADDRESS_CHAINED); +} + +static inline unsigned int enh_desc_rx_desc_len_on_chain(void) { - p->des1 |= cpu_to_le32(ERDES1_SECOND_ADDRESS_CHAINED); + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(ERDES1_BUFFER1_SIZE_MASK)); } static inline void enh_desc_end_tx_desc_on_chain(struct dma_desc *p, dma_addr_t np, @@ -127,9 +153,16 @@ static inline unsigned int enh_desc_tx_desc_len_on_chain(void) } /* Normal descriptors */ -static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int end) +static inline void ndesc_rx_set_on_chain(struct dma_desc *p, int bfsize) +{ + bfsize = umin(bfsize, STMMAC_RX_BUF_ALIGN(RDES1_BUFFER1_SIZE_MASK)); + p->des1 |= cpu_to_le32(FIELD_PREP(RDES1_BUFFER1_SIZE_MASK, bfsize) | + RDES1_SECOND_ADDRESS_CHAINED); +} + +static inline unsigned int ndesc_rx_desc_len_on_chain(void) { - p->des1 |= cpu_to_le32(RDES1_SECOND_ADDRESS_CHAINED); + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(RDES1_BUFFER1_SIZE_MASK)); } static inline void ndesc_end_tx_desc_on_chain(struct dma_desc *p, dma_addr_t np, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c index 90c1567b19b6..31e1c2ad9079 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c @@ -11,6 +11,7 @@ #include <linux/stmmac.h> #include "common.h" #include "dwmac4.h" +#include "dwmac4_dma.h" #include "dwmac4_descs.h" static int dwmac4_wrback_get_tx_status(struct stmmac_extra_stats *x, @@ -333,6 +334,11 @@ static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end) p->des3 = 0; } +static unsigned int dwmac4_rd_get_rx_len(int mode) +{ + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(DMA_RBSZ_MASK)); +} + static unsigned int dwmac4_rd_get_tx_len(int mode) { return FIELD_MAX(TDES2_BUFFER1_SIZE_MASK); @@ -509,15 +515,6 @@ static void dwmac4_set_sarc(struct dma_desc *p, u32 sarc_type) sarc_type)); } -static int set_16kib_bfsize(int mtu) -{ - int ret = 0; - - if (unlikely(mtu >= BUF_SIZE_8KiB)) - ret = BUF_SIZE_16KiB; - return ret; -} - static void dwmac4_set_vlan_tag(struct dma_desc *p, u16 tag, u16 inner_tag, u32 inner_type) { @@ -572,6 +569,7 @@ static void dwmac4_set_tbs(struct dma_edesc *p, u32 sec, u32 nsec) const struct stmmac_desc_ops dwmac4_desc_ops = { .tx_status = dwmac4_wrback_get_tx_status, .rx_status = dwmac4_wrback_get_rx_status, + .get_rx_len = dwmac4_rd_get_rx_len, .get_tx_len = dwmac4_rd_get_tx_len, .get_tx_owner = dwmac4_get_tx_owner, .set_tx_owner = dwmac4_set_tx_owner, @@ -601,7 +599,3 @@ const struct stmmac_desc_ops dwmac4_desc_ops = { .set_sec_addr = dwmac4_set_sec_addr, .set_tbs = dwmac4_set_tbs, }; - -const struct stmmac_mode_ops dwmac4_ring_mode_ops = { - .set_16kib_bfsize = set_16kib_bfsize, -}; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h index c089e2fdd901..e3562fb1ea61 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h @@ -143,7 +143,6 @@ /* TDS3 use for both format (read and write back) */ #define RDES3_OWN BIT(31) -extern const struct stmmac_mode_ops dwmac4_ring_mode_ops; extern const struct stmmac_desc_ops dwmac4_desc_ops; #endif /* __DWMAC4_DESCS_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c index a4b40adeda18..14f2da1e5f02 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c @@ -242,6 +242,11 @@ static void dwxgmac2_init_tx_desc(struct dma_desc *p, int mode, int end) p->des3 = 0; } +static unsigned int dwxgmac2_get_rx_len(int mode) +{ + return STMMAC_RX_BUF_ALIGN(FIELD_MAX(XGMAC_RBSZ)); +} + static unsigned int dwxgmac2_get_tx_len(int mode) { return FIELD_MAX(XGMAC_TDES2_B1L); @@ -445,6 +450,7 @@ static void dwxgmac2_set_tbs(struct dma_edesc *p, u32 sec, u32 nsec) const struct stmmac_desc_ops dwxgmac210_desc_ops = { .tx_status = dwxgmac2_get_tx_status, .rx_status = dwxgmac2_get_rx_status, + .get_rx_len = dwxgmac2_get_rx_len, .get_tx_len = dwxgmac2_get_tx_len, .get_tx_owner = dwxgmac2_get_tx_owner, .set_tx_owner = dwxgmac2_set_tx_owner, diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 6e2a9320bfb2..0781700a554d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -284,17 +284,12 @@ static int enh_desc_get_rx_status_ext(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 bfsize) { - int bfsize1; - p->des0 |= cpu_to_le32(RDES0_OWN); - 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); + enh_desc_rx_set_on_chain(p, bfsize); else - ehn_desc_rx_set_on_ring(p, end, bfsize); + enh_desc_rx_set_on_ring(p, end, bfsize); if (disable_rx_ic) p->des1 |= cpu_to_le32(ERDES1_DISABLE_IC); @@ -324,6 +319,14 @@ static void enh_desc_set_rx_owner(struct dma_desc *p, int disable_rx_ic) p->des0 |= cpu_to_le32(RDES0_OWN); } +static unsigned int enh_desc_get_rx_len(int mode) +{ + if (mode == STMMAC_CHAIN_MODE) + return enh_desc_rx_desc_len_on_chain(); + else + return enh_desc_rx_desc_len_on_ring(); +} + static int enh_desc_get_tx_ls(struct dma_desc *p) { return (le32_to_cpu(p->des0) & ETDES0_LAST_SEGMENT) >> 29; @@ -476,6 +479,7 @@ static void enh_desc_clear(struct dma_desc *p) const struct stmmac_desc_ops enh_desc_ops = { .tx_status = enh_desc_get_tx_status, .rx_status = enh_desc_get_rx_status_nocoe, + .get_rx_len = enh_desc_get_rx_len, .get_tx_len = enh_desc_get_tx_len, .init_rx_desc = enh_desc_init_rx_desc, .init_tx_desc = enh_desc_init_tx_desc, @@ -499,6 +503,7 @@ const struct stmmac_desc_ops enh_desc_ops = { const struct stmmac_desc_ops enh_desc_noext_ops = { .tx_status = enh_desc_get_tx_status, .rx_status = enh_desc_get_rx_status_noext, + .get_rx_len = enh_desc_get_rx_len, .get_tx_len = enh_desc_get_tx_len, .init_rx_desc = enh_desc_init_rx_desc, .init_tx_desc = enh_desc_init_tx_desc, @@ -522,6 +527,7 @@ const struct stmmac_desc_ops enh_desc_noext_ops = { const struct stmmac_desc_ops enh_desc_ext_ops = { .tx_status = enh_desc_get_tx_status, .rx_status = enh_desc_get_rx_status_ext, + .get_rx_len = enh_desc_get_rx_len, .get_tx_len = enh_desc_get_tx_len, .init_rx_desc = enh_desc_init_rx_desc, .init_tx_desc = enh_desc_init_tx_desc, diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c index bc1e585c854d..35d9f058cea2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.c +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c @@ -207,7 +207,6 @@ static const struct stmmac_hwif_entry { .vlan = &dwmac_vlan_ops, .hwtimestamp = &stmmac_ptp, .ptp = &stmmac_ptp_clock_ops, - .mode = &dwmac4_ring_mode_ops, .tc = &dwmac510_tc_ops, .mmc = &dwmac_mmc_ops, .est = &dwmac510_est_ops, @@ -228,7 +227,6 @@ static const struct stmmac_hwif_entry { .vlan = &dwmac_vlan_ops, .hwtimestamp = &stmmac_ptp, .ptp = &stmmac_ptp_clock_ops, - .mode = &dwmac4_ring_mode_ops, .tc = &dwmac510_tc_ops, .mmc = &dwmac_mmc_ops, .est = &dwmac510_est_ops, @@ -249,7 +247,6 @@ static const struct stmmac_hwif_entry { .vlan = &dwmac_vlan_ops, .hwtimestamp = &stmmac_ptp, .ptp = &stmmac_ptp_clock_ops, - .mode = &dwmac4_ring_mode_ops, .tc = &dwmac510_tc_ops, .mmc = &dwmac_mmc_ops, .est = &dwmac510_est_ops, @@ -271,7 +268,6 @@ static const struct stmmac_hwif_entry { .vlan = &dwxgmac210_vlan_ops, .hwtimestamp = &stmmac_ptp, .ptp = &stmmac_ptp_clock_ops, - .mode = NULL, .tc = &dwmac510_tc_ops, .mmc = &dwxgmac_mmc_ops, .est = &dwmac510_est_ops, @@ -293,7 +289,6 @@ static const struct stmmac_hwif_entry { .vlan = &dwxlgmac2_vlan_ops, .hwtimestamp = &stmmac_ptp, .ptp = &stmmac_ptp_clock_ops, - .mode = NULL, .tc = &dwmac510_tc_ops, .mmc = &dwxgmac_mmc_ops, .est = &dwmac510_est_ops, diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 085e2b062478..2216c25abc66 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -67,6 +67,7 @@ struct stmmac_desc_ops { /* Return the transmit status looking at the TDES1 */ int (*tx_status)(struct stmmac_extra_stats *x, struct dma_desc *p); /* Get the buffer(s) size for a single descriptor */ + unsigned int (*get_rx_len)(int mode); unsigned int (*get_tx_len)(int mode); /* Handle extra events on specific interrupts hw dependent */ void (*set_rx_owner)(struct dma_desc *p, int disable_rx_ic); @@ -128,6 +129,8 @@ struct stmmac_desc_ops { stmmac_do_callback(__priv, desc, get_rx_vlan_valid, __args) #define stmmac_tx_status(__priv, __args...) \ stmmac_do_callback(__priv, desc, tx_status, __args) +#define stmmac_get_rx_len(__priv, __args...) \ + stmmac_do_callback(__priv, desc, get_rx_len, __args) #define stmmac_get_tx_len(__priv, __args...) \ stmmac_do_callback(__priv, desc, get_tx_len, __args) #define stmmac_set_rx_owner(__priv, __args...) \ @@ -548,15 +551,12 @@ struct stmmac_rx_queue; struct stmmac_mode_ops { void (*init) (void *des, dma_addr_t phy_addr, unsigned int size, unsigned int extend_desc); - int (*set_16kib_bfsize)(int mtu); void (*init_desc3)(struct dma_desc *p); void (*refill_desc3)(struct stmmac_rx_queue *rx_q, struct dma_desc *p); }; #define stmmac_mode_init(__priv, __args...) \ stmmac_do_void_callback(__priv, mode, init, __args) -#define stmmac_set_16kib_bfsize(__priv, __args...) \ - stmmac_do_callback(__priv, mode, set_16kib_bfsize, __args) #define stmmac_init_desc3(__priv, __args...) \ stmmac_do_void_callback(__priv, mode, init_desc3, __args) #define stmmac_refill_desc3(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 3ca188c934bc..95d3031f4ab9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -158,15 +158,10 @@ static int ndesc_get_rx_status_coe2(struct stmmac_extra_stats *x, static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, int end, int bfsize) { - int bfsize1; - p->des0 |= cpu_to_le32(RDES0_OWN); - bfsize1 = min(bfsize, BUF_SIZE_2KiB - 1); - p->des1 |= cpu_to_le32(bfsize1 & RDES1_BUFFER1_SIZE_MASK); - if (mode == STMMAC_CHAIN_MODE) - ndesc_rx_set_on_chain(p, end); + ndesc_rx_set_on_chain(p, bfsize); else ndesc_rx_set_on_ring(p, end, bfsize); @@ -198,6 +193,14 @@ static void ndesc_set_rx_owner(struct dma_desc *p, int disable_rx_ic) p->des0 |= cpu_to_le32(RDES0_OWN); } +static unsigned int ndesc_get_rx_len(int mode) +{ + if (mode == STMMAC_CHAIN_MODE) + return ndesc_rx_desc_len_on_chain(); + else + return ndesc_rx_desc_len_on_ring(); +} + static int ndesc_get_tx_ls(struct dma_desc *p) { return (le32_to_cpu(p->des1) & TDES1_LAST_SEGMENT) >> 30; @@ -332,6 +335,7 @@ const struct stmmac_desc_ops ndesc_ops = { .tx_status = ndesc_get_tx_status, .rx_status = ndesc_get_rx_status_nocoe, .get_tx_len = ndesc_get_tx_len, + .get_rx_len = ndesc_get_rx_len, .init_rx_desc = ndesc_init_rx_desc, .init_tx_desc = ndesc_init_tx_desc, .get_tx_owner = ndesc_get_tx_owner, @@ -354,6 +358,7 @@ const struct stmmac_desc_ops ndesc_ops = { const struct stmmac_desc_ops ndesc_rxcoe2_ops = { .tx_status = ndesc_get_tx_status, .rx_status = ndesc_get_rx_status_coe2, + .get_rx_len = ndesc_get_rx_len, .get_tx_len = ndesc_get_tx_len, .init_rx_desc = ndesc_init_rx_desc, .init_tx_desc = ndesc_init_tx_desc, diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index ff3ddb800d04..916396cb59b8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -29,16 +29,7 @@ static void init_desc3(struct dma_desc *p) p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); } -static int set_16kib_bfsize(int mtu) -{ - int ret = 0; - if (unlikely(mtu > BUF_SIZE_8KiB)) - ret = BUF_SIZE_16KiB; - return ret; -} - const struct stmmac_mode_ops ring_mode_ops = { .refill_desc3 = refill_desc3, .init_desc3 = init_desc3, - .set_16kib_bfsize = set_16kib_bfsize, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7d47b0ea1a2b..44f3ef3ff361 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -63,7 +63,6 @@ */ #define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCTRLSSR) -#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16) #define TSO_MAX_LOAD_SIZE SZ_256K #define TSO_MAX_BUFF_SIZE (SZ_16K - 1) @@ -1578,22 +1577,42 @@ static unsigned int stmmac_rx_offset(struct stmmac_priv *priv) return NET_SKB_PAD + stmmac_fs_offset(); } -static int stmmac_set_bfsize(int mtu) +/** + * stmmac_get_bfsize- determine Rx DMA-buffer size + * @priv: driver private structure + * @mtu : requested MTU + * Description: calculate the controller Rx DMA-buffer size suitable + * for the passed MTU value. + * + * Return: the Rx DMA-buffer size. + */ +static unsigned int stmmac_get_bfsize(struct stmmac_priv *priv, int mtu) { - int ret; + unsigned int buff_size, buff_max; - if (mtu >= BUF_SIZE_8KiB) - ret = BUF_SIZE_16KiB; - else if (mtu >= BUF_SIZE_4KiB) - ret = BUF_SIZE_8KiB; - else if (mtu >= BUF_SIZE_2KiB) - ret = BUF_SIZE_4KiB; - else if (mtu > DEFAULT_BUFSIZE) - ret = BUF_SIZE_2KiB; + /* Make sure there is a place for the entire frame including + * the Ethernet header plus possible C/S-VLAN headers. + */ + buff_size = umax(mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN, buf_sz); + if (buff_size >= BUF_SIZE_8KiB) + buff_size = BUF_SIZE_16KiB; + else if (buff_size >= BUF_SIZE_4KiB) + buff_size = BUF_SIZE_8KiB; + else if (buff_size >= BUF_SIZE_2KiB) + buff_size = BUF_SIZE_4KiB; + else if (buff_size > DEFAULT_BUFSIZE) + buff_size = BUF_SIZE_2KiB; else - ret = DEFAULT_BUFSIZE; + buff_size = DEFAULT_BUFSIZE; - return ret; + /* Make sure the buffer is bus-width aligned up to fit in all the DMA + * writes and to avoid undefined behaviour (see notes in the databooks). + */ + buff_size = STMMAC_RX_BUF_ADJUST(buff_size); + + /* Apply the max buf size constraint specific to the particular MAC */ + buff_max = stmmac_get_rx_len(priv, priv->mode); + return umin(buff_size, buff_max); } /** @@ -4057,7 +4076,7 @@ static struct stmmac_dma_conf * stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) { struct stmmac_dma_conf *dma_conf; - int chan, bfsize, ret; + int chan, ret; dma_conf = kzalloc_obj(*dma_conf); if (!dma_conf) { @@ -4066,15 +4085,8 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) return ERR_PTR(-ENOMEM); } - /* Returns 0 or BUF_SIZE_16KiB if mtu > 8KiB and dwmac4 or ring mode */ - bfsize = stmmac_set_16kib_bfsize(priv, mtu); - if (bfsize < 0) - bfsize = 0; + dma_conf->dma_buf_sz = stmmac_get_bfsize(priv, mtu); - if (bfsize < BUF_SIZE_16KiB) - bfsize = stmmac_set_bfsize(max(buf_sz, mtu)); - - dma_conf->dma_buf_sz = bfsize; /* Chose the tx/rx size from the already defined one in the * priv struct. (if defined) */ @@ -6227,7 +6239,6 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) struct stmmac_priv *priv = netdev_priv(dev); int txfifosz = priv->plat->tx_fifo_size; struct stmmac_dma_conf *dma_conf; - const int mtu = new_mtu; int ret; if (txfifosz == 0) @@ -6240,8 +6251,6 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; } - new_mtu = STMMAC_ALIGN(new_mtu); - /* If condition true, FIFO is too small or MTU too large */ if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) return -EINVAL; @@ -6249,10 +6258,10 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) if (netif_running(dev)) { netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); /* Try to allocate the new DMA conf with the new mtu */ - dma_conf = stmmac_setup_dma_desc(priv, mtu); + dma_conf = stmmac_setup_dma_desc(priv, new_mtu); if (IS_ERR(dma_conf)) { netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n", - mtu); + new_mtu); return PTR_ERR(dma_conf); } @@ -6271,7 +6280,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu) stmmac_set_rx_mode(dev); } - WRITE_ONCE(dev->mtu, mtu); + WRITE_ONCE(dev->mtu, new_mtu); netdev_update_features(dev); return 0; |
