The AX88772B occasionally send rx packets that cross urb boundaries and the remaining partial packet is sent with no hardware header. When the buffer with a partial packet is of less number of octets than the value of hard_header_len the buffer is discarded by the usbnet module. This is causing dropped packages and error messages in dmesg.
This can be reproduced by using ping with a packet size between 1965-1976. The bug has been reported here: https://bugzilla.kernel.org/show_bug.cgi?id=29082 This patch introduces a new flag that enable minidrivers to disable the cleaning up of small partial packets with no hardware header. It is likely that other minidrivers will want to use this flag, currently the cx82310_eth is describing the same problem and solving it by setting the hard_header_len to 0. This patch also makes it more obvious to minidriver writers that the usbnet module is discarding small skbs, which I believe has caused some confusion. Signed-off-by: Emil Goode <emilgo...@gmail.com> Reported-by: Igor Gnatenko <i.gnatenko.br...@gmail.com> --- v2: This patch solves the bug by introducing a new flag instead of setting hard_header_len to 0. I realize that there are already a lot of flags but hard_header_len is used to calculate the mtu values in the usbnet_change_mtu, usbnet_probe functions and I believe it's better not to change it. drivers/net/usb/asix_devices.c | 10 ++++++---- drivers/net/usb/usbnet.c | 3 ++- include/linux/usb/usbnet.h | 3 +++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 9765a7d..7ced4ed 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -891,7 +891,8 @@ static const struct driver_info ax88772_info = { .status = asix_status, .link_reset = ax88772_link_reset, .reset = ax88772_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | + FLAG_MULTI_PACKET | FLAG_PARTIAL_RX_PKT, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, }; @@ -904,7 +905,7 @@ static const struct driver_info ax88772b_info = { .link_reset = ax88772_link_reset, .reset = ax88772_reset, .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | - FLAG_MULTI_PACKET, + FLAG_MULTI_PACKET | FLAG_PARTIAL_RX_PKT, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, .data = FLAG_EEPROM_MAC, @@ -917,7 +918,8 @@ static const struct driver_info ax88178_info = { .status = asix_status, .link_reset = ax88178_link_reset, .reset = ax88178_reset, - .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR, + .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | + FLAG_PARTIAL_RX_PKT, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, }; @@ -939,7 +941,7 @@ static const struct driver_info hg20f9_info = { .link_reset = ax88772_link_reset, .reset = ax88772_reset, .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | - FLAG_MULTI_PACKET, + FLAG_MULTI_PACKET | FLAG_PARTIAL_RX_PKT, .rx_fixup = asix_rx_fixup_common, .tx_fixup = asix_tx_fixup, .data = FLAG_EEPROM_MAC, diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 4671da7..a1e6964 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -574,7 +574,8 @@ static void rx_complete (struct urb *urb) switch (urb_status) { /* success */ case 0: - if (skb->len < dev->net->hard_header_len) { + if (skb->len < dev->net->hard_header_len && + !(dev->driver_info->flags & FLAG_PARTIAL_RX_PKT)) { state = rx_cleanup; dev->net->stats.rx_errors++; dev->net->stats.rx_length_errors++; diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index e303eef..818da3b 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -117,6 +117,9 @@ struct driver_info { #define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */ #define FLAG_NOARP 0x8000 /* device can't do ARP */ +/* Disables cleanup of small partial rx packets with no hardware header */ +#define FLAG_PARTIAL_RX_PKT 0x10000 + /* init device ... can sleep, or cause probe() failure */ int (*bind)(struct usbnet *, struct usb_interface *); -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/