[Spice-devel] [PATCH usbredir 3/8] Add a timer to make sure that urb dequeue requests go through, one way or another.

Jeremy White jwhite at codeweavers.com
Wed Dec 9 14:16:03 PST 2015


Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
 kernel/device.c   | 42 ++++++++++++++++++++++++++++++++++++++++++
 kernel/urb.c      | 16 ++++++++++++++++
 kernel/usbredir.h |  8 ++++++++
 3 files changed, 66 insertions(+)

diff --git a/kernel/device.c b/kernel/device.c
index e39e264..f8d1f83 100644
--- a/kernel/device.c
+++ b/kernel/device.c
@@ -21,6 +21,42 @@
 
 #include "usbredir.h"
 
+
+static void device_timer(unsigned long arg)
+{
+	struct usbredir_device *udev = (struct usbredir_device *) arg;
+	struct usbredir_unlink *unlink, *tmp;
+	unsigned long j = 0;
+	int dequeue = -1;
+	pr_debug("%ld(%d): device_timer\n", jiffies, udev->rhport);
+
+	if (! atomic_read(&udev->active))
+		return;
+
+	spin_lock(&udev->lock);
+
+	/* Dequeue at most one per invocation, so we can unlock */
+	list_for_each_entry_safe(unlink, tmp, &udev->unlink_rx, list) {
+		if (dequeue == -1 && time_after_eq(jiffies, unlink->expires)) {
+			dequeue = unlink->unlink_seqnum;
+			list_del(&unlink->list);
+			kfree(unlink);
+		}
+		else if (j == 0 || time_after(j, unlink->expires)) {
+			j = unlink->expires;
+		}
+	}
+
+	if (j)
+		mod_timer(&udev->timer, j);
+	spin_unlock(&udev->lock);
+
+	if (dequeue != -1) {
+		usbredir_cancel_urb(udev, dequeue);
+	}
+}
+
+
 void usbredir_device_init(struct usbredir_device *udev, int port,
 			  struct usbredir_hub *hub)
 {
@@ -37,6 +73,10 @@ void usbredir_device_init(struct usbredir_device *udev, int port,
 	INIT_LIST_HEAD(&udev->unlink_rx);
 
 	init_waitqueue_head(&udev->waitq_tx);
+
+	init_timer(&udev->timer);
+	udev->timer.data = (unsigned long) udev;
+	udev->timer.function = device_timer;
 }
 
 void usbredir_device_allocate(struct usbredir_device *udev,
@@ -93,6 +133,8 @@ void usbredir_device_deallocate(struct usbredir_device *udev,
 		return;
 	}
 
+	del_timer(&udev->timer);
+
 	/* Release the rx thread */
 	if (udev->socket)
 		kernel_sock_shutdown(udev->socket, SHUT_RDWR);
diff --git a/kernel/urb.c b/kernel/urb.c
index a7566fc..2f98d63 100644
--- a/kernel/urb.c
+++ b/kernel/urb.c
@@ -169,6 +169,8 @@ static void usbredir_free_uurb(struct usbredir_device *udev, struct urb *urb)
 	}
 }
 
+/* TODO - find justification for a timeout value; 250ms is pulled from air*/
+#define DEQUEUE_TIMEOUT		((250 * HZ) / 1000)
 int usbredir_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 {
 	struct usbredir_urb *uurb;
@@ -216,9 +218,13 @@ int usbredir_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 		unlink->seqnum = usbredir_hub_seqnum(hub);
 		unlink->unlink_seqnum = uurb->seqnum;
+		unlink->expires = jiffies + DEQUEUE_TIMEOUT;
 
 		/* TODO - are we failing to pass through the status here? */
 		spin_lock(&udev->lock);
+		if (! timer_pending(&udev->timer))
+			mod_timer(&udev->timer, unlink->expires);
+
 		list_add_tail(&unlink->list, &udev->unlink_tx);
 		spin_unlock(&udev->lock);
 
@@ -280,3 +286,13 @@ struct urb *usbredir_pop_rx_urb(struct usbredir_device *udev, int seqnum)
 
 	return urb;
 }
+
+void usbredir_cancel_urb(struct usbredir_device *udev, int seqnum)
+{
+	struct urb *urb = usbredir_pop_rx_urb(udev, seqnum);
+	if (urb) {
+		usb_hcd_unlink_urb_from_ep(udev->hub->hcd, urb);
+		usb_hcd_giveback_urb(udev->hub->hcd, urb, urb->status);
+	}
+}
+
diff --git a/kernel/usbredir.h b/kernel/usbredir.h
index 3263b5d..f1be9e5 100644
--- a/kernel/usbredir.h
+++ b/kernel/usbredir.h
@@ -13,6 +13,7 @@
 #define __USBREDIR_H
 
 #include <linux/device.h>
+#include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/platform_device.h>
 #include <linux/usb.h>
@@ -49,6 +50,8 @@
  * @unlink_tx		A list of urb's to be send to be unlinked
  * @unlink_xx		A list of urb's we have requested cancellation of
  * @waitq_tx		Wait queue the transmit thread sleeps on
+ *
+ * @timer               A timer to clear stale URBs
  */
 struct usbredir_device {
 	spinlock_t lock;
@@ -84,6 +87,8 @@ struct usbredir_device {
 	struct list_head unlink_rx;
 
 	wait_queue_head_t waitq_tx;
+
+	struct timer_list timer;
 };
 
 /**
@@ -139,6 +144,7 @@ struct usbredir_urb {
  * @seqnum		Sequence number of this request
  * @list		Place holder to keep it in device/unlink_[rt]x
  * @unlink_seqnum	Sequence number of the urb to unlink
+ * @expires		When we should forcibly terminate this urb
  */
 struct usbredir_unlink {
 	int seqnum;
@@ -146,6 +152,7 @@ struct usbredir_unlink {
 	struct list_head list;
 
 	int unlink_seqnum;
+	unsigned long expires;
 };
 
 
@@ -208,6 +215,7 @@ int usbredir_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
 int usbredir_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
 struct urb *usbredir_pop_rx_urb(struct usbredir_device *udev, int seqnum);
 void usbredir_urb_cleanup_urblists(struct usbredir_device *udev);
+void usbredir_cancel_urb(struct usbredir_device *udev, int seqnum);
 
 /* Fast lookup functions */
 static inline struct usbredir_hub *usbredir_hub_from_hcd(struct usb_hcd *hcd)
-- 
2.1.4



More information about the Spice-devel mailing list