[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