Re: [PATCH] xHCI: supporting MSI/MSI-X

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

 



See my comments below.  I haven't tested this yet; I'm just being
nit-picky about the coding style because the way it's written makes it
hard for me to reason about error paths.

Sarah Sharp

On Fri, Apr 30, 2010 at 02:24:52AM +0800, Andiry Xu wrote:
> >From 4643a08179b0c2aac8f3f4cdb09a6fedf5ddbf72 Mon Sep 17 00:00:00 2001
> From: Dong Nguyen <Dong.Nguyen@xxxxxxx>
> Date: Thu, 22 Apr 2010 16:58:23 -0700
> Subject: [PATCH] xHCI: supporting MSI/MSI-X
> 
> Enable MSI/MSI-X supporting in xHCI driver.
> 
> xHCI driver will try to use MSI-X as default.
> If MSI-X register fails, it will roll back using MSI and Legacy IRQs.
> 
> Signed-off-by: Dong Nguyen <Dong.Nguyen@xxxxxxx>
> Signed-off-by: Andiry Xu <andiry.xu@xxxxxxx>
> ---
>  drivers/usb/host/xhci-pci.c |    5 +
>  drivers/usb/host/xhci.c     |  214 +++++++++++++++++++++++++++++++++++-------
>  drivers/usb/host/xhci.h     |    7 ++
>  3 files changed, 190 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index 417d37a..a351b64 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -113,7 +113,12 @@ static const struct hc_driver xhci_pci_hc_driver = {
>  	/*
>  	 * generic hardware linkage
>  	 */
> +	/* do not register irq for xhci HCD
> +	 * xhci use MSIx in xhci-hcd
> +	 */
> +#if 0
>  	.irq =			xhci_irq,
> +#endif

Please don't #if 0 out these lines, just remove them.  That's what we
have git history for.  If you want, you can leave the comment.

>  	.flags =		HCD_MEMORY | HCD_USB3,
>  
>  	/*
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 492a61c..cbf4e3e 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -20,6 +20,7 @@
>   * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>   */
>  
> +#include <linux/pci.h>
>  #include <linux/irq.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> @@ -31,6 +32,7 @@
>  
>  /* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
>  static int link_quirk;
> +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
>  module_param(link_quirk, int, S_IRUGO | S_IWUSR);
>  MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
>  
> @@ -131,22 +133,137 @@ int xhci_reset(struct xhci_hcd *xhci)
>  	return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000);
>  }
>  
> +/*
> + * request IRQs from MSI-X.
> + *
> + * This requests for msix_count IRQs and set the xhci_flags properly.
> + */
> +static int xhci_request_irq(struct xhci_hcd *xhci)
> +{
> +	int	i, ret = 0;
> +	irq_handler_t   fn;
> +	struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
> +
> +	if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSIX) {
> +		for (i = 0; i < xhci->msix_count; i++) {
> +			fn = (irq_handler_t)xhci_msi_irq;
> +			ret = request_irq(xhci->msix_entries[i].vector,
> +					fn, 0, "xhci_hcd", xhci_to_hcd(xhci));
> +			if (ret)
> +				goto disable_msix;
> +		}
> +		xhci->xhci_flags |= XHCI_FLAG_USING_MSIX;
> +	} else if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSI) {
> +		ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
> +					0, "xhci", xhci_to_hcd(xhci));

Why do you need fn here?  Just cast the function pointer in that first
call like you do in the line above.

> +		if (ret)
> +			goto disable_msi;
> +
> +		xhci->xhci_flags |= XHCI_FLAG_USING_MSI;
> +	} else {
> +		xhci_err(xhci, "Unknown xhci_flags for MSI-X interrupt\n");
> +	}
> +	return 0;
> +
> +disable_msi:
> +	xhci_err(xhci, "disable MSI interrupt\n");
> +	pci_disable_msi(pdev);
> +	xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSI;
> +	return ret;
> +
> +disable_msix:
> +	xhci_err(xhci, "disable MSI-X interrupt\n");
> +	pci_disable_msix(pdev);

I have to check, but does pci_disable_msix() free all the vectors
associated with that device, or do you have to cancel each one?

