[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87fvnn4c2k.fsf@nemi.mork.no>
Date: Thu, 13 Feb 2014 10:05:39 +0100
From: Bjørn Mork <bjorn@...k.no>
To: Emil Goode <emilgoode@...il.com>
Cc: Steve Glendinning <steve.glendinning@...well.net>,
Oliver Neukum <oneukum@...e.de>,
"David S. Miller" <davem@...emloft.net>,
Freddy Xin <freddy@...x.com.tw>,
Eric Dumazet <edumazet@...gle.com>,
Ming Lei <ming.lei@...onical.com>,
Paul Gortmaker <paul.gortmaker@...driver.com>,
Jeff Kirsher <jeffrey.t.kirsher@...el.com>,
Liu Junliang <liujunliang_ljl@....com>,
Octavian Purdila <octavian.purdila@...el.com>,
linux-usb@...r.kernel.org, netdev@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH] usbnet: remove generic hard_header_len check
Emil Goode <emilgoode@...il.com> writes:
> This patch removes a generic hard_header_len check from the usbnet
> module that is causing dropped packages under certain circumstances
> for devices that send rx packets that cross urb boundaries.
>
> One example is the AX88772B which occasionally send rx packets that
> cross urb boundaries where 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.
>
> With AX88772B 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 the following changes:
> - Removes the generic hard_header_len check in the rx_complete
> function in the usbnet module.
> - Introduces a ETH_HLEN check for skbs that are not cloned from
> within a rx_fixup callback.
> - For safety a hard_header_len check is added to each rx_fixup
> callback function that could be affected by this change.
> These extra checks could possibly be removed by someone
> who has the hardware to test.
>
> The changes place full responsibility on the rx_fixup callback
> functions that clone skbs to only pass valid skbs to the
> usbnet_skb_return function.
>
> Signed-off-by: Emil Goode <emilgoode@...il.com>
> Reported-by: Igor Gnatenko <i.gnatenko.brain@...il.com>
Great work! Looks good to me.
Just a couple of questions:
> diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
> index ff5c871..b2e2aee 100644
> --- a/drivers/net/usb/qmi_wwan.c
> +++ b/drivers/net/usb/qmi_wwan.c
> @@ -80,10 +80,10 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
> {
> __be16 proto;
>
> - /* usbnet rx_complete guarantees that skb->len is at least
> - * hard_header_len, so we can inspect the dest address without
> - * checking skb->len
> - */
> + /* This check is no longer done by usbnet */
> + if (skb->len < dev->net->hard_header_len)
> + return 0;
> +
> switch (skb->data[0] & 0xf0) {
> case 0x40:
> proto = htons(ETH_P_IP);
> diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
> index a48bc0f..524a47a 100644
> --- a/drivers/net/usb/rndis_host.c
> +++ b/drivers/net/usb/rndis_host.c
> @@ -492,6 +492,10 @@ EXPORT_SYMBOL_GPL(rndis_unbind);
> */
> int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
> {
> + /* This check is no longer done by usbnet */
> + if (skb->len < dev->net->hard_header_len)
> + return 0;
> +
Wouldn't it be better to test against ETH_HLEN, since that is a constant
and "obviously correct" in this case?
> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
> index 4671da7..dd10d58 100644
> --- a/drivers/net/usb/usbnet.c
> +++ b/drivers/net/usb/usbnet.c
> @@ -542,17 +542,19 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
> }
> // else network stack removes extra byte if we forced a short packet
>
> - if (skb->len) {
> - /* all data was already cloned from skb inside the driver */
> - if (dev->driver_info->flags & FLAG_MULTI_PACKET)
> - dev_kfree_skb_any(skb);
> - else
> - usbnet_skb_return(dev, skb);
> + /* all data was already cloned from skb inside the driver */
> + if (dev->driver_info->flags & FLAG_MULTI_PACKET)
> + goto done;
> +
> + if (skb->len < ETH_HLEN) {
> + dev->net->stats.rx_errors++;
> + dev->net->stats.rx_length_errors++;
> + netif_dbg(dev, rx_err, dev->net, "rx length %d\n", skb->len);
> + } else {
> + usbnet_skb_return(dev, skb);
> return;
> }
>
> - netif_dbg(dev, rx_err, dev->net, "drop\n");
> - dev->net->stats.rx_errors++;
> done:
> skb_queue_tail(&dev->done, skb);
> }
At first glance I wondered where the dev_kfree_skb_any(skb) went. But
then I realized that you leave that to the common rx_cleanup path, using
the dev->done queue. Is that right? If so, then I guess it could use a
bit of explaining in the commit message?
If I'm wrong, then I'm still looking for the explanation of where the
dev_kfree_skb_any went :-)
Bjørn
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists