Hi,

Mathias Nyman <mathias.ny...@linux.intel.com> writes:
> On 17.04.2018 10:07, Felipe Balbi wrote:
>> 
>> Hi,
>> 
>> Mathias Nyman <mathias.ny...@linux.intel.com> 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

Attachment: signature.asc
Description: PGP signature

Reply via email to