> +	kfree(xhci->msix_entries);
> +	xhci->msix_entries = NULL;
> +	xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSIX;
> +	return ret;
> +}

Please don't use goto statements unless there's shared error handling
code like this:

	xhci_free_command(xhci, stream_info->free_streams_command);
cleanup_ctx:
	kfree(stream_info->stream_rings);
cleanup_info:
	kfree(stream_info);
cleanup_trbs:
	xhci->cmd_ring_reserved_trbs--;
	return NULL;

The goto statements just make this function more confusing.

The code to clean up the irq requests if it fails should be placed back
into xhci_setup_msi() and xhci_setup_msix(), since xhci_request_irq()
doesn't allocate xhci->msix_entries.  Variables should be freed in the
function that allocated them and actions should be undone in the same
function if error conditions happen.  It's just easier to keep track of
whether variables got freed properly.

I think xhci_request_irq() should be two functions, one to setup msi and
one to set up MSI-X.  There's basically no shared code, if you call it
with XHCI_FLAG_SUPPORT_MSI set vs. XHCI_FLAG_USING_MSI.  "Functions
should be short and sweet, and do just one thing."
(Documentation/CodingStyle)

Also, I think you should try to remove as many xhci->xhci_flags as
possible to make this code simpler.  The flags are just confusing and
add extra state to the xhci_hcd that I don't think you need.

Splitting up this function into two functions will help remove some uses
of XHCI_FLAG_SUPPORT_MSIX and XHCI_FLAG_SUPPORT_MSI.  I think you can
get rid of the other usages, see my comments below.

> +
> +/*
> + * Free IRQs
> + * free all IRQs request
> + */
> +static void xhci_free_irq(struct xhci_hcd *xhci)
> +{
> +	int i;
> +	struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
> +
> +	if (xhci->xhci_flags & XHCI_FLAG_USING_MSIX) {
> +		if (!xhci->msix_entries)
> +			return;
> +
> +		for (i = 0; i < xhci->msix_count; i++)
> +			free_irq(xhci->msix_entries[i].vector,
> +					xhci_to_hcd(xhci));
> +
> +			xhci->xhci_flags &= ~XHCI_FLAG_USING_MSIX;
> +			return;
> +	}
> +
> +	if (xhci->xhci_flags & XHCI_FLAG_USING_MSI) {
> +		free_irq(pdev->irq, xhci_to_hcd(xhci));
> +		xhci->xhci_flags &= ~XHCI_FLAG_USING_MSI;
> +		return;
> +	}
> +
> +	if (xhci->xhci_flags & XHCI_FLAG_USING_LEGACY_IRQ) {
> +		free_irq(pdev->irq, xhci_to_hcd(xhci));
> +		xhci->xhci_flags &= ~XHCI_FLAG_USING_LEGACY_IRQ;
> +		return;
> +	}

The freeing code for these two cases are basically the same, so why do
you have two different flags?

Also, where do you set XHCI_FLAG_USING_LEGACY_IRQ?  I only see it
checked in this patch, never set, which means that second if statement
will never run.

> +
> +	return;
> +}
> +
> +/*
> + * Set up MSI
> + */
> +static int xhci_setup_msi(struct xhci_hcd *xhci)
> +{
> +	int	ret;
> +	struct	pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
> +
> +	ret = pci_enable_msi(pdev);
> +	if (ret) {
> +		xhci_err(xhci, "Failed to allocate MSI entry\n");
> +		return ret;
> +	}
> +
> +	xhci->xhci_flags |= XHCI_FLAG_SUPPORT_MSI;
> +	ret = xhci_request_irq(xhci);
> +
> +	return ret;
> +}
>  
> -#if 0
> -/* Set up MSI-X table for entry 0 (may claim other entries later) */
> +/*
> + * Set up MSI-X
> + */
>  static int xhci_setup_msix(struct xhci_hcd *xhci)
>  {
> -	int ret;
> +	int	i, ret = 0;
> +	int	cpus = num_online_cpus();
> +	int	msix_vecs, max_vecs;
>  	struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
>  
> -	xhci->msix_count = 0;
> +	xhci->msix_entries = NULL;
> +	max_vecs = HCS_MAX_INTRS(xhci->hcs_params1);
> +	msix_vecs = min(cpus + 1, max_vecs);
> +	xhci->msix_count = msix_vecs;
> +
>  	/* XXX: did I do this right?  ixgbe does kcalloc for more than one */

You can remove my comment here.

> -	xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
> +	xhci->msix_entries =
> +			kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
> +					GFP_KERNEL);
>  	if (!xhci->msix_entries) {
>  		xhci_err(xhci, "Failed to allocate MSI-X entries\n");
>  		return -ENOMEM;
>  	}
> -	xhci->msix_entries[0].entry = 0;
> +
> +	for (i = 0; i < xhci->msix_count; i++) {
> +		xhci->msix_entries[i].entry = i;
> +		xhci->msix_entries[i].vector = 0;
> +	}
>  
>  	ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
>  	if (ret) {
> @@ -154,21 +271,10 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
>  		goto free_entries;
>  	}
>  
> -	/*
> -	 * Pass the xhci pointer value as the request_irq "cookie".
> -	 * If more irqs are added, this will need to be unique for each one.
> -	 */
> -	ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
> -			"xHCI", xhci_to_hcd(xhci));
> -	if (ret) {
> -		xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
> -		goto disable_msix;
> -	}
> -	xhci_dbg(xhci, "Finished setting up MSI-X\n");
> -	return 0;
> +	xhci->xhci_flags |= XHCI_FLAG_SUPPORT_MSIX;
> +	ret = xhci_request_irq(xhci);
> +	return ret;
>  
> -disable_msix:
> -	pci_disable_msix(pdev);
>  free_entries:
>  	kfree(xhci->msix_entries);
>  	xhci->msix_entries = NULL;
> @@ -180,16 +286,28 @@ free_entries:
>  static void xhci_cleanup_msix(struct xhci_hcd *xhci)
>  {
>  	struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
> -	if (!xhci->msix_entries)
> +
> +	xhci_free_irq(xhci);
> +
> +	if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSIX) {
> +		if (!xhci->msix_entries)
> +			return;
> +
> +		pci_disable_msix(pdev);
> +		kfree(xhci->msix_entries);
> +		xhci->msix_entries = NULL;
> +		xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSIX;
>  		return;
> +	}

The first if statement should just read
	if (xhci->msix_entries)
		pci_disable_msix(pdev);
		...
		return;

Then you can stop using XHCI_FLAG_SUPPORT_MSIX once you split up
xhci_request_irq().

> -	free_irq(xhci->msix_entries[0].vector, xhci);
> -	pci_disable_msix(pdev);
> -	kfree(xhci->msix_entries);
> -	xhci->msix_entries = NULL;
> -	xhci_dbg(xhci, "Finished cleaning up MSI-X\n");
> +	if (xhci->xhci_flags & XHCI_FLAG_SUPPORT_MSI) {
> +		pci_disable_msi(pdev);
> +		xhci->xhci_flags &= ~XHCI_FLAG_SUPPORT_MSI;
> +		return;
> +	}

You can call pci_disable_msi() unconditionally here, because it will
immediately return if it never enabled MSI for the device.  So you don't
need XHCI_FLAG_SUPPORT_MSI either.

> +
> +	return;
>  }
> -#endif
>  
>  /*
>   * Initialize memory for HCD and xHC (one-time init).
> @@ -312,6 +430,14 @@ hw_died:
>  	return IRQ_HANDLED;
>  }
>  
> +static irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
> +{
> +	irqreturn_t ret;
> +
> +	ret = xhci_irq(hcd);
> +	return ret;
> +}
> +
>  #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
>  void xhci_event_ring_work(unsigned long arg)
>  {
> @@ -387,21 +513,36 @@ int xhci_run(struct usb_hcd *hcd)
>  {
>  	u32 temp;
>  	u64 temp_64;
> +	u32	ret;
>  	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> +	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
>  	void (*doorbell)(struct xhci_hcd *) = NULL;
>  
>  	hcd->uses_new_polling = 1;
>  	hcd->poll_rh = 0;
>  
>  	xhci_dbg(xhci, "xhci_run\n");
> -#if 0	/* FIXME: MSI not setup yet */
> +
>  	/* Do this at the very last minute */
>  	ret = xhci_setup_msix(xhci);
> -	if (!ret)
> -		return ret;
> +	if (ret) {
> +		/* use msi*/
> +		ret = xhci_setup_msi(xhci);
> +		if (ret) {
> +			/* use legacy IRQ */
> +			ret = request_irq(pdev->irq,
> +					(irq_handler_t)xhci_irq,
> +					IRQF_SHARED,
> +					"xhci",
> +					xhci_to_hcd(xhci));
> +			if (ret) {
> +				xhci_err(xhci,
> +				"Failed to request irq interrupt\n");
> +				return ret;
> +			}
> +		}
> +	}

