Commit b076e706 authored by Max Vozeler's avatar Max Vozeler Committed by Greg Kroah-Hartman

staging: usbip: vhci: give back URBs from in-flight unlink requests

commit b92a5e23 upstream.

If we never received a RET_UNLINK because the TCP
connection broke the pending URBs still need to be
unlinked and given back.

Previously processes would be stuck trying to kill
the URB even after the device was detached.
Signed-off-by: default avatarMax Vozeler <max@vozeler.com>
Tested-by: default avatarMark Wehby <MWehby@luxotticaRetail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 96ea4801
...@@ -119,6 +119,9 @@ void rh_port_disconnect(int rhport); ...@@ -119,6 +119,9 @@ void rh_port_disconnect(int rhport);
void vhci_rx_loop(struct usbip_task *ut); void vhci_rx_loop(struct usbip_task *ut);
void vhci_tx_loop(struct usbip_task *ut); void vhci_tx_loop(struct usbip_task *ut);
struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
__u32 seqnum);
#define hardware (&the_controller->pdev.dev) #define hardware (&the_controller->pdev.dev)
static inline struct vhci_device *port_to_vdev(__u32 port) static inline struct vhci_device *port_to_vdev(__u32 port)
......
...@@ -807,7 +807,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ...@@ -807,7 +807,6 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
return 0; return 0;
} }
static void vhci_device_unlink_cleanup(struct vhci_device *vdev) static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
{ {
struct vhci_unlink *unlink, *tmp; struct vhci_unlink *unlink, *tmp;
...@@ -815,11 +814,34 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) ...@@ -815,11 +814,34 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
spin_lock(&vdev->priv_lock); spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
usbip_uinfo("unlink cleanup tx %lu\n", unlink->unlink_seqnum);
list_del(&unlink->list); list_del(&unlink->list);
kfree(unlink); kfree(unlink);
} }
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
struct urb *urb;
/* give back URB of unanswered unlink request */
usbip_uinfo("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
if (!urb) {
usbip_uinfo("the urb (seqnum %lu) was already given back\n",
unlink->unlink_seqnum);
list_del(&unlink->list);
kfree(unlink);
continue;
}
urb->status = -ENODEV;
spin_lock(&the_controller->lock);
usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
spin_unlock(&the_controller->lock);
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
list_del(&unlink->list); list_del(&unlink->list);
kfree(unlink); kfree(unlink);
} }
......
...@@ -21,16 +21,14 @@ ...@@ -21,16 +21,14 @@
#include "vhci.h" #include "vhci.h"
/* get URB from transmitted urb queue */ /* get URB from transmitted urb queue. caller must hold vdev->priv_lock */
static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
__u32 seqnum) __u32 seqnum)
{ {
struct vhci_priv *priv, *tmp; struct vhci_priv *priv, *tmp;
struct urb *urb = NULL; struct urb *urb = NULL;
int status; int status;
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) {
if (priv->seqnum == seqnum) { if (priv->seqnum == seqnum) {
urb = priv->urb; urb = priv->urb;
...@@ -61,8 +59,6 @@ static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, ...@@ -61,8 +59,6 @@ static struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev,
} }
} }
spin_unlock(&vdev->priv_lock);
return urb; return urb;
} }
...@@ -72,9 +68,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, ...@@ -72,9 +68,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
struct usbip_device *ud = &vdev->ud; struct usbip_device *ud = &vdev->ud;
struct urb *urb; struct urb *urb;
spin_lock(&vdev->priv_lock);
urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum);
spin_unlock(&vdev->priv_lock);
if (!urb) { if (!urb) {
usbip_uerr("cannot find a urb of seqnum %u\n", usbip_uerr("cannot find a urb of seqnum %u\n",
...@@ -159,7 +157,12 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, ...@@ -159,7 +157,12 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
return; return;
} }
spin_lock(&vdev->priv_lock);
urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum);
spin_unlock(&vdev->priv_lock);
if (!urb) { if (!urb) {
/* /*
* I get the result of a unlink request. But, it seems that I * I get the result of a unlink request. But, it seems that I
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment