PCI Express设备驱动 (7,linux对于多个MSI中断向量的支持)

从Linux2.6.30内核版本开始,Linux增加了对于请求多个MSI中断向量号的支持。

下面的代码来自Linux2.6.38内核。(不同的cpu对MSI多中断的支持不尽相同,通过下面对Linux2.6.38内核源码的追溯分析,最终可以看到X86处理器不支持MSI多中断!!!)

申请多个多个MSI中断向量号的函数为int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec);

下面没有标志出处的源码都出自文件linux-2.6.38.6\drivers\pci\msi.c

/**
* pci_enable_msi_block - configure device's MSI capability structure
* @dev: device to configure
* @nvec: number of interrupts to configure
*
* Allocate IRQs for a device with the MSI capability.
* This function returns a negative errno if an error occurs. If it
* is unable to allocate the number of interrupts requested, it returns
* the number of interrupts it might be able to allocate. If it successfully
* allocates at least the number of interrupts requested, it returns 0 and
* updates the @dev's irq member to the lowest new interrupt number; the
* other interrupt numbers allocated to this device are consecutive.
*/
int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
{
int status, pos, maxvec;
u16 msgctl;

pos
= pci_find_capability(dev, PCI_CAP_ID_MSI);
if (!pos)
return -EINVAL;
pci_read_config_word(dev, pos
+ PCI_MSI_FLAGS, &msgctl);
maxvec
= 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
if (nvec > maxvec)
return maxvec;

status
= pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI);
if (status)
return status;

WARN_ON(
!!dev->msi_enabled);

/* Check whether driver already requested MSI-X irqs */
if (dev->msix_enabled) {
dev_info(
&dev->dev, "can't enable MSI "
"(MSI-X already enabled)\n");
return -EINVAL;
}

status
= msi_capability_init(dev, nvec);
return status;
}
EXPORT_SYMBOL(pci_enable_msi_block);
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
* @nvec: number of interrupts to allocate
*
* Setup the MSI capability structure of the device with the requested
* number of interrupts. A return value of zero indicates the successful
* setup of an entry with the new MSI irq. A negative return value indicates
* an error, and a positive return value indicates the number of interrupts
* which could have been allocated.
*/
static int msi_capability_init(struct pci_dev *dev, int nvec)
{
struct msi_desc *entry;
int pos, ret;
u16 control;
unsigned mask;

pos
= pci_find_capability(dev, PCI_CAP_ID_MSI);
msi_set_enable(dev, pos,
0); /* Disable MSI during set up */

pci_read_config_word(dev, msi_control_reg(pos),
&control);
/* MSI Entry Initialization */
entry
= alloc_msi_entry(dev);
if (!entry)
return -ENOMEM;

entry
->msi_attrib.is_msix = 0;
entry
->msi_attrib.is_64 = is_64bit_address(control);
entry
->msi_attrib.entry_nr = 0;
entry
->msi_attrib.maskbit = is_mask_bit_support(control);
entry
->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry
->msi_attrib.pos = pos;

entry
->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64);
/* All MSIs are unmasked by default, Mask them all */
if (entry->msi_attrib.maskbit)
pci_read_config_dword(dev, entry
->mask_pos, &entry->masked);
mask
= msi_capable_mask(control);
msi_mask_irq(entry, mask, mask);

list_add_tail(
&entry->list, &dev->msi_list);

/* Configure MSI capability structure */
ret
= arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);
if (ret) {
msi_mask_irq(entry, mask,
~mask);
free_msi_irqs(dev);
return ret;
}

/* Set MSI enabled bits */
pci_intx_for_msi(dev,
0);
msi_set_enable(dev, pos,
1);
dev
->msi_enabled = 1;

dev
->irq = entry->irq;
return 0;
}
#ifndef arch_setup_msi_irqs
# define arch_setup_msi_irqs default_setup_msi_irqs
# define HAVE_DEFAULT_MSI_SETUP_IRQS
#endif

#ifdef HAVE_DEFAULT_MSI_SETUP_IRQS
int default_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct msi_desc *entry;
int ret;

/*
* If an architecture wants to support multiple MSI, it needs to
* override arch_setup_msi_irqs()
*/
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;

list_for_each_entry(entry,
&dev->msi_list, list) {
ret
= arch_setup_msi_irq(dev, entry);
if (ret < 0)
return ret;
if (ret > 0)
return -ENOSPC;
}

return 0;
}
#endif

此话If an architecture wants to support multiple MSI, it needs to override arch_setup_msi_irqs()表明为了支持MSI多中断不同的处理器平台可以重新实现arch_setup_msi_irqs()。

OK,下面来看X86处理器平台对于arch_setup_msi_irqs()的实现。

1、目录linux-2.6.38.6\arch\x86\include\asm\pci.h中

#define arch_setup_msi_irqs x86_setup_msi_irqs

/* MSI arch specific hooks */
static inline int x86_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
return x86_msi.setup_msi_irqs(dev, nvec, type);
}

2、目录linux-2.6.38.6\arch\x86\include\asm\x86_init.h

struct x86_msi_ops {
int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type);
void (*teardown_msi_irq)(unsigned int irq);
void (*teardown_msi_irqs)(struct pci_dev *dev);
};

extern struct x86_msi_ops x86_msi;

目录linux-2.6.38.6\arch\x86\include\asm\x86_init.c

struct x86_msi_ops x86_msi = {
.setup_msi_irqs
= native_setup_msi_irqs,
.teardown_msi_irq
= native_teardown_msi_irq,
.teardown_msi_irqs
= default_teardown_msi_irqs,
};

3、目录linux-2.6.38.6\arch\x86\kernel\apic\io_apic.c

int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
int node, ret, sub_handle, index = 0;
unsigned
int irq, irq_want;
struct msi_desc *msidesc;
struct intel_iommu *iommu = NULL;

/* x86 doesn't support multiple MSI yet */
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;

node
= dev_to_node(&dev->dev);
irq_want
= nr_irqs_gsi;
sub_handle
= 0;
list_for_each_entry(msidesc,
&dev->msi_list, list) {
irq
= create_irq_nr(irq_want, node);
if (irq == 0)
return -1;
irq_want
= irq + 1;
if (!intr_remapping_enabled)
goto no_ir;

if (!sub_handle) {
/*
* allocate the consecutive block of IRTE's
* for 'nvec'
*/
index
= msi_alloc_irte(dev, irq, nvec);
if (index < 0) {
ret
= index;
goto error;
}
}
else {
iommu
= map_dev_to_ir(dev);
if (!iommu) {
ret
= -ENOENT;
goto error;
}
/*
* setup the mapping between the irq and the IRTE
* base index, the sub_handle pointing to the
* appropriate interrupt remap table entry.
*/
set_irte_irq(irq, iommu, index, sub_handle);
}
no_ir:
ret
= setup_msi_irq(dev, msidesc, irq);
if (ret < 0)
goto error;
sub_handle
++;
}
return 0;

error:
destroy_irq(irq);
return ret;
}

  

  


  

posted on 2011-08-24 12:32  zzwworld  阅读(4065)  评论(0编辑  收藏  举报

导航