Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7834ddbc authored by Jussi Kivilinna's avatar Jussi Kivilinna Committed by John W. Linville
Browse files

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: default avatarJussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent d4de9532
Loading
Loading
Loading
Loading
+43 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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);
+12 −1
Original line number Diff line number Diff line
@@ -1764,9 +1764,16 @@ 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);
	}
}


static int rndis_iw_get_essid(struct net_device *dev,
@@ -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 */
+6 −0
Original line number Diff line number Diff line
@@ -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);