Skip to content

Commit

Permalink
usbnet: add rx queue pausing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jussi Kivilinna authored and John W. Linville committed Aug 14, 2009
1 parent d4de953 commit 7834ddb
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 2 deletions.
44 changes: 43 additions & 1 deletion drivers/net/usb/usbnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -525,6 +530,41 @@ static void intr_complete (struct urb *urb)
deverr(dev, "intr resubmit --> %d", status);
}

/*-------------------------------------------------------------------------*/
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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1113,7 +1155,6 @@ static void usbnet_bh (unsigned long param)
}



/*-------------------------------------------------------------------------
*
* USB Device Driver support
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 12 additions & 1 deletion drivers/net/wireless/rndis_wlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}


Expand Down Expand Up @@ -2328,6 +2335,8 @@ static void rndis_wlan_worker(struct work_struct *work)
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)) {
Expand Down Expand Up @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions include/linux/usb/usbnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 7834ddb

Please sign in to comment.