Commit 2beb6488 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

[PATCH] USB kaweth: fix races between timeout, xmit and disconnect

USB kaweth driver update

 - always set the ASYNC_UNLINK flag for the tx urb
 - correct cancelling the tx urb under all circumstances
 - detect a running disconnect method in xmit path
 - fix a potential deadlock by using GFP_NOIO
parent aeb48803
......@@ -5,6 +5,7 @@
* (c) 2000 Interlan Communications
* (c) 2000 Stephane Alnet
* (C) 2001 Brad Hards
* (C) 2002 Oliver Neukum
*
* Original author: The Zapman <zapman@interlan.net>
* Inspired by, and much credit goes to Michael Rothwell
......@@ -200,10 +201,12 @@ struct kaweth_device
spinlock_t device_lock;
__u32 status;
int end;
int removed;
struct usb_device *dev;
struct net_device *net;
wait_queue_head_t control_wait;
wait_queue_head_t term_wait;
struct urb *rx_urb;
struct urb *tx_urb;
......@@ -490,6 +493,14 @@ static void kaweth_usb_receive(struct urb *urb)
return;
}
if(unlikely(urb->status == -ECONNRESET || urb->status == -ECONNABORTED))
/* we are killed - set a flag and wake the disconnect handler */
{
kaweth->end = 1;
wake_up(&kaweth->term_wait);
return;
}
if(urb->status && urb->status != -EREMOTEIO && count != 1) {
kaweth_err("%s RX status: %d count: %d packet_len: %d",
net->name,
......@@ -607,6 +618,13 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
spin_lock(&kaweth->device_lock);
if (kaweth->removed) {
/* our device is undergoing disconnection - we bail out */
spin_unlock(&kaweth->device_lock);
dev_kfree_skb(skb);
return 0;
}
kaweth_async_set_rx_mode(kaweth);
netif_stop_queue(net);
......@@ -623,6 +641,8 @@ static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
count + 2,
kaweth_usb_transmit_complete,
kaweth);
kaweth->end = 0;
kaweth->tx_urb->transfer_flags |= USB_ASYNC_UNLINK;
if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
{
......@@ -757,6 +777,7 @@ static void *kaweth_probe(
kaweth->dev = dev;
spin_lock_init(&kaweth->device_lock);
init_waitqueue_head(&kaweth->term_wait);
kaweth_dbg("Resetting.");
......@@ -923,9 +944,19 @@ static void kaweth_disconnect(struct usb_device *dev, void *ptr)
kaweth_warn("unregistering non-existant device");
return;
}
usb_unlink_urb(kaweth->tx_urb);
kaweth->removed = 1;
usb_unlink_urb(kaweth->rx_urb);
/* we need to wait for the urb to be cancelled, if it is active */
spin_lock(&kaweth->device_lock);
if (usb_unlink_urb(kaweth->tx_urb) == -EINPROGRESS) {
spin_unlock(&kaweth->device_lock);
wait_event(kaweth->term_wait, kaweth->end);
} else {
spin_unlock(&kaweth->device_lock);
}
if(kaweth->net) {
if(kaweth->net->flags & IFF_UP) {
kaweth_dbg("Closing net device");
......@@ -1021,7 +1052,7 @@ int kaweth_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
int retv;
int length;
urb = usb_alloc_urb(0, GFP_KERNEL);
urb = usb_alloc_urb(0, GFP_NOIO);
if (!urb)
return -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment