diff options
author | Jussi Kivilinna <jussi.kivilinna@mbnet.fi> | 2009-08-11 15:57:16 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-14 09:14:04 -0400 |
commit | 7834ddbcc7a097443761b0722e8c9fb8511b95b1 (patch) | |
tree | f764502e46a6a5db52dcec7b961238161848e9b6 /drivers | |
parent | d4de9532fd0b50d486259ace17650a58bbb751c2 (diff) |
usbnet: add rx queue pausing
Add rx queue pausing to usbnet. This is needed by rndis_wlan so that it can
control rx queue and prevent received packets from being send forward before
rndis_wlan receives and handles 'media connect'-indication. Without this
establishing WPA connections is hard and fail often.
[v2] - removed unneeded use of skb_clone
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/usb/usbnet.c | 44 | ||||
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 13 |
2 files changed, 55 insertions, 2 deletions
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index af1fe4696509..7d471fca2743 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c | |||
@@ -233,6 +233,11 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) | |||
233 | { | 233 | { |
234 | int status; | 234 | int status; |
235 | 235 | ||
236 | if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { | ||
237 | skb_queue_tail(&dev->rxq_pause, skb); | ||
238 | return; | ||
239 | } | ||
240 | |||
236 | skb->protocol = eth_type_trans (skb, dev->net); | 241 | skb->protocol = eth_type_trans (skb, dev->net); |
237 | dev->net->stats.rx_packets++; | 242 | dev->net->stats.rx_packets++; |
238 | dev->net->stats.rx_bytes += skb->len; | 243 | dev->net->stats.rx_bytes += skb->len; |
@@ -526,6 +531,41 @@ static void intr_complete (struct urb *urb) | |||
526 | } | 531 | } |
527 | 532 | ||
528 | /*-------------------------------------------------------------------------*/ | 533 | /*-------------------------------------------------------------------------*/ |
534 | void usbnet_pause_rx(struct usbnet *dev) | ||
535 | { | ||
536 | set_bit(EVENT_RX_PAUSED, &dev->flags); | ||
537 | |||
538 | if (netif_msg_rx_status(dev)) | ||
539 | devdbg(dev, "paused rx queue enabled"); | ||
540 | } | ||
541 | EXPORT_SYMBOL_GPL(usbnet_pause_rx); | ||
542 | |||
543 | void usbnet_resume_rx(struct usbnet *dev) | ||
544 | { | ||
545 | struct sk_buff *skb; | ||
546 | int num = 0; | ||
547 | |||
548 | clear_bit(EVENT_RX_PAUSED, &dev->flags); | ||
549 | |||
550 | while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { | ||
551 | usbnet_skb_return(dev, skb); | ||
552 | num++; | ||
553 | } | ||
554 | |||
555 | tasklet_schedule(&dev->bh); | ||
556 | |||
557 | if (netif_msg_rx_status(dev)) | ||
558 | devdbg(dev, "paused rx queue disabled, %d skbs requeued", num); | ||
559 | } | ||
560 | EXPORT_SYMBOL_GPL(usbnet_resume_rx); | ||
561 | |||
562 | void usbnet_purge_paused_rxq(struct usbnet *dev) | ||
563 | { | ||
564 | skb_queue_purge(&dev->rxq_pause); | ||
565 | } | ||
566 | EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq); | ||
567 | |||
568 | /*-------------------------------------------------------------------------*/ | ||
529 | 569 | ||
530 | // unlink pending rx/tx; completion handlers do all other cleanup | 570 | // unlink pending rx/tx; completion handlers do all other cleanup |
531 | 571 | ||
@@ -623,6 +663,8 @@ int usbnet_stop (struct net_device *net) | |||
623 | 663 | ||
624 | usb_kill_urb(dev->interrupt); | 664 | usb_kill_urb(dev->interrupt); |
625 | 665 | ||
666 | usbnet_purge_paused_rxq(dev); | ||
667 | |||
626 | /* deferred work (task, timer, softirq) must also stop. | 668 | /* deferred work (task, timer, softirq) must also stop. |
627 | * can't flush_scheduled_work() until we drop rtnl (later), | 669 | * can't flush_scheduled_work() until we drop rtnl (later), |
628 | * else workers could deadlock; so make workers a NOP. | 670 | * else workers could deadlock; so make workers a NOP. |
@@ -1113,7 +1155,6 @@ static void usbnet_bh (unsigned long param) | |||
1113 | } | 1155 | } |
1114 | 1156 | ||
1115 | 1157 | ||
1116 | |||
1117 | /*------------------------------------------------------------------------- | 1158 | /*------------------------------------------------------------------------- |
1118 | * | 1159 | * |
1119 | * USB Device Driver support | 1160 | * USB Device Driver support |
@@ -1210,6 +1251,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) | |||
1210 | skb_queue_head_init (&dev->rxq); | 1251 | skb_queue_head_init (&dev->rxq); |
1211 | skb_queue_head_init (&dev->txq); | 1252 | skb_queue_head_init (&dev->txq); |
1212 | skb_queue_head_init (&dev->done); | 1253 | skb_queue_head_init (&dev->done); |
1254 | skb_queue_head_init(&dev->rxq_pause); | ||
1213 | dev->bh.func = usbnet_bh; | 1255 | dev->bh.func = usbnet_bh; |
1214 | dev->bh.data = (unsigned long) dev; | 1256 | dev->bh.data = (unsigned long) dev; |
1215 | INIT_WORK (&dev->kevent, kevent); | 1257 | INIT_WORK (&dev->kevent, kevent); |
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 828dc1825bba..d42692dfbc67 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c | |||
@@ -1764,8 +1764,15 @@ static int rndis_iw_set_essid(struct net_device *dev, | |||
1764 | 1764 | ||
1765 | if (!wrqu->essid.flags || length == 0) | 1765 | if (!wrqu->essid.flags || length == 0) |
1766 | return disassociate(usbdev, 1); | 1766 | return disassociate(usbdev, 1); |
1767 | else | 1767 | else { |
1768 | /* Pause and purge rx queue, so we don't pass packets before | ||
1769 | * 'media connect'-indication. | ||
1770 | */ | ||
1771 | usbnet_pause_rx(usbdev); | ||
1772 | usbnet_purge_paused_rxq(usbdev); | ||
1773 | |||
1768 | return set_essid(usbdev, &ssid); | 1774 | return set_essid(usbdev, &ssid); |
1775 | } | ||
1769 | } | 1776 | } |
1770 | 1777 | ||
1771 | 1778 | ||
@@ -2328,6 +2335,8 @@ get_bssid: | |||
2328 | memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN); | 2335 | memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN); |
2329 | wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); | 2336 | wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); |
2330 | } | 2337 | } |
2338 | |||
2339 | usbnet_resume_rx(usbdev); | ||
2331 | } | 2340 | } |
2332 | 2341 | ||
2333 | if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) { | 2342 | if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) { |
@@ -2541,6 +2550,8 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) | |||
2541 | 2550 | ||
2542 | switch (msg->status) { | 2551 | switch (msg->status) { |
2543 | case RNDIS_STATUS_MEDIA_CONNECT: | 2552 | case RNDIS_STATUS_MEDIA_CONNECT: |
2553 | usbnet_pause_rx(usbdev); | ||
2554 | |||
2544 | devinfo(usbdev, "media connect"); | 2555 | devinfo(usbdev, "media connect"); |
2545 | 2556 | ||
2546 | /* queue work to avoid recursive calls into rndis_command */ | 2557 | /* queue work to avoid recursive calls into rndis_command */ |