The r39147 commit introduces a regression: at lease on some routers with ar8216 switch large packets get lost if 802.1q tagged port is used on the interface connected to the aforementioned switch.
The r39147 changes code in the way so interface is set to accept packets no longer than max ethernet frame length for a given mtu. Unfortunately ar8216 has a feature: it sends to additional bytes as a packet header and those this header needs to be added to the max frame length. Otherwise long enough packets get lost. The problem only manuifests itself if interface is used in vlan tagged mode. If integface is untagged then ar8216's header fits into space used by 802.1q tag and not packets are lost. This patch is tested and works with Trendnet TEW-632BRP. Signed-off-by Nikolay Martynov <mar.ko...@gmail.com> --- .../ar71xx/files/arch/mips/ath79/mach-tew-632brp.c | 1 + .../mips/include/asm/mach-ath79/ag71xx_platform.h | 21 +++++++++++++++++++++ .../net/ethernet/atheros/ag71xx/ag71xx_main.c | 13 ++++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-tew-632brp.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-tew-632brp.c index 855664e..accfb0e 100644 --- a/target/linux/ar71xx/files/arch/mips/ath79/mach-tew-632brp.c +++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-tew-632brp.c @@ -88,6 +88,7 @@ static void __init tew_632brp_setup(void) ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ath79_eth0_data.phy_mask = TEW_632BRP_LAN_PHYMASK; + ath79_eth0_data.has_ar8216_headers = 1; ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RMII; ath79_eth1_data.phy_mask = TEW_632BRP_WAN_PHYMASK; diff --git a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h index d46dc4e..2585a08 100644 --- a/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h +++ b/target/linux/ar71xx/files/arch/mips/include/asm/mach-ath79/ag71xx_platform.h @@ -35,7 +35,28 @@ struct ag71xx_platform_data { u8 is_ar91xx:1; u8 is_ar7240:1; u8 is_ar724x:1; + + /* + These two flags are not to be confused. The main issue here + is that ar8216 switch passes two additional bytes with each + packet and these two bytes need to be dealt with in the + kernel side of things. So: + + has_ar8216: the device has an ar8216 switch, but for some + reason it is impossible to communicate with it using normal + control interface. This means that generic driver is used, + and two byte header is removed in ag71xx code. + + has_ar8216_headers: the device has an ar8216 switch and + ar8216 driver is working fine for it. The ar8216 driver + handles the header. Unfortunately the length of the header + counts towards max packet length, so we need to increase + max packet length for such devices. Otherwise packet with + maximum payload length (say 1500) will be dropped by + hardware because it is considered two bytes too long. + */ u8 has_ar8216:1; + u8 has_ar8216_headers:1; struct ag71xx_switch_platform_data *switch_data; diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c index 715c1ce..5bc3e42 100644 --- a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c +++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c @@ -28,9 +28,12 @@ static int ag71xx_msg_level = -1; module_param_named(msg_level, ag71xx_msg_level, int, 0); MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)"); -static inline unsigned int ag71xx_max_frame_len(unsigned int mtu) +static inline unsigned int ag71xx_max_frame_len(struct ag71xx *ag, unsigned int mtu) { - return ETH_HLEN + VLAN_HLEN + mtu + ETH_FCS_LEN; + unsigned int switch_headers = + ag71xx_get_pdata(ag)->has_ar8216_headers + || ag71xx_get_pdata(ag)->has_ar8216 ? 2 : 0; + return ETH_HLEN + VLAN_HLEN + mtu + ETH_FCS_LEN + switch_headers; } static void ag71xx_dump_dma_regs(struct ag71xx *ag) @@ -510,7 +513,7 @@ static void ag71xx_fast_reset(struct ag71xx *ag) /* setup max frame length */ ag71xx_wr(ag, AG71XX_REG_MAC_MFL, - ag71xx_max_frame_len(ag->dev->mtu)); + ag71xx_max_frame_len(ag, ag->dev->mtu)); ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds); ag71xx_wr(ag, AG71XX_REG_TX_DESC, tx_ds); @@ -619,7 +622,7 @@ static int ag71xx_open(struct net_device *dev) unsigned int max_frame_len; int ret; - max_frame_len = ag71xx_max_frame_len(dev->mtu); + max_frame_len = ag71xx_max_frame_len(ag, dev->mtu); ag->rx_buf_size = max_frame_len + NET_SKB_PAD + NET_IP_ALIGN; /* setup max frame length */ @@ -1068,7 +1071,7 @@ static int ag71xx_change_mtu(struct net_device *dev, int new_mtu) struct ag71xx *ag = netdev_priv(dev); unsigned int max_frame_len; - max_frame_len = ag71xx_max_frame_len(new_mtu); + max_frame_len = ag71xx_max_frame_len(ag, new_mtu); if (new_mtu < 68 || max_frame_len > ag->max_frame_len) return -EINVAL; -- Thanks. Nikolay. _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel