usbnet: add rx queue pausing
Subsystems (5)
PATCH v2 1/1 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 <[email protected]>
Signed-off-by Jussi Kivilinna <[email protected]>
---
drivers/net/usb/usbnet.c | 44 ++++++++++++++++++++++++++++++++++++- drivers/net/wireless/rndis_wlan.c | 13 ++++++++++- include/linux/usb/usbnet.h | 6 +++++ 3 files changed, 61 insertions(+), 2 deletions(-)
drivers/net/usb/usbnet.c +43 -1
@@ -233,6 +233,11 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) {int status; +if (test_bit (EVENT_RX_PAUSED , &dev->flags)) { +skb_queue_tail (&dev->rxq_pause, skb); +return ; + } + skb->protocol =eth_type_trans (skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len;
@@ -526,6 +531,41 @@ static void intr_complete (struct urb *urb) }/*-------------------------------------------------------------------------*/ +void usbnet_pause_rx (struct usbnet *dev) +{ +set_bit (EVENT_RX_PAUSED , &dev->flags); + +if (netif_msg_rx_status (dev)) +devdbg (dev,"paused rx queue enabled" ); +} +EXPORT_SYMBOL_GPL (usbnet_pause_rx); + +void usbnet_resume_rx (struct usbnet *dev) +{ +struct sk_buff *skb; +int num =0 ; + +clear_bit (EVENT_RX_PAUSED , &dev->flags); + +while ((skb =skb_dequeue (&dev->rxq_pause)) !=NULL ) { +usbnet_skb_return (dev, skb); + num++; + } + +tasklet_schedule (&dev->bh); + +if (netif_msg_rx_status (dev)) +devdbg (dev,"paused rx queue disabled, %d skbs requeued" , num); +} +EXPORT_SYMBOL_GPL (usbnet_resume_rx); + +void usbnet_purge_paused_rxq (struct usbnet *dev) +{ +skb_queue_purge (&dev->rxq_pause); +} +EXPORT_SYMBOL_GPL (usbnet_purge_paused_rxq); + +/*-------------------------------------------------------------------------*/ // unlink pending rx/tx; completion handlers do all other cleanup
@@ -623,6 +663,8 @@ int usbnet_stop (struct net_device *net)usb_kill_urb (dev->interrupt); +usbnet_purge_paused_rxq (dev); +/* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP.
@@ -1113,7 +1155,6 @@ static void usbnet_bh (unsigned long param) } -/*------------------------------------------------------------------------- * * USB Device Driver support
@@ -1210,6 +1251,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)skb_queue_head_init (&dev->rxq);skb_queue_head_init (&dev->txq);skb_queue_head_init (&dev->done); +skb_queue_head_init (&dev->rxq_pause); dev->bh.func = usbnet_bh; dev->bh.data = (unsigned long ) dev;INIT_WORK (&dev->kevent, kevent);
drivers/net/wireless/rndis_wlan.c +12 -1
@@ -1764,8 +1764,15 @@ static int rndis_iw_set_essid(struct net_device *dev,if (!wrqu->essid.flags || length ==0 )return disassociate (usbdev,1 ); -else +else { +/* Pause and purge rx queue, so we don't pass packets before +* 'media connect'-indication. +*/ +usbnet_pause_rx (usbdev); +usbnet_purge_paused_rxq (usbdev); +return set_essid (usbdev, &ssid); + } }
@@ -2328,6 +2335,8 @@ get_bssid:memcpy (evt.ap_addr.sa_data, bssid,ETH_ALEN );wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); } + +usbnet_resume_rx (usbdev); }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)switch (msg->status) {case RNDIS_STATUS_MEDIA_CONNECT : +usbnet_pause_rx (usbdev); +devinfo (usbdev,"media connect" );/* queue work to avoid recursive calls into rndis_command */
include/linux/usb/usbnet.h +6 -0
@@ -53,6 +53,7 @@ struct usbnet {struct sk_buff_head rxq;struct sk_buff_head txq;struct sk_buff_head done; +struct sk_buff_head rxq_pause;struct urb *interrupt;struct tasklet_struct bh;
@@ -63,6 +64,7 @@ struct usbnet {# define EVENT_RX_MEMORY 2# define EVENT_STS_SPLIT 3# define EVENT_LINK_RESET 4 +# define EVENT_RX_PAUSED 5 };static inline struct usb_driver *driver_of (struct usb_interface *intf)
@@ -190,6 +192,10 @@ extern void usbnet_defer_kevent (struct usbnet *, int);extern void usbnet_skb_return (struct usbnet *,struct sk_buff *);extern void usbnet_unlink_rx_urbs (struct usbnet *); +extern void usbnet_pause_rx (struct usbnet *); +extern void usbnet_resume_rx (struct usbnet *); +extern void usbnet_purge_paused_rxq (struct usbnet *); +extern int usbnet_get_settings (struct net_device *net,struct ethtool_cmd *cmd);extern int usbnet_set_settings (struct net_device *net,struct ethtool_cmd *cmd);extern u32 usbnet_get_link (struct net_device *net);