You can un-nest most of this code, like so:
	if (ret)
		/* fall back to msi*/
		ret = xhci_setup_msi(xhci);
	if (ret)
		/* fall back to legacy IRQ */
		ret = request_irq(pdev->irq,
				(irq_handler_t)xhci_irq,
				IRQF_SHARED,
				"xhci",
				xhci_to_hcd(xhci));
	if (ret) {
		xhci_err(xhci,
		"Failed to request irq interrupt\n");
		return ret;
	}

>  
> -	return -ENOSYS;
> -#endif
>  #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
>  	init_timer(&xhci->event_ring_timer);
>  	xhci->event_ring_timer.data = (unsigned long) xhci;
> @@ -485,9 +626,8 @@ void xhci_stop(struct usb_hcd *hcd)
>  	xhci_reset(xhci);
>  	spin_unlock_irq(&xhci->lock);
>  
> -#if 0	/* No MSI yet */
>  	xhci_cleanup_msix(xhci);
> -#endif
> +
>  #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
>  	/* Tell the event ring poll function not to reschedule */
>  	xhci->zombie = 1;
> @@ -523,9 +663,7 @@ void xhci_shutdown(struct usb_hcd *hcd)
>  	xhci_halt(xhci);
>  	spin_unlock_irq(&xhci->lock);
>  
> -#if 0
>  	xhci_cleanup_msix(xhci);
> -#endif
>  
>  	xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
>  		    xhci_readl(xhci, &xhci->op_regs->status));
> @@ -792,6 +930,10 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
>  
>  	xhci = hcd_to_xhci(hcd);
>  	spin_lock_irqsave(&xhci->lock, flags);
> +
> +	if (xhci->xhci_flags & (XHCI_FLAG_USING_MSI | XHCI_FLAG_USING_MSIX))
> +		clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
> +

I'm confused as to what you're doing here.  It looks like you're
clearing the interrupt when the URB is being canceled by the class
driver.  Why?  We haven't entered this function because the hardware
interrupted us.

Shouldn't this code be in xhci_msi_irq() instead?  If so, you don't need
to check the flags, because xhci_msi_irq() should only be called if
you've successfully set up MSI or MSI-X.  That would get rid of what I
think is your last usage of XHCI_FLAG_USING_MSI and
XHCI_FLAG_USING_MSIX.

>  	/* Make sure the URB hasn't completed or been unlinked already */
>  	ret = usb_hcd_check_unlink_urb(hcd, urb, status);
>  	if (ret || !urb->hcpriv)
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index e5eb09b..cc27b17 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -1112,6 +1112,13 @@ struct xhci_hcd {
>  	unsigned int		quirks;
>  #define	XHCI_LINK_TRB_QUIRK	(1 << 0)
>  #define XHCI_RESET_EP_QUIRK	(1 << 1)
> +
> +	u32                     xhci_flags;
> +#define XHCI_FLAG_USING_LEGACY_IRQ      0x00000001
> +#define XHCI_FLAG_SUPPORT_MSI           0x00000002
> +#define XHCI_FLAG_SUPPORT_MSIX          0x00000004
> +#define XHCI_FLAG_USING_MSI             0x00000008
> +#define XHCI_FLAG_USING_MSIX            0x00000010
>  };
>  
>  /* For testing purposes */
> -- 
> 1.6.3.3
> 
> 
> 
> --
> 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