Re: [PATCH v3 -tip 5/5] AHCI: Support multiple MSIs

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

 



* Alexander Gordeev <agordeev@xxxxxxxxxx> wrote:

> Take advantage of multiple MSIs implementation on x86 - on systems with
> IRQ remapping AHCI ports not only get assigned separate MSI vectors -
> but also separate IRQs. As result, interrupts generated by different
> ports could be serviced on different CPUs rather than on a single one.
> 
> In cases when number of allocated MSIs is less than requested the Sharing
> Last MSI mode does not get used, no matter implemented in hardware or not.
> Instead, the driver assumes the advantage of multiple MSIs is negated and
> falls back to the single MSI mode as if MRSM bit was set (some Intel chips
> implement this strategy anyway - MRSM bit gets set even if the number of
> allocated MSIs exceeds the number of implemented ports).
> 
> Signed-off-by: Alexander Gordeev <agordeev@xxxxxxxxxx>
> ---
>  drivers/ata/ahci.c    |   91 ++++++++++++++++++++++++++++++++++++--
>  drivers/ata/ahci.h    |    6 +++
>  drivers/ata/libahci.c |  118 ++++++++++++++++++++++++++++++++++++++++++++++---
>  3 files changed, 205 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
> index 7862d17..5463fcea 100644
> --- a/drivers/ata/ahci.c
> +++ b/drivers/ata/ahci.c
> @@ -1057,6 +1057,84 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
>  {}
>  #endif
>  
> +int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv)
> +{
> +	int rc;
> +	unsigned int maxvec;
> +
> +	if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) {
> +		rc = pci_enable_msi_block_auto(pdev, &maxvec);
> +		if (rc > 0) {
> +			if ((rc == maxvec) || (rc == 1))
> +				return rc;
> +			/* assume that advantage of multipe MSIs is negated,
> +			 * so fallback to single MSI mode to save resources */

Please use the customary (multi-line) comment style:

  /*
   * Comment .....
   * ...... goes here.
   */

specified in Documentation/CodingStyle.

> +			pci_disable_msi(pdev);
> +			if (!pci_enable_msi(pdev))
> +				return 1;
> +		}
> +	}
> +
> +	pci_intx(pdev, 1);
> +	return 0;
> +}
> +
> +/**
> + *	ahci_host_activate - start AHCI host, request IRQs and register it
> + *	@host: target ATA host
> + *	@irq: base IRQ number to request
> + *	@n_msis: number of MSIs allocated for this host
> + *	@irq_handler: irq_handler used when requesting IRQs
> + *	@irq_flags: irq_flags used when requesting IRQs
> + *
> + *	Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
> + *	when multiple MSIs were allocated. That is one MSI per port, starting
> + *	from @irq.
> + *
> + *	LOCKING:
> + *	Inherited from calling layer (may sleep).
> + *
> + *	RETURNS:
> + *	0 on success, -errno otherwise.
> + */
> +int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis)
> +{
> +	int i, rc;
> +
> +	/* Sharing Last Message among several ports is not supported */
> +	if (n_msis < host->n_ports)
> +		return -EINVAL;
> +
> +	rc = ata_host_start(host);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < host->n_ports; i++) {
> +		rc = devm_request_threaded_irq(host->dev,
> +			irq + i, ahci_hw_interrupt, ahci_thread_fn, IRQF_SHARED,
> +			dev_driver_string(host->dev), host->ports[i]);
> +		if (rc)
> +			goto out_free_irqs;
> +	}
> +
> +	for (i = 0; i < host->n_ports; i++)
> +		ata_port_desc(host->ports[i], "irq %d", irq + i);
> +
> +	rc = ata_host_register(host, &ahci_sht);
> +	if (rc)
> +		goto out_free_all_irqs;
> +
> +	return 0;
> +
> +out_free_all_irqs:
> +	i = host->n_ports;
> +out_free_irqs:
> +	for (; i; i--)
> +		devm_free_irq(host->dev, irq + i - 1, host->ports[i]);

Please test the failure path somehow - for example I'm quite 
sure that the line above is buggy and will crash/hang a real 
system: it should be host->ports[i-1].

Writing it as:

		devm_free_irq(host->dev, irq + i-1, host->ports[i-1]);

would help readability as well as bit - or just:

	for (i--; i >= 0; i--)
		devm_free_irq(host->dev, irq + i, host->ports[i]);

(untested)

