Re: usb: free DMA if enqueue fails

Pete Zaitcev <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Red Hat, Inc.
Message-ID <[email protected]>
Release DMA resources if submission fails in the HCD.

Signed-Off-By: Pete Zaitcev <[email protected]>

---

> Amazing indeed.

Just for the record, I didn't find it just by looking. IBM filed a bug
RH#236922 after they ran out of IOMMU. Our (older) uhci-hcd was rejecting
bulk requests sent to interrupt endpoints, so running their management
application on the GA kernel depletes sw-iommu.

> Do you think your new routine should be merged with urb_unlink()?   The 
> two always get called together.

Probably a good idea. How about now?

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e277258..3d657ba 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -901,17 +901,32 @@ EXPORT_SYMBOL (usb_calc_bus_time);
 
 /*-------------------------------------------------------------------------*/
 
-static void urb_unlink (struct urb *urb)
+static void urb_unlink(struct usb_hcd *hcd, struct urb *urb)
 {
 	unsigned long		flags;
+	int at_root_hub = (urb->dev == hcd->self.root_hub);
 
 	/* clear all state linking urb to this dev (and hcd) */
-
 	spin_lock_irqsave (&hcd_data_lock, flags);
 	list_del_init (&urb->urb_list);
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
-}
 
+	if (hcd->self.uses_dma && !at_root_hub) {
+		if (usb_pipecontrol (urb->pipe)
+			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
+			dma_unmap_single (hcd->self.controller, urb->setup_dma,
+					sizeof (struct usb_ctrlrequest),
+					DMA_TO_DEVICE);
+		if (urb->transfer_buffer_length != 0
+			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
+			dma_unmap_single (hcd->self.controller, 
+					urb->transfer_dma,
+					urb->transfer_buffer_length,
+					usb_pipein (urb->pipe)
+					    ? DMA_FROM_DEVICE
+					    : DMA_TO_DEVICE);
+	}
+}
 
 /* may be called in any context with a valid urb->dev usecount
  * caller surrenders "ownership" of urb
@@ -1014,7 +1029,7 @@ doit:
 	status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags);
 done:
 	if (unlikely (status)) {
-		urb_unlink (urb);
+		urb_unlink(hcd, urb);
 		atomic_dec (&urb->use_count);
 		if (urb->reject)
 			wake_up (&usb_kill_urb_queue);
@@ -1384,29 +1399,7 @@ EXPORT_SYMBOL (usb_bus_start_enum);
  */
 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
 {
-	int at_root_hub;
-
-	at_root_hub = (urb->dev == hcd->self.root_hub);
-	urb_unlink (urb);
-
-	/* lower level hcd code should use *_dma exclusively if the
-	 * host controller does DMA */
-	if (hcd->self.uses_dma && !at_root_hub) {
-		if (usb_pipecontrol (urb->pipe)
-			&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
-			dma_unmap_single (hcd->self.controller, urb->setup_dma,
-					sizeof (struct usb_ctrlrequest),
-					DMA_TO_DEVICE);
-		if (urb->transfer_buffer_length != 0
-			&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
-			dma_unmap_single (hcd->self.controller, 
-					urb->transfer_dma,
-					urb->transfer_buffer_length,
-					usb_pipein (urb->pipe)
-					    ? DMA_FROM_DEVICE
-					    : DMA_TO_DEVICE);
-	}
-
+	urb_unlink(hcd, urb);
 	usbmon_urb_complete (&hcd->self, urb);
 	/* pass ownership to the completion handler */
 	urb->complete (urb);

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.