[PATCH 5/9] xhci-ring.c: Add the poll_pend state to properly abort transactions

Bin Meng bmeng.cn at gmail.com
Thu Aug 20 10:26:28 CEST 2020


Hi Jason,

On Sat, Jul 25, 2020 at 5:51 AM Jason Wessel <jason.wessel at windriver.com> wrote:
>
> xhci_trl_tx and xhchi_bulk_tx can be called synchronously by other
> drivers such as the usb storage or network, while the keyboard driver
> exclusively uses the polling mode.
>

Could you provide more details as to when this will fail?

> And pending polling transactions must be aborted before switching
> modes to avoid corrupting the state of the controller.
>
> Signed-off-by: Jason Wessel <jason.wessel at windriver.com>
> ---
>  drivers/usb/host/xhci-ring.c | 86 +++++++++++++++++++++++++++++-------
>  1 file changed, 70 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index b7b2e16410..d6339f4f0a 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -24,6 +24,12 @@
>
>  #include <usb/xhci.h>
>
> +static void *last_bulk_tx_buf;
> +static struct usb_device *poll_last_udev;
> +int poll_last_ep_index;
> +static unsigned long bulk_tx_poll_ts;
> +static bool poll_pend;

Should these variables go into struct xhci_ctrl? What happens if 2
controllers are sending data?

> +
>  /**
>   * Is this TRB a link TRB or was the last TRB the last TRB in this event ring
>   * segment?  I.e. would the updated event TRB pointer step off the end of the
> @@ -549,19 +555,8 @@ static void record_transfer_result(struct usb_device *udev,
>         }
>  }
>
> -/**** Bulk and Control transfer methods ****/
> -/**
> - * Queues up the BULK Request
> - *
> - * @param udev         pointer to the USB device structure
> - * @param pipe         contains the DIR_IN or OUT , devnum
> - * @param length       length of the buffer
> - * @param buffer       buffer to be read/written based on the request
> - * @param nonblock     when true do not block waiting for response
> - * @return returns 0 if successful else -1 on failure
> - */
> -int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
> -                       int length, void *buffer, bool nonblock)
> +static int _xhci_bulk_tx_queue(struct usb_device *udev, unsigned long pipe,
> +                              int length, void *buffer)
>  {
>         int num_trbs = 0;
>         struct xhci_generic_trb *start_trb;
> @@ -575,7 +570,6 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
>         struct xhci_virt_device *virt_dev;
>         struct xhci_ep_ctx *ep_ctx;
>         struct xhci_ring *ring;         /* EP transfer ring */
> -       union xhci_trb *event;
>
>         int running_total, trb_buff_len;
>         unsigned int total_packet_count;
> @@ -719,20 +713,73 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
>         } while (running_total < length);
>
>         giveback_first_trb(udev, ep_index, start_cycle, start_trb);
> +       return 0;
> +}
>
> +/**** Bulk and Control transfer methods ****/
> +/**
> + * Queues up the BULK Request
> + *
> + * @param udev         pointer to the USB device structure
> + * @param pipe         contains the DIR_IN or OUT , devnum
> + * @param length       length of the buffer
> + * @param buffer       buffer to be read/written based on the request
> + * @param nonblock     when true do not block waiting for response
> + * @return returns 0 if successful else -1 on failure
> + */
> +int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
> +                int length, void *buffer, bool nonblock)
> +{
> +       u32 field;
> +       int ret;
> +       union xhci_trb *event;
> +       struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
> +       int ep_index = usb_pipe_ep_index(pipe);
> +
> +       if (poll_pend) {
> +               /*
> +                * Abort a pending poll operation if it should have
> +                * timed out, or if this is a different buffer from a
> +                * separate request
> +                */
> +               if (get_timer(bulk_tx_poll_ts) > XHCI_TIMEOUT ||
> +                   last_bulk_tx_buf != buffer || poll_last_udev != udev ||
> +                   ep_index != poll_last_ep_index) {
> +                       abort_td(poll_last_udev, poll_last_ep_index);
> +                       poll_last_udev->status = USB_ST_NAK_REC;  /* closest thing to a timeout */
> +                       poll_last_udev->act_len = 0;
> +                       poll_pend = false;
> +               }
> +       } /* No else here because poll_pend might have changed above */
> +       if (!poll_pend) {
> +               last_bulk_tx_buf = buffer;
> +               ret = _xhci_bulk_tx_queue(udev, pipe, length, buffer);
> +               if (ret)
> +                       return ret;
> +       }
>         event = xhci_wait_for_event(ctrl, TRB_TRANSFER, nonblock);
>         if (!event) {
> -               if (nonblock)
> +               if (nonblock) {
> +                       if (!poll_pend) {
> +                               /* Start the timer */
> +                               bulk_tx_poll_ts = get_timer(0);
> +                               poll_last_udev = udev;
> +                               poll_last_ep_index = ep_index;
> +                               poll_pend = true;
> +                       }
>                         return -EINVAL;
> +               }
>                 debug("XHCI bulk transfer timed out, aborting...\n");
>                 abort_td(udev, ep_index);
>                 udev->status = USB_ST_NAK_REC;  /* closest thing to a timeout */
>                 udev->act_len = 0;
> +               poll_pend = false;
>                 return -ETIMEDOUT;
>         }
> +       poll_pend = false;
>         field = le32_to_cpu(event->trans_event.flags);
>
> -       BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
> +       BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
>         BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
>         BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) -
>                 buffer > (size_t)length);
> @@ -779,6 +826,13 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
>                 le16_to_cpu(req->value), le16_to_cpu(req->value),
>                 le16_to_cpu(req->index));
>
> +       if (poll_pend) {
> +               abort_td(poll_last_udev, poll_last_ep_index);
> +               poll_last_udev->status = USB_ST_NAK_REC;  /* closest thing to a timeout */
> +               poll_last_udev->act_len = 0;
> +               poll_pend = false;
> +       }
> +
>         ep_index = usb_pipe_ep_index(pipe);
>
>         ep_ring = virt_dev->eps[ep_index].ring;
> --

Regards,
Bin


More information about the U-Boot mailing list