> +
> +	return rc;
> +}
> +
>  static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  {
>  	unsigned int board_id = ent->driver_data;
> @@ -1065,7 +1143,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	struct device *dev = &pdev->dev;
>  	struct ahci_host_priv *hpriv;
>  	struct ata_host *host;
> -	int n_ports, i, rc;
> +	int n_ports, n_msis, i, rc;
>  	int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
>  
>  	VPRINTK("ENTER\n");
> @@ -1150,11 +1228,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	if (ahci_sb600_enable_64bit(pdev))
>  		hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
>  
> -	if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
> -		pci_intx(pdev, 1);
> -
>  	hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
>  
> +	n_msis = ahci_init_interrupts(pdev, hpriv);
> +	if (n_msis > 1)
> +		hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
> +
>  	/* save initial config */
>  	ahci_pci_save_initial_config(pdev, hpriv);
>  
> @@ -1250,6 +1329,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>  	ahci_pci_print_info(host);
>  
>  	pci_set_master(pdev);
> +
> +	if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
> +		return ahci_host_activate(host, pdev->irq, n_msis);
> +
>  	return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
>  				 &ahci_sht);
>  }
> diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
> index 57eb1c2..24251e8 100644
> --- a/drivers/ata/ahci.h
> +++ b/drivers/ata/ahci.h
> @@ -216,6 +216,7 @@ enum {
>  	AHCI_HFLAG_DELAY_ENGINE		= (1 << 15), /* do not start engine on
>  						        port start (wait until
>  						        error-handling stage) */
> +	AHCI_HFLAG_MULTI_MSI		= (1 << 16), /* multiple PCI MSIs */
>  
>  	/* ap->flags bits */
>  
> @@ -282,6 +283,8 @@ struct ahci_port_priv {
>  	unsigned int		ncq_saw_d2h:1;
>  	unsigned int		ncq_saw_dmas:1;
>  	unsigned int		ncq_saw_sdb:1;
> +	u32			intr_status;	/* interrupts to handle */
> +	spinlock_t		lock;

In general it's nice to add a small comment that explains what 
data the lock protects precisely and in what circumstances it's 
used - especially as it's in the middle of a structure, making 
it unclear at first sight whether it's for the whole ahci_port 
descriptor or just part of it.

>  	u32 			intr_mask;	/* interrupts to enable */
>  	bool			fbs_supported;	/* set iff FBS is supported */
>  	bool			fbs_enabled;	/* set iff FBS is enabled */
> @@ -343,7 +346,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
>  			  struct ata_port_info *pi);
>  int ahci_reset_em(struct ata_host *host);
>  irqreturn_t ahci_interrupt(int irq, void *dev_instance);
> +irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
> +irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
>  void ahci_print_info(struct ata_host *host, const char *scc_s);
> +int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis);
>  
>  static inline void __iomem *__ahci_port_base(struct ata_host *host,
>  					     unsigned int port_no)
> diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
> index 555c07a..3b54e84 100644
> --- a/drivers/ata/libahci.c
> +++ b/drivers/ata/libahci.c
> @@ -1639,19 +1639,16 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
>  		ata_port_abort(ap);
>  }
>  
> -static void ahci_port_intr(struct ata_port *ap)
> +static void ahci_handle_port_interrupt(struct ata_port *ap,
> +				       void __iomem *port_mmio, u32 status)
>  {
> -	void __iomem *port_mmio = ahci_port_base(ap);
>  	struct ata_eh_info *ehi = &ap->link.eh_info;
>  	struct ahci_port_priv *pp = ap->private_data;
>  	struct ahci_host_priv *hpriv = ap->host->private_data;
>  	int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
> -	u32 status, qc_active = 0;
> +	u32 qc_active = 0;
>  	int rc;
>  
> -	status = readl(port_mmio + PORT_IRQ_STAT);
> -	writel(status, port_mmio + PORT_IRQ_STAT);
> -
>  	/* ignore BAD_PMP while resetting */
>  	if (unlikely(resetting))
>  		status &= ~PORT_IRQ_BAD_PMP;
> @@ -1727,6 +1724,107 @@ static void ahci_port_intr(struct ata_port *ap)
>  	}
>  }
>  
> +void ahci_port_intr(struct ata_port *ap)
> +{
> +	void __iomem *port_mmio = ahci_port_base(ap);
> +	u32 status;
> +
> +	status = readl(port_mmio + PORT_IRQ_STAT);
> +	writel(status, port_mmio + PORT_IRQ_STAT);
> +
> +	ahci_handle_port_interrupt(ap, port_mmio, status);
> +}
> +
> +irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
> +{
> +	struct ata_port *ap = dev_instance;
> +	struct ahci_port_priv *pp = ap->private_data;
> +	void __iomem *port_mmio = ahci_port_base(ap);
> +	unsigned long flags;
> +	u32 status;
> +
> +	spin_lock_irqsave(&ap->host->lock, flags);
> +	status = pp->intr_status;
> +	if (status)
> +		pp->intr_status = 0;
> +	spin_unlock_irqrestore(&ap->host->lock, flags);
> +
> +	spin_lock_bh(ap->lock);
> +	ahci_handle_port_interrupt(ap, port_mmio, status);
> +	spin_unlock_bh(ap->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL_GPL(ahci_thread_fn);
> +
> +void ahci_hw_port_interrupt(struct ata_port *ap)
> +{
> +	void __iomem *port_mmio = ahci_port_base(ap);
> +	struct ahci_port_priv *pp = ap->private_data;
> +	u32 status;
> +
> +	status = readl(port_mmio + PORT_IRQ_STAT);
> +	writel(status, port_mmio + PORT_IRQ_STAT);
> +
> +	pp->intr_status |= status;
> +}
> +
> +irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
> +{
> +	struct ata_port *ap_this = dev_instance;
> +	struct ahci_port_priv *pp = ap_this->private_data;
> +	struct ata_host *host = ap_this->host;
> +	struct ahci_host_priv *hpriv = host->private_data;
> +	void __iomem *mmio = hpriv->mmio;
> +	unsigned int i;
> +	u32 irq_stat, irq_masked;
> +
> +	VPRINTK("ENTER\n");

Is this per IRQ handler execution debugging code still needed? 
Same for the other VPRINTK() lines in this patch.

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux