* usbnet: fix misc bugs
@ 2012-04-30 8:51 Ming Lei
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-05-03 0:14 ` usbnet: fix misc bugs David Miller
0 siblings, 2 replies; 14+ messages in thread
From: Ming Lei @ 2012-04-30 8:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman; +Cc: netdev, linux-usb
These patches fix three bugs in usbnet:
usbnet: fix leak of transfer buffer of dev->interrupt
usbnet: fix failure handling in usbnet_probe
usbnet: fix skb traversing races during unlink(v1)
drivers/net/usb/usbnet.c | 58 ++++++++++++++++++++++++++++++++------------
include/linux/usb/usbnet.h | 3 ++-
2 files changed, 44 insertions(+), 17 deletions(-)
Thanks,
--
Ming Lei
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2012-04-30 8:51 ` Ming Lei
[not found] ` <1335775864-4873-2-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-30 8:51 ` [PATCH 2/3] usbnet: fix failure handling in usbnet_probe Ming Lei
2012-04-30 8:51 ` [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1) Ming Lei
2 siblings, 1 reply; 14+ messages in thread
From: Ming Lei @ 2012-04-30 8:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
Ming Lei
The transfer buffer of dev->interrupt is allocated in .probe path,
but not freed in .disconnet path, so mark the interrupt URB as
URB_FREE_BUFFER to free the buffer when the URB is destroyed.
Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
drivers/net/usb/usbnet.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index db99536..5b38a3a 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
} else {
usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
buf, maxp, intr_complete, dev, period);
+ dev->interrupt->transfer_flags |= URB_FREE_BUFFER;
dev_dbg(&intf->dev,
"status ep%din, %d bytes period %d\n",
usb_pipeendpoint(pipe), maxp, period);
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/3] usbnet: fix failure handling in usbnet_probe
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-30 8:51 ` [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt Ming Lei
@ 2012-04-30 8:51 ` Ming Lei
2012-05-03 0:13 ` David Miller
2012-04-30 8:51 ` [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1) Ming Lei
2 siblings, 1 reply; 14+ messages in thread
From: Ming Lei @ 2012-04-30 8:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
Ming Lei
If register_netdev returns failure, the dev->interrupt and
its transfer buffer should be released, so just fix it.
Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
drivers/net/usb/usbnet.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 5b38a3a..80b837c 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1445,7 +1445,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
status = register_netdev (net);
if (status)
- goto out3;
+ goto out4;
netif_info(dev, probe, dev->net,
"register '%s' at usb-%s-%s, %s, %pM\n",
udev->dev.driver->name,
@@ -1463,6 +1463,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
return 0;
+out4:
+ usb_free_urb(dev->interrupt);
out3:
if (info->unbind)
info->unbind (dev, udev);
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-30 8:51 ` [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt Ming Lei
2012-04-30 8:51 ` [PATCH 2/3] usbnet: fix failure handling in usbnet_probe Ming Lei
@ 2012-04-30 8:51 ` Ming Lei
[not found] ` <1335775864-4873-4-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2 siblings, 1 reply; 14+ messages in thread
From: Ming Lei @ 2012-04-30 8:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
Ming Lei, Huajun Li, Oliver Neukum, stable-DgEjT+Ai2ygdnm+yROfE0A
Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid
recursive locking in usbnet_stop()) fixes the recursive locking
problem by releasing the skb queue lock before unlink, but may
cause skb traversing races:
- after URB is unlinked and the queue lock is released,
the refered skb and skb->next may be moved to done queue,
even be released
- in skb_queue_walk_safe, the next skb is still obtained
by next pointer of the last skb
- so maybe trigger oops or other problems
This patch extends the usage of entry->state to describe 'start_unlink'
state, so always holding the queue(rx/tx) lock to change the state if
the referd skb is in rx or tx queue because we need to know if the
refered urb has been started unlinking in unlink_urbs.
Also the patch uses usb_block_urb introduced by Oliver to block
URB resubmit in complete handler if the URB will be unlinked.
The other part of this patch is based on Huajun's patch:
always traverse from head of the tx/rx queue to get skb which is
to be unlinked but not been started unlinking.
Signed-off-by: Huajun Li <huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org>
Cc: stable-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
---
v1:
- fix comment style and avoiding to expose the internal
details of SKB lists, as suggested by David Miller
This patch depends on the usb_block_urb/usb_unblock_urb patch from
Oliver.
drivers/net/usb/usbnet.c | 53 +++++++++++++++++++++++++++++++-------------
include/linux/usb/usbnet.h | 3 ++-
2 files changed, 40 insertions(+), 16 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 80b837c..2013001 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -282,17 +282,30 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
}
EXPORT_SYMBOL_GPL(usbnet_change_mtu);
+/* The caller must hold list->lock */
+static void __usbnet_queue_skb(struct sk_buff_head *list,
+ struct sk_buff *newsk, enum skb_state state)
+{
+ struct skb_data *entry = (struct skb_data *) newsk->cb;
+
+ __skb_queue_tail(list, newsk);
+ entry->state = state;
+}
+
/*-------------------------------------------------------------------------*/
/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
* completion callbacks. 2.5 should have fixed those bugs...
*/
-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+static void defer_bh(struct usbnet *dev, struct sk_buff *skb,
+ struct sk_buff_head *list, enum skb_state state)
{
unsigned long flags;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
spin_lock_irqsave(&list->lock, flags);
+ entry->state = state;
__skb_unlink(skb, list);
spin_unlock(&list->lock);
spin_lock(&dev->done.lock);
@@ -340,7 +353,6 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = rx_start;
entry->length = 0;
usb_fill_bulk_urb (urb, dev->udev, dev->in,
@@ -372,7 +384,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
tasklet_schedule (&dev->bh);
break;
case 0:
- __skb_queue_tail (&dev->rxq, skb);
+ __usbnet_queue_skb(&dev->rxq, skb, rx_start);
}
} else {
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -423,16 +435,17 @@ static void rx_complete (struct urb *urb)
struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev;
int urb_status = urb->status;
+ enum skb_state state;
skb_put (skb, urb->actual_length);
- entry->state = rx_done;
+ state = rx_done;
entry->urb = NULL;
switch (urb_status) {
/* success */
case 0:
if (skb->len < dev->net->hard_header_len) {
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
netif_dbg(dev, rx_err, dev->net,
@@ -471,7 +484,7 @@ static void rx_complete (struct urb *urb)
"rx throttle %d\n", urb_status);
}
block:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
entry->urb = urb;
urb = NULL;
break;
@@ -482,13 +495,13 @@ block:
// FALLTHROUGH
default:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
break;
}
- defer_bh(dev, skb, &dev->rxq);
+ defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
if (netif_running (dev->net) &&
@@ -579,16 +592,23 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
unsigned long flags;
- struct sk_buff *skb, *skbnext;
+ struct sk_buff *skb;
int count = 0;
spin_lock_irqsave (&q->lock, flags);
- skb_queue_walk_safe(q, skb, skbnext) {
+ while (!skb_queue_empty(q)) {
struct skb_data *entry;
struct urb *urb;
int retval;
- entry = (struct skb_data *) skb->cb;
+ skb_queue_walk(q, skb) {
+ entry = (struct skb_data *) skb->cb;
+ if (entry->state != unlink_start)
+ goto found;
+ }
+ break;
+found:
+ entry->state = unlink_start;
urb = entry->urb;
/*
@@ -599,6 +619,10 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
* handler(include defer_bh).
*/
usb_get_urb(urb);
+
+ /* speedup unlink by blocking resubmit */
+ usb_block_urb(urb);
+
spin_unlock_irqrestore(&q->lock, flags);
// during some PM-driven resume scenarios,
// these (async) unlinks complete immediately
@@ -607,6 +631,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
else
count++;
+ usb_unblock_urb(urb);
usb_put_urb(urb);
spin_lock_irqsave(&q->lock, flags);
}
@@ -1040,8 +1065,7 @@ static void tx_complete (struct urb *urb)
}
usb_autopm_put_interface_async(dev->intf);
- entry->state = tx_done;
- defer_bh(dev, skb, &dev->txq);
+ defer_bh(dev, skb, &dev->txq, tx_done);
}
/*-------------------------------------------------------------------------*/
@@ -1097,7 +1121,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = tx_start;
entry->length = length;
usb_fill_bulk_urb (urb, dev->udev, dev->out,
@@ -1156,7 +1179,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
break;
case 0:
net->trans_start = jiffies;
- __skb_queue_tail (&dev->txq, skb);
+ __usbnet_queue_skb(&dev->txq, skb, tx_start);
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 605b0aa..76f4396 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -191,7 +191,8 @@ extern void usbnet_cdc_status(struct usbnet *, struct urb *);
enum skb_state {
illegal = 0,
tx_start, tx_done,
- rx_start, rx_done, rx_cleanup
+ rx_start, rx_done, rx_cleanup,
+ unlink_start
};
struct skb_data { /* skb->cb is one of these */
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt
[not found] ` <1335775864-4873-2-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2012-04-30 8:58 ` Oliver Neukum
2012-05-03 0:13 ` David Miller
0 siblings, 1 reply; 14+ messages in thread
From: Oliver Neukum @ 2012-04-30 8:58 UTC (permalink / raw)
To: Ming Lei
Cc: David S. Miller, Greg Kroah-Hartman,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
Am Montag, 30. April 2012, 10:51:02 schrieb Ming Lei:
> The transfer buffer of dev->interrupt is allocated in .probe path,
> but not freed in .disconnet path, so mark the interrupt URB as
> URB_FREE_BUFFER to free the buffer when the URB is destroyed.
>
> Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Acked-by: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
[not found] ` <1335775864-4873-4-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2012-05-03 0:12 ` David Miller
[not found] ` <20120502.201212.909292764269133334.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
0 siblings, 1 reply; 14+ messages in thread
From: David Miller @ 2012-05-03 0:12 UTC (permalink / raw)
To: tom.leiming-Re5JQEeQqe8AvxtiuMwx3w
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w, oneukum-l3A5Bk7waGM,
stable-DgEjT+Ai2ygdnm+yROfE0A
From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date: Mon, 30 Apr 2012 16:51:04 +0800
> This patch depends on the usb_block_urb/usb_unblock_urb patch from
> Oliver.
Therefore I cannot apply it to any of my trees.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt
2012-04-30 8:58 ` Oliver Neukum
@ 2012-05-03 0:13 ` David Miller
0 siblings, 0 replies; 14+ messages in thread
From: David Miller @ 2012-05-03 0:13 UTC (permalink / raw)
To: oneukum; +Cc: tom.leiming, gregkh, netdev, linux-usb
From: Oliver Neukum <oneukum@suse.de>
Date: Mon, 30 Apr 2012 10:58:14 +0200
> Am Montag, 30. April 2012, 10:51:02 schrieb Ming Lei:
>> The transfer buffer of dev->interrupt is allocated in .probe path,
>> but not freed in .disconnet path, so mark the interrupt URB as
>> URB_FREE_BUFFER to free the buffer when the URB is destroyed.
>>
>> Signed-off-by: Ming Lei <tom.leiming@gmail.com>
> Acked-by: Oliver Neukum <oneukum@suse.de>
Applied.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/3] usbnet: fix failure handling in usbnet_probe
2012-04-30 8:51 ` [PATCH 2/3] usbnet: fix failure handling in usbnet_probe Ming Lei
@ 2012-05-03 0:13 ` David Miller
0 siblings, 0 replies; 14+ messages in thread
From: David Miller @ 2012-05-03 0:13 UTC (permalink / raw)
To: tom.leiming; +Cc: gregkh, netdev, linux-usb
From: Ming Lei <tom.leiming@gmail.com>
Date: Mon, 30 Apr 2012 16:51:03 +0800
> If register_netdev returns failure, the dev->interrupt and
> its transfer buffer should be released, so just fix it.
>
> Signed-off-by: Ming Lei <tom.leiming@gmail.com>
Applied.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: usbnet: fix misc bugs
2012-04-30 8:51 usbnet: fix misc bugs Ming Lei
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2012-05-03 0:14 ` David Miller
[not found] ` <20120502.201444.2074945018843883757.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
1 sibling, 1 reply; 14+ messages in thread
From: David Miller @ 2012-05-03 0:14 UTC (permalink / raw)
To: tom.leiming; +Cc: gregkh, netdev, linux-usb
From: Ming Lei <tom.leiming@gmail.com>
Date: Mon, 30 Apr 2012 16:51:01 +0800
> These patches fix three bugs in usbnet:
> usbnet: fix leak of transfer buffer of dev->interrupt
> usbnet: fix failure handling in usbnet_probe
> usbnet: fix skb traversing races during unlink(v1)
The third fix looks quite critical, but depends upon an interface
not present in Linus's tree, nor mine. That doesn't work.
You need to find a way to propagate this fix into Linus's tree
as well as all -stable trees that need that fix, but without
this unmet dependency.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: usbnet: fix misc bugs
[not found] ` <20120502.201444.2074945018843883757.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2012-05-03 0:34 ` Ming Lei
0 siblings, 0 replies; 14+ messages in thread
From: Ming Lei @ 2012-05-03 0:34 UTC (permalink / raw)
To: David Miller
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA
On Thu, May 3, 2012 at 8:14 AM, David Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org> wrote:
> From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Date: Mon, 30 Apr 2012 16:51:01 +0800
>
>> These patches fix three bugs in usbnet:
>> usbnet: fix leak of transfer buffer of dev->interrupt
>> usbnet: fix failure handling in usbnet_probe
>> usbnet: fix skb traversing races during unlink(v1)
>
> The third fix looks quite critical, but depends upon an interface
> not present in Linus's tree, nor mine. That doesn't work.
>
> You need to find a way to propagate this fix into Linus's tree
> as well as all -stable trees that need that fix, but without
> this unmet dependency.
OK, usb_block_urb/usb_unlock_urb is not a must to fix the
problem, which is added to speed up the unlink. I will submit
a new version without them.
Thanks,
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
[not found] ` <20120502.201212.909292764269133334.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2012-05-03 1:04 ` Ming Lei
2012-05-09 23:47 ` Ming Lei
2012-05-15 17:42 ` David Miller
0 siblings, 2 replies; 14+ messages in thread
From: Ming Lei @ 2012-05-03 1:04 UTC (permalink / raw)
To: David Miller
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w, oneukum-l3A5Bk7waGM,
stable-DgEjT+Ai2ygdnm+yROfE0A
>From a87ff961f0a5d50223bd084dfac4fe5ce84f3913 Mon Sep 17 00:00:00 2001
From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date: Thu, 26 Apr 2012 11:33:46 +0800
Subject: [PATCH] usbnet: fix skb traversing races during unlink(v2)
Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid
recursive locking in usbnet_stop()) fixes the recursive locking
problem by releasing the skb queue lock before unlink, but may
cause skb traversing races:
- after URB is unlinked and the queue lock is released,
the refered skb and skb->next may be moved to done queue,
even be released
- in skb_queue_walk_safe, the next skb is still obtained
by next pointer of the last skb
- so maybe trigger oops or other problems
This patch extends the usage of entry->state to describe 'start_unlink'
state, so always holding the queue(rx/tx) lock to change the state if
the referd skb is in rx or tx queue because we need to know if the
refered urb has been started unlinking in unlink_urbs.
The other part of this patch is based on Huajun's patch:
always traverse from head of the tx/rx queue to get skb which is
to be unlinked but not been started unlinking.
Signed-off-by: Huajun Li <huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org>
Cc: stable-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
---
v2:
- if the Rx URB has been marked as being unlink, just not resubmit it
in complete handler, so usb_block_urb/usb_unblock_urb can be avoided,
then make backporting for -stable easier.
v1:
- fix comment style and avoiding to expose the internal
details of SKB lists, as suggested by David Miller
drivers/net/usb/usbnet.c | 54 +++++++++++++++++++++++++++++++-------------
include/linux/usb/usbnet.h | 3 ++-
2 files changed, 40 insertions(+), 17 deletions(-)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 80b837c..9f58330 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -282,17 +282,32 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
}
EXPORT_SYMBOL_GPL(usbnet_change_mtu);
+/* The caller must hold list->lock */
+static void __usbnet_queue_skb(struct sk_buff_head *list,
+ struct sk_buff *newsk, enum skb_state state)
+{
+ struct skb_data *entry = (struct skb_data *) newsk->cb;
+
+ __skb_queue_tail(list, newsk);
+ entry->state = state;
+}
+
/*-------------------------------------------------------------------------*/
/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
* completion callbacks. 2.5 should have fixed those bugs...
*/
-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+ struct sk_buff_head *list, enum skb_state state)
{
unsigned long flags;
+ enum skb_state old_state;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
spin_lock_irqsave(&list->lock, flags);
+ old_state = entry->state;
+ entry->state = state;
__skb_unlink(skb, list);
spin_unlock(&list->lock);
spin_lock(&dev->done.lock);
@@ -300,6 +315,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea
if (dev->done.qlen == 1)
tasklet_schedule(&dev->bh);
spin_unlock_irqrestore(&dev->done.lock, flags);
+ return old_state;
}
/* some work can't be done in tasklets, so we use keventd
@@ -340,7 +356,6 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = rx_start;
entry->length = 0;
usb_fill_bulk_urb (urb, dev->udev, dev->in,
@@ -372,7 +387,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
tasklet_schedule (&dev->bh);
break;
case 0:
- __skb_queue_tail (&dev->rxq, skb);
+ __usbnet_queue_skb(&dev->rxq, skb, rx_start);
}
} else {
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -423,16 +438,17 @@ static void rx_complete (struct urb *urb)
struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev;
int urb_status = urb->status;
+ enum skb_state state;
skb_put (skb, urb->actual_length);
- entry->state = rx_done;
+ state = rx_done;
entry->urb = NULL;
switch (urb_status) {
/* success */
case 0:
if (skb->len < dev->net->hard_header_len) {
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
netif_dbg(dev, rx_err, dev->net,
@@ -471,7 +487,7 @@ static void rx_complete (struct urb *urb)
"rx throttle %d\n", urb_status);
}
block:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
entry->urb = urb;
urb = NULL;
break;
@@ -482,17 +498,18 @@ block:
// FALLTHROUGH
default:
- entry->state = rx_cleanup;
+ state = rx_cleanup;
dev->net->stats.rx_errors++;
netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
break;
}
- defer_bh(dev, skb, &dev->rxq);
+ state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
if (netif_running (dev->net) &&
- !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ !test_bit (EVENT_RX_HALT, &dev->flags) &&
+ state != unlink_start) {
rx_submit (dev, urb, GFP_ATOMIC);
usb_mark_last_busy(dev->udev);
return;
@@ -579,16 +596,23 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
unsigned long flags;
- struct sk_buff *skb, *skbnext;
+ struct sk_buff *skb;
int count = 0;
spin_lock_irqsave (&q->lock, flags);
- skb_queue_walk_safe(q, skb, skbnext) {
+ while (!skb_queue_empty(q)) {
struct skb_data *entry;
struct urb *urb;
int retval;
- entry = (struct skb_data *) skb->cb;
+ skb_queue_walk(q, skb) {
+ entry = (struct skb_data *) skb->cb;
+ if (entry->state != unlink_start)
+ goto found;
+ }
+ break;
+found:
+ entry->state = unlink_start;
urb = entry->urb;
/*
@@ -1040,8 +1064,7 @@ static void tx_complete (struct urb *urb)
}
usb_autopm_put_interface_async(dev->intf);
- entry->state = tx_done;
- defer_bh(dev, skb, &dev->txq);
+ (void) defer_bh(dev, skb, &dev->txq, tx_done);
}
/*-------------------------------------------------------------------------*/
@@ -1097,7 +1120,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
entry = (struct skb_data *) skb->cb;
entry->urb = urb;
entry->dev = dev;
- entry->state = tx_start;
entry->length = length;
usb_fill_bulk_urb (urb, dev->udev, dev->out,
@@ -1156,7 +1178,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
break;
case 0:
net->trans_start = jiffies;
- __skb_queue_tail (&dev->txq, skb);
+ __usbnet_queue_skb(&dev->txq, skb, tx_start);
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 605b0aa..76f4396 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -191,7 +191,8 @@ extern void usbnet_cdc_status(struct usbnet *, struct urb *);
enum skb_state {
illegal = 0,
tx_start, tx_done,
- rx_start, rx_done, rx_cleanup
+ rx_start, rx_done, rx_cleanup,
+ unlink_start
};
struct skb_data { /* skb->cb is one of these */
--
1.7.9.5
Thanks,
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
2012-05-03 1:04 ` Ming Lei
@ 2012-05-09 23:47 ` Ming Lei
[not found] ` <CACVXFVNYFSSP2+MFXAqT60E0qOp8+hjU3u0SbYoWHA_a5ouaxw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-05-15 17:42 ` David Miller
1 sibling, 1 reply; 14+ messages in thread
From: Ming Lei @ 2012-05-09 23:47 UTC (permalink / raw)
To: David Miller
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w, oneukum-l3A5Bk7waGM,
stable-DgEjT+Ai2ygdnm+yROfE0A
Hi David,
On Thu, May 3, 2012 at 9:04 AM, Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> From a87ff961f0a5d50223bd084dfac4fe5ce84f3913 Mon Sep 17 00:00:00 2001
> From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Date: Thu, 26 Apr 2012 11:33:46 +0800
> Subject: [PATCH] usbnet: fix skb traversing races during unlink(v2)
>
> Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid
> recursive locking in usbnet_stop()) fixes the recursive locking
> problem by releasing the skb queue lock before unlink, but may
> cause skb traversing races:
> - after URB is unlinked and the queue lock is released,
> the refered skb and skb->next may be moved to done queue,
> even be released
> - in skb_queue_walk_safe, the next skb is still obtained
> by next pointer of the last skb
> - so maybe trigger oops or other problems
>
> This patch extends the usage of entry->state to describe 'start_unlink'
> state, so always holding the queue(rx/tx) lock to change the state if
> the referd skb is in rx or tx queue because we need to know if the
> refered urb has been started unlinking in unlink_urbs.
>
> The other part of this patch is based on Huajun's patch:
> always traverse from head of the tx/rx queue to get skb which is
> to be unlinked but not been started unlinking.
>
> Signed-off-by: Huajun Li <huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Oliver Neukum <oneukum-l3A5Bk7waGM@public.gmane.org>
> Cc: stable-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org
> ---
> v2:
> - if the Rx URB has been marked as being unlink, just not resubmit it
> in complete handler, so usb_block_urb/usb_unblock_urb can be avoided,
Considered that this one(v2) doesn't depend on usb tree any more and looks
no one objects it, could you apply this one on your tree?
Thanks,
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
[not found] ` <CACVXFVNYFSSP2+MFXAqT60E0qOp8+hjU3u0SbYoWHA_a5ouaxw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2012-05-14 22:34 ` David Miller
0 siblings, 0 replies; 14+ messages in thread
From: David Miller @ 2012-05-14 22:34 UTC (permalink / raw)
To: tom.leiming-Re5JQEeQqe8AvxtiuMwx3w
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w, oneukum-l3A5Bk7waGM,
stable-DgEjT+Ai2ygdnm+yROfE0A
From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date: Thu, 10 May 2012 07:47:47 +0800
> Considered that this one(v2) doesn't depend on usb tree any more and looks
> no one objects it, could you apply this one on your tree?
I will, thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1)
2012-05-03 1:04 ` Ming Lei
2012-05-09 23:47 ` Ming Lei
@ 2012-05-15 17:42 ` David Miller
1 sibling, 0 replies; 14+ messages in thread
From: David Miller @ 2012-05-15 17:42 UTC (permalink / raw)
To: tom.leiming-Re5JQEeQqe8AvxtiuMwx3w
Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w, oneukum-l3A5Bk7waGM,
stable-DgEjT+Ai2ygdnm+yROfE0A
From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Date: Thu, 3 May 2012 09:04:50 +0800
>>From a87ff961f0a5d50223bd084dfac4fe5ce84f3913 Mon Sep 17 00:00:00 2001
> From: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Date: Thu, 26 Apr 2012 11:33:46 +0800
> Subject: [PATCH] usbnet: fix skb traversing races during unlink(v2)
>
> Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid
> recursive locking in usbnet_stop()) fixes the recursive locking
> problem by releasing the skb queue lock before unlink, but may
> cause skb traversing races:
> - after URB is unlinked and the queue lock is released,
> the refered skb and skb->next may be moved to done queue,
> even be released
> - in skb_queue_walk_safe, the next skb is still obtained
> by next pointer of the last skb
> - so maybe trigger oops or other problems
>
> This patch extends the usage of entry->state to describe 'start_unlink'
> state, so always holding the queue(rx/tx) lock to change the state if
> the referd skb is in rx or tx queue because we need to know if the
> refered urb has been started unlinking in unlink_urbs.
>
> The other part of this patch is based on Huajun's patch:
> always traverse from head of the tx/rx queue to get skb which is
> to be unlinked but not been started unlinking.
>
> Signed-off-by: Huajun Li <huajun.li.lee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Ming Lei <tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Applied.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2012-05-15 17:42 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-04-30 8:51 usbnet: fix misc bugs Ming Lei
[not found] ` <1335775864-4873-1-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-30 8:51 ` [PATCH 1/3] usbnet: fix leak of transfer buffer of dev->interrupt Ming Lei
[not found] ` <1335775864-4873-2-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-04-30 8:58 ` Oliver Neukum
2012-05-03 0:13 ` David Miller
2012-04-30 8:51 ` [PATCH 2/3] usbnet: fix failure handling in usbnet_probe Ming Lei
2012-05-03 0:13 ` David Miller
2012-04-30 8:51 ` [PATCH 3/3] usbnet: fix skb traversing races during unlink(v1) Ming Lei
[not found] ` <1335775864-4873-4-git-send-email-tom.leiming-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-05-03 0:12 ` David Miller
[not found] ` <20120502.201212.909292764269133334.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2012-05-03 1:04 ` Ming Lei
2012-05-09 23:47 ` Ming Lei
[not found] ` <CACVXFVNYFSSP2+MFXAqT60E0qOp8+hjU3u0SbYoWHA_a5ouaxw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2012-05-14 22:34 ` David Miller
2012-05-15 17:42 ` David Miller
2012-05-03 0:14 ` usbnet: fix misc bugs David Miller
[not found] ` <20120502.201444.2074945018843883757.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2012-05-03 0:34 ` Ming Lei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).