Re: [RFT PATCH] xhci: rework cycle bit checking for new dequeue pointers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jul 25, 2014 at 8:47 AM, Mathias Nyman
<mathias.nyman@xxxxxxxxxxxxxxx> wrote:
> Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>
> ---
>  drivers/usb/host/xhci-ring.c | 95 +++++++++++++++-----------------------------
>  1 file changed, 31 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index 749fc68..35da18c 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -364,32 +364,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
>         }
>  }
>
> -/*
> - * Find the segment that trb is in.  Start searching in start_seg.
> - * If we must move past a segment that has a link TRB with a toggle cycle state
> - * bit set, then we will toggle the value pointed at by cycle_state.
> - */
> -static struct xhci_segment *find_trb_seg(
> -               struct xhci_segment *start_seg,
> -               union xhci_trb  *trb, int *cycle_state)
> -{
> -       struct xhci_segment *cur_seg = start_seg;
> -       struct xhci_generic_trb *generic_trb;
> -
> -       while (cur_seg->trbs > trb ||
> -                       &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) {
> -               generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic;
> -               if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE))
> -                       *cycle_state ^= 0x1;
> -               cur_seg = cur_seg->next;
> -               if (cur_seg == start_seg)
> -                       /* Looped over the entire list.  Oops! */
> -                       return NULL;
> -       }
> -       return cur_seg;
> -}
> -
> -
>  static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
>                 unsigned int slot_id, unsigned int ep_index,
>                 unsigned int stream_id)
> @@ -459,9 +433,11 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
>         struct xhci_virt_device *dev = xhci->devs[slot_id];
>         struct xhci_virt_ep *ep = &dev->eps[ep_index];
>         struct xhci_ring *ep_ring;
> -       struct xhci_generic_trb *trb;
> +       struct xhci_segment *new_seg;
> +       union xhci_trb *new_deq, *tmp_trb;
>         dma_addr_t addr;
>         u64 hw_dequeue;
> +       bool cycle_found = false;
>
>         ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
>                         ep_index, stream_id);
> @@ -486,45 +462,36 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
>                 hw_dequeue = le64_to_cpu(ep_ctx->deq);
>         }
>
> -       /* Find virtual address and segment of hardware dequeue pointer */
> -       state->new_deq_seg = ep_ring->deq_seg;
> -       state->new_deq_ptr = ep_ring->dequeue;
> -       while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr)
> -                       != (dma_addr_t)(hw_dequeue & ~0xf)) {
> -               next_trb(xhci, ep_ring, &state->new_deq_seg,
> -                                       &state->new_deq_ptr);
> -               if (state->new_deq_ptr == ep_ring->dequeue) {
> -                       WARN_ON(1);
> -                       return;
> -               }
> -       }
> +       new_seg = ep_ring->deq_seg;
> +       new_deq = ep_ring->dequeue;
> +       state->new_cycle_state = hw_dequeue & 0x1;
>         /*
> -        * Find cycle state for last_trb, starting at old cycle state of
> -        * hw_dequeue. If there is only one segment ring, find_trb_seg() will
> -        * return immediately and cannot toggle the cycle state if this search
> -        * wraps around, so add one more toggle manually in that case.
> +        * We want to find the pointer, segment and cycle state of the
> +        * new trb (the one after current TD's last_trb). We know the
> +        * cycle state at hw_dequeue, so walk the ring until it's found.
> +        * Once found we can jump a whole segments, but carefully cross them
> +        * to check for cycle toggles.
>          */
> -       state->new_cycle_state = hw_dequeue & 0x1;
> -       if (ep_ring->first_seg == ep_ring->first_seg->next &&
> -                       cur_td->last_trb < state->new_deq_ptr)
> -               state->new_cycle_state ^= 0x1;
> -
> -       state->new_deq_ptr = cur_td->last_trb;
> -       xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
> -                       "Finding segment containing last TRB in TD.");
> -       state->new_deq_seg = find_trb_seg(state->new_deq_seg,
> -                       state->new_deq_ptr, &state->new_cycle_state);
> -       if (!state->new_deq_seg) {
> -               WARN_ON(1);
> -               return;
> -       }
> -
> -       /* Increment to find next TRB after last_trb. Cycle if appropriate. */
> -       trb = &state->new_deq_ptr->generic;
> -       if (TRB_TYPE_LINK_LE32(trb->field[3]) &&
> -           (trb->field[3] & cpu_to_le32(LINK_TOGGLE)))
> -               state->new_cycle_state ^= 0x1;
> -       next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr);
> +       do {
> +               if (cycle_found) {
> +                       if (new_seg->trbs > cur_td->last_trb ||
> +                           &new_seg->trbs[TRBS_PER_SEGMENT - 1] < cur_td->last_trb)
> +                               new_deq = &new_seg->trbs[TRBS_PER_SEGMENT - 1];
> +               } else if (xhci_trb_virt_to_dma(new_seg, new_deq)
> +                          == (dma_addr_t)(hw_dequeue & ~0xf))
> +                       cycle_found = true;
> +               tmp_trb = new_deq;
> +               next_trb(xhci, ep_ring, &new_seg, &new_deq);
> +
> +               if (cycle_found &&
> +                   TRB_TYPE_LINK_LE32(tmp_trb->generic.field[3]) &&
> +                   tmp_trb->generic.field[3] & cpu_to_le32(LINK_TOGGLE))
> +                       state->new_cycle_state ^= 0x1;
> +
> +       } while (tmp_trb != cur_td->last_trb && new_deq != ep->ring->dequeue);

What happens when new_deq == ep->ring->dequeue? I'm not sure if
there's anything good you could do (save for reinitializing the whole
ring), and it shouldn't happen anyway, but you should probably at
least print some kind of warning.

> +
> +       state->new_deq_seg = new_seg;
> +       state->new_deq_ptr = new_deq;
>
>         /* Don't update the ring cycle state for the producer (us). */
>         xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
> --
> 1.8.3.2
>

Looks pretty good! Merging both searches together like that really
does make it look much simpler.

But it makes the slightly dangerous assumption that hw_dequeue is at
most one TRB after last_trb (because once you found last_trb you
always exit even if cycle_found hasn't been set yet). I'm worried
about what Maciej's controller will do if it hits a Stall Error on the
last Normal TRB before a Link TRB with cycle change (a hard case to
test, unfortunately). Will it just put hw_dequeue on the Link TRB, or
will it keep advancing further to the first TRB of the next segment?
In the latter case, your cycle state would be wrong with this code
(because you set the dequeue pointer to the Link TRB but give it the
cycle state from the next segment).
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux