Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx> --- 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel