Linux2.6.30核心版本開始,Linux增加了對於請求多個MSI中斷向量號的支持。

下面的代碼來自Linux2.6.38核心。(不同的cpuMSI多中斷的支持不盡相同,通過下面對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.
 */
staticint 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;
    return0;
}

 

 

#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)
        return1;

    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;
    }

    return0;
}
#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);
};

externstruct 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)
        return1;

    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++;
    }
    return0;

error:
    destroy_irq(irq);
    return ret;
}

 

  

 

http://www.cnblogs.com/TheGrandDesign/archive/2011/08/24/2151823.html

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()