USB: whci-hcd: handle early deletion of endpoints
If an endpoint is deleted before it's been fully added to the hardware list, the associated qset will not be fully initialized and an oops will occur when complete(&qset->remove_complete) is called. This can happen if a queued URB is cancelled. Fix this by only removing the qset from the hardware list if the cancelled URB had qTDs. Signed-off-by: David Vrabel <david.vrabel@csr.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
b41ecf9a80
commit
171b37ee95
2 changed files with 25 additions and 14 deletions
|
@ -305,6 +305,7 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
|
|||
struct whc_urb *wurb = urb->hcpriv;
|
||||
struct whc_qset *qset = wurb->qset;
|
||||
struct whc_std *std, *t;
|
||||
bool has_qtd = false;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -315,17 +316,21 @@ int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
|
|||
goto out;
|
||||
|
||||
list_for_each_entry_safe(std, t, &qset->stds, list_node) {
|
||||
if (std->urb == urb)
|
||||
if (std->urb == urb) {
|
||||
if (std->qtd)
|
||||
has_qtd = true;
|
||||
qset_free_std(whc, std);
|
||||
else
|
||||
} else
|
||||
std->qtd = NULL; /* so this std is re-added when the qset is */
|
||||
}
|
||||
|
||||
asl_qset_remove(whc, qset);
|
||||
wurb->status = status;
|
||||
wurb->is_async = true;
|
||||
queue_work(whc->workqueue, &wurb->dequeue_work);
|
||||
|
||||
if (has_qtd) {
|
||||
asl_qset_remove(whc, qset);
|
||||
wurb->status = status;
|
||||
wurb->is_async = true;
|
||||
queue_work(whc->workqueue, &wurb->dequeue_work);
|
||||
} else
|
||||
qset_remove_urb(whc, qset, urb, status);
|
||||
out:
|
||||
spin_unlock_irqrestore(&whc->lock, flags);
|
||||
|
||||
|
|
|
@ -333,6 +333,7 @@ int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
|
|||
struct whc_urb *wurb = urb->hcpriv;
|
||||
struct whc_qset *qset = wurb->qset;
|
||||
struct whc_std *std, *t;
|
||||
bool has_qtd = false;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -343,17 +344,22 @@ int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
|
|||
goto out;
|
||||
|
||||
list_for_each_entry_safe(std, t, &qset->stds, list_node) {
|
||||
if (std->urb == urb)
|
||||
if (std->urb == urb) {
|
||||
if (std->qtd)
|
||||
has_qtd = true;
|
||||
qset_free_std(whc, std);
|
||||
else
|
||||
} else
|
||||
std->qtd = NULL; /* so this std is re-added when the qset is */
|
||||
}
|
||||
|
||||
pzl_qset_remove(whc, qset);
|
||||
wurb->status = status;
|
||||
wurb->is_async = false;
|
||||
queue_work(whc->workqueue, &wurb->dequeue_work);
|
||||
|
||||
if (has_qtd) {
|
||||
pzl_qset_remove(whc, qset);
|
||||
update_pzl_hw_view(whc);
|
||||
wurb->status = status;
|
||||
wurb->is_async = false;
|
||||
queue_work(whc->workqueue, &wurb->dequeue_work);
|
||||
} else
|
||||
qset_remove_urb(whc, qset, urb, status);
|
||||
out:
|
||||
spin_unlock_irqrestore(&whc->lock, flags);
|
||||
|
||||
|
|
Reference in a new issue