Re: [PATCH 06/10 v8] xHCI: Missed Service Error Event process

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

 



On Tue, Jul 20, 2010 at 04:49:25PM +0800, Andiry Xu wrote:
> >From 02b3e01af9fef97d113f30de96c07f6a4c24a895 Mon Sep 17 00:00:00 2001
> From: Andiry Xu <andiry.xu@xxxxxxx>
> Date: Thu, 15 Jul 2010 16:14:09 +0800
> Subject: [PATCH 06/10] xHCI: Missed Service Error Event process
> 
> This patch adds mechanism to process Missed Service Error Event.
> Sometimes the xHC is unable to process the isoc TDs in time, it will
> generate Missed Service Error Event. In this case some TDs on the ring are
> not processed and missed. When encounter a Missed Servce Error Event, set
> the skip flag of the ep, and process the missed TDs until reach the next
> processed TD, then clear the skip flag.
> 
> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>

> ---
>  drivers/usb/host/xhci-mem.c  |    1 +
>  drivers/usb/host/xhci-ring.c |  158 ++++++++++++++++++++++++++++-------------
>  drivers/usb/host/xhci.h      |    8 ++
>  3 files changed, 117 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
> index 8cc824b..5763dd3 100644
> --- a/drivers/usb/host/xhci-mem.c
> +++ b/drivers/usb/host/xhci-mem.c
> @@ -1123,6 +1123,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
>  		virt_dev->num_rings_cached--;
>  		xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
>  	}
> +	virt_dev->eps[ep_index].skip = false;
>  	ep_ring = virt_dev->eps[ep_index].new_ring;
>  	ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
>  
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index 5bb12fe..4c35010 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -1675,6 +1675,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
>  					"still with TDs queued?\n",
>  				TRB_TO_SLOT_ID(event->flags), ep_index);
>  		goto cleanup;
> +	case COMP_MISSED_INT:
> +		/*
> +		 * When encounter missed service error, one or more isoc tds
> +		 * may be missed by xHC.
> +		 * Set skip flag of the ep_ring; Complete the missed tds as
> +		 * short transfer when process the ep_ring next time.
> +		 */
> +		ep->skip = true;
> +		xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
> +		goto cleanup;
>  	default:
>  		if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
>  			status = 0;
> @@ -1685,60 +1695,108 @@ static int handle_tx_event(struct xhci_hcd *xhci,
>  		goto cleanup;
>  	}
>  
> -	/* This TRB should be in the TD at the head of this ring's TD list */
> -	if (list_empty(&ep_ring->td_list)) {
> -		xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
> -				TRB_TO_SLOT_ID(event->flags), ep_index);
> -		xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
> -				(unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
> -		xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
> -		goto cleanup;
> -	}
> -	td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
> -
> -	/* Is this a TRB in the currently executing TD? */
> -	event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
> -			td->last_trb, event_dma);
> -	if (!event_seg) {
> -		/* HC is busted, give up! */
> -		xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n");
> -		return -ESHUTDOWN;
> -	}
> -	event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)];
> +	do {
> +		/* This TRB should be in the TD at the head of this ring's
> +		 * TD list.
> +		 */
> +		if (list_empty(&ep_ring->td_list)) {
> +			xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
> +					"with no TDs queued?\n",
> +				  TRB_TO_SLOT_ID(event->flags), ep_index);
> +			xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
> +			  (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
> +			xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
> +			if (ep->skip) {
> +				ep->skip = false;
> +				xhci_dbg(xhci, "td_list is empty while skip "
> +						"flag set. Clear skip flag.\n");
> +			}
> +			ret = 0;
> +			goto cleanup;
> +		}
>  
> -	/* Now update the urb's actual_length and give back to the core */
> -	/* Was this a control transfer? */
> -	if (usb_endpoint_xfer_control(&td->urb->ep->desc))
> -		ret = process_ctrl_td(xhci, td, event_trb, event, ep,
> -					&status);
> -	else
> -		ret = process_bulk_intr_td(xhci, td, event_trb, event, ep,
> -					&status);
> +		td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
> +		/* Is this a TRB in the currently executing TD? */
> +		event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
> +				td->last_trb, event_dma);
> +		if (event_seg && ep->skip) {
> +			xhci_dbg(xhci, "Found td. Clear skip flag.\n");
> +			ep->skip = false;
> +		}
> +		if (!event_seg &&
> +		   (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
> +			/* HC is busted, give up! */
> +			xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
> +					"part of current TD\n");
> +			return -ESHUTDOWN;
> +		}
>  
> -cleanup:
> -	inc_deq(xhci, xhci->event_ring, true);
> -	xhci_set_hc_event_deq(xhci);
> +		if (event_seg) {
> +			event_trb = &event_seg->trbs[(event_dma -
> +					 event_seg->dma) / sizeof(*event_trb)];
> +			/*
> +			 * No-op TRB should not trigger interrupts.
> +			 * If event_trb is a no-op TRB, it means the
> +			 * corresponding TD has been cancelled. Just ignore
> +			 * the TD.
> +			 */
> +			if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
> +					 == TRB_TYPE(TRB_TR_NOOP)) {
> +				xhci_dbg(xhci, "event_trb is a no-op TRB. "
> +						"Skip it\n");
> +				goto cleanup;
> +			}
> +		}
>  
> -	/* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */
> -	if (ret) {
> -		urb = td->urb;
> -		/* Leave the TD around for the reset endpoint function to use
> -		 * (but only if it's not a control endpoint, since we already
> -		 * queued the Set TR dequeue pointer command for stalled
> -		 * control endpoints).
> +		/* Now update the urb's actual_length and give back to
> +		 * the core
>  		 */
> -		if (usb_endpoint_xfer_control(&urb->ep->desc) ||
> -			(trb_comp_code != COMP_STALL &&
> -				trb_comp_code != COMP_BABBLE))
> -			kfree(td);
> -
> -		usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
> -		xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n",
> -				urb, urb->actual_length, status);
> -		spin_unlock(&xhci->lock);
> -		usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
> -		spin_lock(&xhci->lock);
> -	}
> +		if (usb_endpoint_xfer_control(&td->urb->ep->desc))
> +			ret = process_ctrl_td(xhci, td, event_trb, event, ep,
> +						 &status);
> +		else
> +			ret = process_bulk_intr_td(xhci, td, event_trb, event,
> +						 ep, &status);
> +
> +cleanup:
> +		/*
> +		 * Do not update event ring dequeue pointer if ep->skip is set.
> +		 * Will roll back to continue process missed tds.
> +		 */
> +		if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
> +			inc_deq(xhci, xhci->event_ring, true);
> +			xhci_set_hc_event_deq(xhci);
> +		}
> +
> +		if (ret) {
> +			urb = td->urb;
> +			/* Leave the TD around for the reset endpoint function
> +			 * to use(but only if it's not a control endpoint,
> +			 * since we already queued the Set TR dequeue pointer
> +			 * command for stalled control endpoints).
> +			 */
> +			if (usb_endpoint_xfer_control(&urb->ep->desc) ||
> +				(trb_comp_code != COMP_STALL &&
> +					trb_comp_code != COMP_BABBLE))
> +				kfree(td);
> +
> +			usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
> +			xhci_dbg(xhci, "Giveback URB %p, len = %d, "
> +					"status = %d\n",
> +					urb, urb->actual_length, status);
> +			spin_unlock(&xhci->lock);
> +			usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
> +			spin_lock(&xhci->lock);
> +		}
> +
> +	/*
> +	 * If ep->skip is set, it means there are missed tds on the
> +	 * endpoint ring need to take care of.
> +	 * Process them as short transfer until reach the td pointed by
> +	 * the event.
> +	 */
> +	} while (ep->skip && trb_comp_code != COMP_MISSED_INT);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 6c7e343..01da844 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -720,6 +720,14 @@ struct xhci_virt_ep {
>  	struct timer_list	stop_cmd_timer;
>  	int			stop_cmds_pending;
>  	struct xhci_hcd		*xhci;
> +	/*
> +	 * Sometimes the xHC can not process isochronous endpoint ring quickly
> +	 * enough, and it will miss some isoc tds on the ring and generate
> +	 * a Missed Service Error Event.
> +	 * Set skip flag when receive a Missed Service Error Event and
> +	 * process the missed tds on the endpoint ring.
> +	 */
> +	bool			skip;
>  };
>  
>  struct xhci_virt_device {
> -- 
> 1.7.0.4
> 
> 
> 
> --
> 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
--
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