Hi, Mathias Nyman <[email protected]> writes: > On 17.04.2018 10:07, Felipe Balbi wrote: >> >> Hi, >> >> Mathias Nyman <[email protected]> writes: >>> On 16.04.2018 15:29, Felipe Balbi wrote: >>>> Instead of allocating urb priv and, maybe, bail out due to xhci being >>>> in DYING state, we can move the check earlier and avoid the memory >>>> allocation altogether. >>> >>> This also moves checking for DYING outside the lock. >>> >>> Most cases set DYING with lock held, so if we first get the lock before >>> checking DYING we have a better chance of not being in the process of dying. >> >> pretty sure that's atomic, though. > > That's not what I'm after, your fix is cleaning up code in the case where > DYING flag is > set before xhci_urb_enqueue() is called. I'm worried about the case when > setting DYING flag races > with xhci_urb_enqueue(). i.e. xhci_urb_enqueue() is spinning on the lock, > waiting, while > some other part of the driver is desperately trying to access hw with lock > held, failing, > finally setting DYING flag, and then releasing lock. > > If the check is done before taking the lock then the URB might be queued on > dying device, > at a time when xhci_hc_died already started cancelling and giving back all > queued URB
this can only happen if checking that bit isn't an atomic operation
which, AFAICT, it is. IOW, it would be the same if you were to change:
if (a & b)
return -1;
to:
if (test_bit(b, &a))
return -1;
right? Now, if this isn't an atomic operation, I'm happy to be educated.
>>> Small thing, but so is this cleanup, so not sure its worth the change
>>
>> Look at the result. With this change we don't need to take a lock,
>> allocate memory, search for endpoint index, search for endpoint
>> state. All of those are needed for proper operation of the function, but
>> if the controller has already died, there's no point in going any
>> further.
>
> But we might miss the fact that host died, and go even further, adding URB to
> list,
> writing TRBs to ringbuffers etc.
>
> In code we save one line,
> goto: free_priv
We're saving a lot more than that, actually. All of the following ends
up being skipped. All of these are unnecessary work when xHC has already
died:
8<------------------------------------------------------------------------
slot_id = urb->dev->slot_id;
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state;
if (!HCD_HW_ACCESSIBLE(hcd)) {
if (!in_interrupt())
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
return -ESHUTDOWN;
}
if (usb_endpoint_xfer_isoc(&urb->ep->desc))
num_tds = urb->number_of_packets;
else if (usb_endpoint_is_bulk_out(&urb->ep->desc) &&
urb->transfer_buffer_length > 0 &&
urb->transfer_flags & URB_ZERO_PACKET &&
!(urb->transfer_buffer_length % usb_endpoint_maxp(&urb->ep->desc)))
num_tds = 2;
else
num_tds = 1;
urb_priv = kzalloc(sizeof(struct urb_priv) +
num_tds * sizeof(struct xhci_td), mem_flags);
if (!urb_priv)
return -ENOMEM;
urb_priv->num_tds = num_tds;
urb_priv->num_tds_done = 0;
urb->hcpriv = urb_priv;
trace_xhci_urb_enqueue(urb);
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
/* Check to see if the max packet size for the default control
* endpoint changed during FS device enumeration
*/
if (urb->dev->speed == USB_SPEED_FULL) {
ret = xhci_check_maxpacket(xhci, slot_id,
ep_index, urb);
if (ret < 0) {
xhci_urb_free_priv(urb_priv);
urb->hcpriv = NULL;
return ret;
}
}
}
spin_lock_irqsave(&xhci->lock, flags);
8<------------------------------------------------------------------------
--
balbi
signature.asc
Description: PGP signature
