All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christian Lamparter <chunkeey@web.de>
To: Artur Skawina <art.08.09@gmail.com>
Cc: linux-wireless@vger.kernel.org, Larry Finger <Larry.Finger@lwfinger.net>
Subject: Re: [RFC][PATCH v2] p54usb: rx refill revamp
Date: Sat, 24 Jan 2009 12:06:18 +0100	[thread overview]
Message-ID: <200901241206.18690.chunkeey@web.de> (raw)
In-Reply-To: <497A967F.2010900@gmail.com>

On Saturday 24 January 2009 05:18:07 Artur Skawina wrote:
> Christian Lamparter wrote:
> > 1) urb_poison_anchored_urbs gets called
> > 	1) poison anchor structure
> > 	2) poison & killing every single urb
> > 2) the usb_hcd_giveback_urb is called
> > 	1) >>unanchores<< the urb form anchor_list
> > 	2) calles urb->complete (urb)
> > 		3) p54u_rx_cb -here- but nothing interesting there
> > 3) ... [time goes by]
> > 4) urb_unpoison_anchored_urb is called
> > 	1) unpoison the anchor structure
> > 	2) tries to unpoison the anchored urbs... But there's not a single one in the anchor_list,
> > 	     since step 2.1 (usb_hcd_giveback_urb) killed them off. 
> 
> I assume that's just how it's supposed to be. You could always anchor
> the urbs to another anchor in the completion_. Or free any buffers and
> drop the last ref before leaving the completion. (in fact, the former
> is basically what you're doing, just using a list instead)
true! In fact --- behold the clean up patch which uses another anchor instead of the list ---

> >> I'm curious why you keep the urbs around in the stopped state?
> > well, in most cases the "stopped state" is very short and most wlan-adapters are always connected.
> > So, why throw them away when we need them again in a few seconds?
> > (usually wpa_supplicant/NM has the bad habit of doing a interface up/down dance... sometimes)
> 
> ok. (i don't know about most wlans being always up, but it seems a
> reasonable compromise. still, that's 100k+ wasted ram in the down
> state.)

??? We don't waste 100k+.
We recycle the skbs, so the only thing left is 32 urb structures.

> > well, we don't schedule the workqueue if we canceling the urbs now,
> > ( that's what the urb->status switch is supposed to do/( or in this context )stop...)
> 
> yep, noticed that later, see below.
> 
> > Another maybe related thing: ( a bit above)
> > 	 * In order to prevent a loop, we put the URB
> > 	 * back at the _front_ of the list, so we can
> > 	 * march on, in out-of-memory situations.
> > 
> > I guess this could be true for -EPERM as well?
> > As far as I know list_for_each_entry_* iterates until it hits (head)
> > and since we insert the -EPERM "urb" with list_add (_head),
> > we will never do more than 32 iterations?! (since list_add put the elements in (head)->next )
> > 
> > But if we cancel on -EPERM, we should bail out on -ENODEV
> > (or -ECONNRESET, what ever says that the device is unavailable ) as well...
> 
> I'm not sure I follow.. Ah, the only reason I bailed out on -EPERM
> is that usb_submit_urb() will return -EPERM for poisoned urbs and
> i didn't want to retry this call for every other urb as they would
> all fail. Each try involves a useless skb alloc and free...
> [My version schedules the work for every urb, even the poisoned ones]
well, there's now a hard limit... no change of a endless loop now.
 
> >>> +	if (skb_queue_len(&priv->rx_queue) != 32) {
> >>> +		dev_err(&priv->udev->dev, "Not enough useable transfer buffers "
> >>> +			"available to initialize the device.");
> >>> +		return -ENOMEM;
> >> Why 32 urbs?
> > Well, that's the firmware/hardware limit for all prism54 chips
> > (doesn't matter if usb/pci fullmac/softmac etc...)
> > all have 32 rx and tx slots in the "normal priority" queue/ring-buffer.
> > 
> >> And why should open() fail if, say, only 28 got successfully allocated?
> >> Shouldn't the device function nonetheless?
> > Well, what's the point of supporting a system that has problems finding 32 pages with GFP_KERNEL?
> > you know "one allocation on device init isn't worth avoiding."   :-p
> 
> ok. that's not something this patch changes anyway ;)
> 
> 
> I looked at your v2 briefly yesterday and even wrote a reply, but
> didn't send it. I really liked your v1 much better, the new version
> makes the code much harder to follow, and still can stall the device
> after a few consecutive urb completion or submission (this is new)
> errors happen. Uhm, i probably should shut up now ;)

be prepared to write the reply again ;-)

Yeah, it's always easier to follow your own code, however its sometimes
harder to find bugs, because you assumed you did everything right in the first place...
---
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 9539ddc..64da6cb 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -89,54 +89,102 @@ static void p54u_rx_cb(struct urb *urb)
 
 	skb_unlink(skb, &priv->rx_queue);
 
-	if (unlikely(urb->status)) {
-		dev_kfree_skb_irq(skb);
-		return;
-	}
-
-	skb_put(skb, urb->actual_length);
+	switch (urb->status) {
+	case 0:
+		/*
+		 * The device sent us a packet for processing.
+		 */
+		skb_put(skb, urb->actual_length);
+
+		/*
+		 * remove firmware/device specific headers in
+		 * front of the frame.
+		 */
+		if (priv->hw_type == P54U_NET2280)
+			skb_pull(skb, priv->common.tx_hdr_len);
+		if (priv->common.fw_interface == FW_LM87) {
+			skb_pull(skb, 4);
+			skb_put(skb, 4);
+		}
 
-	if (priv->hw_type == P54U_NET2280)
-		skb_pull(skb, priv->common.tx_hdr_len);
-	if (priv->common.fw_interface == FW_LM87) {
-		skb_pull(skb, 4);
-		skb_put(skb, 4);
-	}
+		if (p54_rx(dev, skb)) {
+			/*
+			 * This skb has been accepted by library and now
+			 * belongs to the mac80211 layer.
+			 * We should put the URB on the refill list, where it
+			 * can be linked to a newly allocated skb later.
+			 * Except, if the workqueue that does the refilling
+			 * can't keep up, we'll try a bit harder and attempt
+			 * to obtain a new skb right here.
+			 */
+
+			if (skb_queue_len(&priv->rx_queue) < 4) {
+				skb = dev_alloc_skb(priv->common.rx_mtu + 32);
+				if (skb) {
+					info = (struct p54u_rx_info *) skb->cb;
+					info->urb = urb;
+					info->dev = dev;
+					/* Update the urb to use the new skb */
+					urb->context = skb;
+					break;
+				}
+			}
 
-	if (p54_rx(dev, skb)) {
-		skb = dev_alloc_skb(priv->common.rx_mtu + 32);
-		if (unlikely(!skb)) {
-			/* TODO check rx queue length and refill *somewhere* */
-			return;
+			usb_anchor_urb(urb, &priv->urb_pool);
+			queue_work(priv->common.hw->workqueue,
+				   &priv->rx_refill_work);
+			return ;
 		}
 
-		info = (struct p54u_rx_info *) skb->cb;
-		info->urb = urb;
-		info->dev = dev;
-		urb->transfer_buffer = skb_tail_pointer(skb);
-		urb->context = skb;
-	} else {
+		/* Reverse all modifications, it must look like new. */
 		if (priv->hw_type == P54U_NET2280)
 			skb_push(skb, priv->common.tx_hdr_len);
 		if (priv->common.fw_interface == FW_LM87) {
 			skb_push(skb, 4);
 			skb_put(skb, 4);
 		}
-		skb_reset_tail_pointer(skb);
-		skb_trim(skb, 0);
-		if (urb->transfer_buffer != skb_tail_pointer(skb)) {
-			/* this should not happen */
-			WARN_ON(1);
-			urb->transfer_buffer = skb_tail_pointer(skb);
-		}
+
+		break;
+
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ENODEV:
+	case -ESHUTDOWN:
+		/*
+		 * The device has been stopped or disconnected.
+		 * Free the skb and put the URBs into the refill_list.
+		 */
+
+		usb_anchor_urb(urb, &priv->urb_pool);
+		dev_kfree_skb_irq(skb);
+		return;
+
+	default:
+		/*
+		 * USB error
+		 * This frame is lost, but we can resubmit URB + skb and
+		 * wait for a successful retry.
+		 */
+		break;
 	}
-	skb_queue_tail(&priv->rx_queue, skb);
+
+	/*
+	 * Reuse the URB and its associated skb.
+	 * Reset all data pointers into their original state and resubmit it.
+	 */
+	skb_reset_tail_pointer(skb);
+	skb_trim(skb, 0);
+	urb->transfer_buffer = skb_tail_pointer(skb);
+
 	usb_anchor_urb(urb, &priv->submitted);
 	if (usb_submit_urb(urb, GFP_ATOMIC)) {
-		skb_unlink(skb, &priv->rx_queue);
 		usb_unanchor_urb(urb);
 		dev_kfree_skb_irq(skb);
-	}
+		usb_anchor_urb(urb, &priv->urb_pool);
+	} else
+		skb_queue_tail(&priv->rx_queue, skb);
+
+	return ;
 }
 
 static void p54u_tx_cb(struct urb *urb)
@@ -150,59 +198,146 @@ static void p54u_tx_cb(struct urb *urb)
 
 static void p54u_tx_dummy_cb(struct urb *urb) { }
 
-static void p54u_free_urbs(struct ieee80211_hw *dev)
+static void p54u_cancel_urbs(struct ieee80211_hw *dev)
 {
 	struct p54u_priv *priv = dev->priv;
-	usb_kill_anchored_urbs(&priv->submitted);
+
+	usb_poison_anchored_urbs(&priv->submitted);
+
+	/*
+	 * By now every RX URB has either finished or been canceled;
+	 * the p54u_rx_cb() completion has placed it on the refill
+	 * list; any attempts to resubmit from p54u_rx_refill(),
+	 * which could still be scheduled to run, will fail.
+	 */
+	cancel_work_sync(&priv->rx_refill_work);
+
+	/*
+	 * Unpoison the anchor itself; the old URBs are already gone,
+	 * p54u_rx_cb() has moved them all to the refill list.
+	 * Prevents new URBs from being poisoned when anchored.
+	 */
+	usb_unpoison_anchored_urbs(&priv->submitted);
+
+	/*
+	 * Unpoison all unused URBs in the pool, in case we want to reuse them.
+	 */
+	usb_unpoison_anchored_urbs(&priv->urb_pool);
 }
 
-static int p54u_init_urbs(struct ieee80211_hw *dev)
+static int p54u_rx_refill(struct ieee80211_hw *dev)
 {
 	struct p54u_priv *priv = dev->priv;
-	struct urb *entry = NULL;
-	struct sk_buff *skb;
-	struct p54u_rx_info *info;
-	int ret = 0;
+	struct urb *entry;
+	unsigned int refilled_urbs = 0, cnt = 0;
+	int err = -EINVAL;
+
+	while (cnt++ != 32 && (entry = usb_get_from_anchor(&priv->urb_pool))) {
+		struct p54u_rx_info *info;
+		struct sk_buff *skb;
 
-	while (skb_queue_len(&priv->rx_queue) < 32) {
 		skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
-		if (!skb) {
-			ret = -ENOMEM;
-			goto err;
-		}
-		entry = usb_alloc_urb(0, GFP_KERNEL);
-		if (!entry) {
-			ret = -ENOMEM;
-			goto err;
+
+		if (unlikely(!skb)) {
+			/*
+			 * In order to prevent a loop, we put the URB
+			 * back at the _front_ of the list, so we can
+			 * march on, in out-of-memory situations.
+			 */
+
+			usb_anchor_urb(entry, &priv->urb_pool);
+			usb_free_urb(entry);
+			err = -ENOMEM;
+			continue;
 		}
 
 		usb_fill_bulk_urb(entry, priv->udev,
 				  usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
 				  skb_tail_pointer(skb),
 				  priv->common.rx_mtu + 32, p54u_rx_cb, skb);
+
 		info = (struct p54u_rx_info *) skb->cb;
 		info->urb = entry;
 		info->dev = dev;
-		skb_queue_tail(&priv->rx_queue, skb);
 
 		usb_anchor_urb(entry, &priv->submitted);
-		ret = usb_submit_urb(entry, GFP_KERNEL);
-		if (ret) {
-			skb_unlink(skb, &priv->rx_queue);
+		err = usb_submit_urb(entry, GFP_KERNEL);
+		if (err) {
+			/*
+			 * URB submission failed.
+			 * Free the associated skb and put the URB back into
+			 * the front of the refill list, so we can try our luck
+			 * next time.
+			 */
+
+			dev_err(&priv->udev->dev, "failed to resubmit %p\n",
+				entry);
+
 			usb_unanchor_urb(entry);
-			goto err;
+			kfree_skb(skb);
+			usb_anchor_urb(entry, &priv->urb_pool);
+		} else {
+			skb_queue_tail(&priv->rx_queue, skb);
+			refilled_urbs++;
 		}
 		usb_free_urb(entry);
-		entry = NULL;
+	}
+
+	return refilled_urbs ? 0 : err;
+}
+
+static int p54u_open(struct ieee80211_hw *dev)
+{
+	struct p54u_priv *priv = dev->priv;
+	int err;
+
+	err = p54u_rx_refill(dev);
+	if (err)
+		return err;
+
+	if (skb_queue_len(&priv->rx_queue) != 32) {
+		dev_err(&priv->udev->dev, "Not enough useable transfer buffers "
+			"available to initialize the device.");
+		p54u_cancel_urbs(dev);
+		return -ENOMEM;
 	}
 
 	return 0;
+}
 
- err:
-	usb_free_urb(entry);
-	kfree_skb(skb);
-	p54u_free_urbs(dev);
-	return ret;
+static void p54u_drain_urb_pool(struct ieee80211_hw *dev)
+{
+	struct p54u_priv *priv = dev->priv;
+	struct urb *entry;
+
+	while ((entry = usb_get_from_anchor(&priv->urb_pool))) {
+		usb_free_urb(entry);
+		usb_free_urb(entry);
+	}
+}
+
+
+static int p54u_init_urbs(struct ieee80211_hw *dev)
+{
+	struct p54u_priv *priv = dev->priv;
+	struct urb *entry;
+	int err = 0;
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		entry = usb_alloc_urb(0, GFP_KERNEL);
+		if (!entry) {
+			err = -ENOMEM;
+			break;
+		}
+
+		usb_anchor_urb(entry, &priv->urb_pool);
+	}
+
+	if (err)
+		p54u_drain_urb_pool(dev);
+
+	return err;
 }
 
 static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb)
@@ -880,28 +1015,12 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
 	return err;
 }
 
-static int p54u_open(struct ieee80211_hw *dev)
+static void p54u_rx_refill_work(struct work_struct *work)
 {
-	struct p54u_priv *priv = dev->priv;
-	int err;
-
-	err = p54u_init_urbs(dev);
-	if (err) {
-		return err;
-	}
-
-	priv->common.open = p54u_init_urbs;
-
-	return 0;
-}
+	struct p54u_priv *priv = container_of(work, struct p54u_priv,
+					      rx_refill_work);
 
-static void p54u_stop(struct ieee80211_hw *dev)
-{
-	/* TODO: figure out how to reliably stop the 3887 and net2280 so
-	   the hardware is still usable next time we want to start it.
-	   until then, we just stop listening to the hardware.. */
-	p54u_free_urbs(dev);
-	return;
+	p54u_rx_refill(priv->common.hw);
 }
 
 static int __devinit p54u_probe(struct usb_interface *intf,
@@ -928,6 +1047,8 @@ static int __devinit p54u_probe(struct usb_interface *intf,
 	priv->intf = intf;
 	skb_queue_head_init(&priv->rx_queue);
 	init_usb_anchor(&priv->submitted);
+	init_usb_anchor(&priv->urb_pool);
+	INIT_WORK(&priv->rx_refill_work, p54u_rx_refill_work);
 
 	usb_get_dev(udev);
 
@@ -949,8 +1070,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
 			recognized_pipes++;
 		}
 	}
-	priv->common.open = p54u_open;
-	priv->common.stop = p54u_stop;
+
 	if (recognized_pipes < P54U_PIPE_NUMBER) {
 		priv->hw_type = P54U_3887;
 		err = p54u_upload_firmware_3887(dev);
@@ -970,12 +1090,19 @@ static int __devinit p54u_probe(struct usb_interface *intf,
 	if (err)
 		goto err_free_dev;
 
-	p54u_open(dev);
+	err = p54u_init_urbs(dev);
+	if (err)
+		goto err_free_dev;
+	err = p54u_open(dev);
+	if (err)
+		goto err_free_dev;
 	err = p54_read_eeprom(dev);
-	p54u_stop(dev);
+	p54u_cancel_urbs(dev);
 	if (err)
 		goto err_free_dev;
 
+	priv->common.open = p54u_open;
+	priv->common.stop = p54u_cancel_urbs;
 	err = ieee80211_register_hw(dev);
 	if (err) {
 		dev_err(&udev->dev, "(p54usb) Cannot register netdevice\n");
@@ -999,9 +1126,12 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
 	if (!dev)
 		return;
 
+	priv = dev->priv;
+
+	p54u_drain_urb_pool(dev);
+
 	ieee80211_unregister_hw(dev);
 
-	priv = dev->priv;
 	usb_put_dev(interface_to_usbdev(intf));
 	p54_free_common(dev);
 	ieee80211_free_hw(dev);
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index 8bc5898..4a5d8b2 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -132,9 +132,10 @@ struct p54u_priv {
 		P54U_3887
 	} hw_type;
 
-	spinlock_t lock;
 	struct sk_buff_head rx_queue;
+	struct work_struct rx_refill_work;
 	struct usb_anchor submitted;
+	struct usb_anchor urb_pool;
 };
 
 #endif /* P54USB_H */

  reply	other threads:[~2009-01-24 11:06 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-01-23 21:45 [RFC][PATCH v2] p54usb: rx refill revamp Christian Lamparter
2009-01-23 22:59 ` Artur Skawina
2009-01-24  1:15   ` Christian Lamparter
2009-01-24  4:18     ` Artur Skawina
2009-01-24 11:06       ` Christian Lamparter [this message]
2009-01-24 19:54         ` Artur Skawina
2009-01-24 20:56           ` Artur Skawina
2009-01-24 21:41             ` Artur Skawina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=200901241206.18690.chunkeey@web.de \
    --to=chunkeey@web.de \
    --cc=Larry.Finger@lwfinger.net \
    --cc=art.08.09@gmail.com \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.