Re: [PATCH] Better fix for NIU MSI-X problem

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

 



On Wed, 13 May 2009 15:43:10 -0600
Matthew Wilcox <matthew@xxxxxx> wrote:

> 
> [This is against Jesse's tree which includes my original patch]
> 
> The previous MSI-X fix (8d181018532dd709ec1f789e374cda92d7b01ce1) had
> three bugs.  First, it didn't move the write that disabled the vector.
> This led to writing garbage to the MSI-X vector (spotted by Michael
> Ellerman).  It didn't fix the PCI resume case, and it had a race
> window where the device could generate an interrupt before the MSI-X
> registers were programmed (leading to a DMA to random addresses).
> 
> Fortunately, the MSI-X capability has a bit to mask all the vectors.
> By setting this bit instead of clearing the enable bit, we can ensure
> the device will not generate spurious interrupts.  Since the
> capability is now enabled, the NIU device will not have a problem
> with the reads and writes to the MSI-X registers being in the
> original order in the code.
> 
> Signed-off-by: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
> 
> diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
> index 3627732..f530611 100644
> --- a/drivers/pci/msi.c
> +++ b/drivers/pci/msi.c
> @@ -93,7 +93,7 @@ static void msi_set_enable(struct pci_dev *dev, int
> enable) __msi_set_enable(dev, pci_find_capability(dev,
> PCI_CAP_ID_MSI), enable); }
>  
> -static void msix_set_enable(struct pci_dev *dev, int enable)
> +static void msix_disable(struct pci_dev *dev)
>  {
>  	int pos;
>  	u16 control;
> @@ -102,8 +102,6 @@ static void msix_set_enable(struct pci_dev *dev,
> int enable) if (pos) {
>  		pci_read_config_word(dev, pos + PCI_MSIX_FLAGS,
> &control); control &= ~PCI_MSIX_FLAGS_ENABLE;
> -		if (enable)
> -			control |= PCI_MSIX_FLAGS_ENABLE;
>  		pci_write_config_word(dev, pos + PCI_MSIX_FLAGS,
> control); }
>  }
> @@ -321,22 +319,22 @@ static void __pci_restore_msix_state(struct
> pci_dev *dev) 
>  	if (!dev->msix_enabled)
>  		return;
> +	BUG_ON(list_empty(&dev->msi_list));
> +	entry = list_entry(dev->msi_list.next, struct msi_desc,
> list);
> +	pos = entry->msi_attrib.pos;
> +	pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
>  
>  	/* route the table */
>  	pci_intx_for_msi(dev, 0);
> -	msix_set_enable(dev, 0);
> +	control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL;
> +	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
>  
>  	list_for_each_entry(entry, &dev->msi_list, list) {
>  		write_msi_msg(entry->irq, &entry->msg);
>  		msix_mask_irq(entry, entry->masked);
>  	}
>  
> -	BUG_ON(list_empty(&dev->msi_list));
> -	entry = list_entry(dev->msi_list.next, struct msi_desc,
> list);
> -	pos = entry->msi_attrib.pos;
> -	pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
>  	control &= ~PCI_MSIX_FLAGS_MASKALL;
> -	control |= PCI_MSIX_FLAGS_ENABLE;
>  	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
>  }
>  
> @@ -427,11 +425,18 @@ static int msix_capability_init(struct pci_dev
> *dev, u8 bir;
>  	void __iomem *base;
>  
> -	msix_set_enable(dev, 0);/* Ensure msix is disabled as I set
> it up */ -
>     	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
> +
> +	/*
> +	 * Some devices require MSI-X to be enabled before we can
> touch the
> +	 * MSI-X registers.  We need to mask all the vectors to
> prevent
> +	 * interrupts coming in before they're fully set up.
> +	 */
> +	pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
> +	control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL;
> +	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
> +
>  	/* Request & Map MSI-X table region */
> - 	pci_read_config_word(dev, msi_control_reg(pos), &control);
>  	nr_entries = multi_msix_capable(control);
>  
>   	pci_read_config_dword(dev, msix_table_offset_reg(pos),
> &table_offset); @@ -439,8 +444,10 @@ static int
> msix_capability_init(struct pci_dev *dev, table_offset &=
> ~PCI_MSIX_FLAGS_BIRMASK; phys_addr = pci_resource_start (dev, bir) +
> table_offset; base = ioremap_nocache(phys_addr, nr_entries *
> PCI_MSIX_ENTRY_SIZE);
> -	if (base == NULL)
> -		return -ENOMEM;
> +	if (base == NULL) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
>  
>  	/* MSI-X Table Initialization */
>  	for (i = 0; i < nvec; i++) {
> @@ -455,6 +462,8 @@ static int msix_capability_init(struct pci_dev
> *dev, entry->msi_attrib.default_irq = dev->irq;
>  		entry->msi_attrib.pos = pos;
>  		entry->mask_base = base;
> +		entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE
> +
> +
> PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); msix_mask_irq(entry, 1);
>  
>  		list_add_tail(&entry->list, &dev->msi_list);
> @@ -475,10 +484,8 @@ static int msix_capability_init(struct pci_dev
> *dev, ret = avail;
>  	}
>  
> -	if (ret) {
> -		msi_free_irqs(dev);
> -		return ret;
> -	}
> +	if (ret)
> +		goto fail;
>  
>  	i = 0;
>  	list_for_each_entry(entry, &dev->msi_list, list) {
> @@ -486,18 +493,21 @@ static int msix_capability_init(struct pci_dev
> *dev, set_irq_msi(entry->irq, entry);
>  		i++;
>  	}
> -	/* Set MSI-X enabled bits */
> +
> +	/* Set MSI-X enabled bits and unmask the function */
>  	pci_intx_for_msi(dev, 0);
> -	msix_set_enable(dev, 1);
>  	dev->msix_enabled = 1;
>  
> -	list_for_each_entry(entry, &dev->msi_list, list) {
> -		int vector = entry->msi_attrib.entry_nr;
> -		entry->masked = readl(base + vector *
> PCI_MSIX_ENTRY_SIZE +
> -
> PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
> -	}
> +	control &= ~PCI_MSIX_FLAGS_MASKALL;
> +	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
>  
>  	return 0;
> +
> + fail:
> +	msi_free_irqs(dev);
> +	control &= ~PCI_MSIX_FLAGS_ENABLE;
> +	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
> +	return ret;
>  }
>  
>  /**
> @@ -742,10 +752,11 @@ void pci_msix_shutdown(struct pci_dev* dev)
>  	if (!pci_msi_enable || !dev || !dev->msix_enabled)
>  		return;
>  
> -	msix_set_enable(dev, 0);
> +	msix_disable(dev);
>  	pci_intx_for_msi(dev, 1);
>  	dev->msix_enabled = 0;
>  }
> +
>  void pci_disable_msix(struct pci_dev* dev)
>  {
>  	if (!pci_msi_enable || !dev || !dev->msix_enabled)
> diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h
> index 71f4df2..5b58ab0 100644
> --- a/drivers/pci/msi.h
> +++ b/drivers/pci/msi.h
> @@ -25,8 +25,6 @@
>  
>  #define msix_table_offset_reg(base)	(base + 0x04)
>  #define msix_pba_offset_reg(base)	(base + 0x08)
> -#define msix_enable(control)	 	control |=
> PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control)
> control &= ~PCI_MSIX_FLAGS_ENABLE #define msix_table_size(control)
> 	((control & PCI_MSIX_FLAGS_QSIZE)+1) #define
> multi_msix_capable		msix_table_size #define
> msix_unmask(address)	 	(address &
> ~PCI_MSIX_FLAGS_BITMASK)
> 

Applied this one to linux-next.  The thread for this had quite a bit of
discussion though, so if further fixes are needed just send me
incremental patches on top please.

Thanks,
-- 
Jesse Barnes, Intel Open Source Technology Center
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux