From: Gennaro Cimmino Date: Sun, 26 Jul 2026 16:51:30 +0000 (+0200) Subject: realtek: eth: bound the packet length at the CPU port X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec229e1aa3358432b8faba270dd7c6c5a1dbef09;p=thirdparty%2Fopenwrt.git realtek: eth: bound the packet length at the CPU port The length of the frames the switch may hand to the CPU has nothing to do with the MTU the conduit runs at. The registers holding it keep whatever the bootloader left, and the only other mechanism in the path is the DMA truncation length, which does not drop what exceeds it: it cuts the frame short and hands the remains to the stack. Nothing in the target programs either of them from the MTU. Add a per SoC set_max_packet_length() that programs those registers from the conduit MTU and turns the truncation off, so an oversized frame is dropped rather than delivered mutilated. The registers differ per family, from the maps at svanheule.net, and every length field is 14 bits wide: RTL838x DMA_IF_PKT_RX_FLTR_CTRL 0x6b10 RX_MAX_LEN[13:0] DMA_IF_PKT_TX_FLTR_CTRL 0xaa6c TX_MAX_LEN[13:0] RTL839x DMA_IF_PKT_FLTR_CTRL 0x1000 RX_MAX_LEN[13:0] TX_MAX_LEN[27:14] RTL930x MAC_L2_CPU_MAX_LEN_CTRL 0xa3a0 CPU_PORT_TX_MAX_LEN[13:0] MAC_L2_PORT_MAX_LEN_CTRL 0x326c MAX_LEN_1G_2P5G_5G_10G_SEL MAX_LEN_100M_10M_SEL RTL931x MAC_L2_CPU_MAX_LEN_CTRL 0x1368 CPU_PORT_TX_MAX_LEN[13:0] CPU_PORT_RX_MAX_LEN[27:14] Longan is the only family whose CPU register carries a single field, covering what the switch sends towards the CPU. What it takes from the CPU lives in the port register of that port, 0x326c + port * 64, whose two fields hold the length for the slow and the fast link speeds. The vendor SDK reads the slow one there; both are programmed, as the driver does not know at which speed the internal port runs. Mango keeps its port registers in an array documented for ports 0 to 55, one short of its CPU port, so there the CPU register alone holds both directions. Characterised on an RTL9303 (Hasivo S1100W-8XGT-SE) by lowering the truncation length and the maximum length of the CPU port in turn, while frames of growing size were sent into a user port. The largest frame that survives is the maximum length minus four, and the truncation length minus eight, both constant at three settings of each register. Beyond that boundary the truncation delivers a frame cut short, seen on the conduit as 992 bytes where 1014 were sent, while the maximum length register lets nothing through. Read at the U-Boot prompt on the same board, reproduced across two boots, the bootloader leaves 0xa3a0 at 0x600, 0x396c at 0x0c003000 and DMA_IF_CTRL at 0x06400000, a truncation length of 1600 with every enable still clear. With this patch and the conduit at its idle MTU of 1504, both length registers read 1530, while DMA_IF_CTRL has its truncation bit clear and the transmit and receive enables set. Compile-tested only on RTL838x/839x/931x, where the offsets come from the maps alone and the effect has not been observed. This changes what those boards do at every interface open, not only when the MTU changes, so testing from their owners is welcome. On RTL838x the transmit length lives in a register of its own: https://svanheule.net/realtek/maple/register/dma_if_pkt_tx_fltr_ctrl Assisted-by: Claude:claude-opus-5 Signed-off-by: Gennaro Cimmino Link: https://github.com/openwrt/openwrt/pull/24421 Signed-off-by: Markus Stockhausen --- diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 3a62dc2b53f..05c2d3be940 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -38,11 +38,14 @@ #define NOTIFY_EVENTS 10 #define NOTIFY_BLOCKS 10 + #define RX_TRUNCATE_EN_93XX BIT(6) #define RX_TRUNCATE_EN_83XX BIT(4) #define TX_PAD_EN_838X BIT(5) #define SKB_MTU 1600 +/* Ethernet header, two stacked VLAN tags (802.1ad QinQ) and FCS */ +#define RTETH_FRAME_OVERHEAD (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN) /* TODO: change this to 1568 after fragment handling has been tested in the wild */ #define SKB_FRAG_SIZE 400 #define SKB_PAD MAX(32, L1_CACHE_BYTES) @@ -559,11 +562,63 @@ static void rteth_hw_ring_setup(struct rteth_ctrl *ctrl) offsetof(struct rteth_tx_data, ring)); } +/* + * Three limits bound the frames exchanged with the CPU port, and each family spreads them + * over registers of its own, whose length fields are named after the block holding them: + * what the driver receives is transmitted by the switch, but received by the NIC. + * + * - the DMA truncation length cuts a frame short. It still reaches the CPU, carrying only + * as many bytes as the field allows, so switch it off and leave the length to the others. + * - the largest frame the switch hands to the CPU, which drops the longer ones. Both + * measured on an RTL9303 by lowering one at a time while frames of growing size were + * sent into a user port. + * - the largest frame the switch takes from the CPU. Longan keeps it in the port register + * of the CPU port, Maple in a register of its own, the others in a second field of the + * register above. + */ +static void rteth_838x_set_max_packet_length(struct rteth_ctrl *ctrl, int len) +{ + regmap_update_bits(ctrl->map, RTETH_838X_DMA_IF_PKT_RX_FLTR_CTRL, GENMASK(13, 0), len); + regmap_update_bits(ctrl->map, RTETH_838X_DMA_IF_PKT_TX_FLTR_CTRL, GENMASK(13, 0), len); + regmap_clear_bits(ctrl->map, ctrl->r->dma_if_ctrl, RX_TRUNCATE_EN_83XX); +} + +static void rteth_839x_set_max_packet_length(struct rteth_ctrl *ctrl, int len) +{ + regmap_update_bits(ctrl->map, RTETH_839X_DMA_IF_PKT_FLTR_CTRL, + GENMASK(27, 14) | GENMASK(13, 0), + FIELD_PREP(GENMASK(27, 14), len) | FIELD_PREP(GENMASK(13, 0), len)); + regmap_clear_bits(ctrl->map, ctrl->r->dma_if_ctrl, RX_TRUNCATE_EN_83XX); +} + +static void rteth_930x_set_max_packet_length(struct rteth_ctrl *ctrl, int len) +{ + regmap_update_bits(ctrl->map, RTETH_930X_MAC_L2_CPU_MAX_LEN_CTRL, GENMASK(13, 0), len); + regmap_update_bits(ctrl->map, RTETH_930X_MAC_L2_PORT_MAX_LEN_CTRL, + GENMASK(27, 14) | GENMASK(13, 0), + FIELD_PREP(GENMASK(27, 14), len) | FIELD_PREP(GENMASK(13, 0), len)); + regmap_clear_bits(ctrl->map, ctrl->r->dma_if_ctrl, RX_TRUNCATE_EN_93XX); +} + +static void rteth_931x_set_max_packet_length(struct rteth_ctrl *ctrl, int len) +{ + regmap_update_bits(ctrl->map, RTETH_931X_MAC_L2_CPU_MAX_LEN_CTRL, + GENMASK(27, 14) | GENMASK(13, 0), + FIELD_PREP(GENMASK(27, 14), len) | FIELD_PREP(GENMASK(13, 0), len)); + regmap_clear_bits(ctrl->map, ctrl->r->dma_if_ctrl, RX_TRUNCATE_EN_93XX); +} + +static void rteth_set_max_packet_length(struct rteth_ctrl *ctrl) +{ + ctrl->r->set_max_packet_length(ctrl, ctrl->dev->mtu + RTETH_FRAME_OVERHEAD); +} + static void rteth_838x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Truncate RX buffer to SKB_MTU bytes, pad TX */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, - (SKB_MTU << 16) | RX_TRUNCATE_EN_83XX | TX_PAD_EN_838X); + /* Pad TX */ + regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, TX_PAD_EN_838X); + + rteth_set_max_packet_length(ctrl); rteth_enable_all_rx_irqs(ctrl); @@ -586,8 +641,7 @@ static void rteth_838x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_839x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Setup CPU-Port: RX Buffer */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 5) | RX_TRUNCATE_EN_83XX); + rteth_set_max_packet_length(ctrl); rteth_enable_all_rx_irqs(ctrl); @@ -610,8 +664,7 @@ static void rteth_839x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_930x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Setup CPU-Port: RX Buffer truncated at SKB_MTU Bytes */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 16) | RX_TRUNCATE_EN_93XX); + rteth_set_max_packet_length(ctrl); rteth_enable_all_rx_irqs(ctrl); @@ -627,8 +680,7 @@ static void rteth_930x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_931x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Setup CPU-Port: RX Buffer truncated at SKB_MTU Bytes */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 16) | RX_TRUNCATE_EN_93XX); + rteth_set_max_packet_length(ctrl); rteth_enable_all_rx_irqs(ctrl); @@ -1600,6 +1652,7 @@ static const struct rteth_config rteth_838x_cfg = { .hw_stop = &rteth_838x_hw_stop, .hw_reset = &rteth_838x_hw_reset, .init_mac = &rteth_838x_init_mac, + .set_max_packet_length = rteth_838x_set_max_packet_length, .netdev_ops = &rteth_838x_netdev_ops, }; @@ -1645,6 +1698,7 @@ static const struct rteth_config rteth_839x_cfg = { .hw_stop = &rteth_839x_hw_stop, .hw_reset = &rteth_839x_hw_reset, .init_mac = &rteth_839x_init_mac, + .set_max_packet_length = rteth_839x_set_max_packet_length, .setup_notify_ring_buffer = &rteth_839x_setup_notify_ring_buffer, .netdev_ops = &rteth_839x_netdev_ops, }; @@ -1692,6 +1746,7 @@ static const struct rteth_config rteth_930x_cfg = { .hw_stop = &rteth_930x_hw_stop, .hw_reset = &rteth_93xx_hw_reset, .init_mac = &rteth_930x_init_mac, + .set_max_packet_length = rteth_930x_set_max_packet_length, .netdev_ops = &rteth_930x_netdev_ops, }; @@ -1738,6 +1793,7 @@ static const struct rteth_config rteth_931x_cfg = { .hw_stop = &rteth_931x_hw_stop, .hw_reset = &rteth_93xx_hw_reset, .init_mac = &rteth_931x_init_mac, + .set_max_packet_length = rteth_931x_set_max_packet_length, .netdev_ops = &rteth_931x_netdev_ops, }; diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h index 51692c77882..ffee2e087fb 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h @@ -11,6 +11,8 @@ #define RTETH_838X_DMA_IF_CTRL (0x9f58) #define RTETH_838X_DMA_IF_INTR_MSK (0x9f50) #define RTETH_838X_DMA_IF_INTR_STS (0x9f54) +#define RTETH_838X_DMA_IF_PKT_RX_FLTR_CTRL (0x6b10) +#define RTETH_838X_DMA_IF_PKT_TX_FLTR_CTRL (0xaa6c) #define RTETH_838X_DMA_IF_RX_RING_CNTR (0xb7e8) #define RTETH_838X_DMA_IF_RX_RING_SIZE (0xb7e4) #define RTETH_838X_DMA_RX_BASE (0x9f00) @@ -30,6 +32,7 @@ #define RTETH_839X_DMA_IF_CTRL (0x786c) #define RTETH_839X_DMA_IF_INTR_MSK (0x7864) #define RTETH_839X_DMA_IF_INTR_STS (0x7868) +#define RTETH_839X_DMA_IF_PKT_FLTR_CTRL (0x1000) #define RTETH_839X_DMA_IF_RX_RING_CNTR (0x603c) #define RTETH_839X_DMA_IF_RX_RING_SIZE (0x6038) #define RTETH_839X_DMA_RX_BASE (0x780c) @@ -55,7 +58,9 @@ #define RTETH_930X_DMA_TX_BASE (0xe000) #define RTETH_930X_MAC_FORCE_MODE_CTRL (0xca1c + RTETH_930X_CPU_PORT * 4) #define RTETH_930X_MAC_L2_ADDR_CTRL (0xc714) +#define RTETH_930X_MAC_L2_CPU_MAX_LEN_CTRL (0xa3a0) #define RTETH_930X_MAC_L2_PORT_CTRL (0x3268 + RTETH_930X_CPU_PORT * 64) +#define RTETH_930X_MAC_L2_PORT_MAX_LEN_CTRL (0x326c + RTETH_930X_CPU_PORT * 64) #define RTETH_930X_QM_RSN2CPUQID_CTRL_0 (0xa344) #define RTETH_930X_QM_RSN2CPUQID_CTRL_CNT 11 #define RTETH_930X_RMA_CTRL_0 (0x9e60) @@ -72,6 +77,7 @@ #define RTETH_931X_DMA_TX_BASE (0x0900) #define RTETH_931X_MAC_FORCE_MODE_CTRL (0x0dcc + RTETH_931X_CPU_PORT * 4) #define RTETH_931X_MAC_L2_ADDR_CTRL (0x135c) +#define RTETH_931X_MAC_L2_CPU_MAX_LEN_CTRL (0x1368) #define RTETH_931X_MAC_L2_PORT_CTRL (0x6000 + RTETH_931X_CPU_PORT * 128) #define RTETH_931X_QM_RSN2CPUQID_CTRL_0 (0xa9f4) #define RTETH_931X_QM_RSN2CPUQID_CTRL_CNT 14 @@ -216,6 +222,7 @@ struct rteth_config { void (*hw_stop)(struct rteth_ctrl *ctrl); void (*hw_reset)(struct rteth_ctrl *ctrl); int (*init_mac)(struct rteth_ctrl *ctrl); + void (*set_max_packet_length)(struct rteth_ctrl *ctrl, int len); void (*setup_notify_ring_buffer)(struct rteth_ctrl *ctrl); void (*update_counter)(struct rteth_ctrl *ctrl, int ring, int released); const struct net_device_ops *netdev_ops;