分类: LINUX
2014-01-16 15:19:25
1.什么是MSI中断
Message Signaled Interrupts 是pci2.2中提出来的一种新的中断形式。后续有msi-x扩展。
msi以及msi-x这种中断形式的一个最主要的特点就是,它在系统的特定地址做一个memory write transaction, 将一个系统约定的数据写入,以此通知CPU一个中断产生了。这个特点带来的最主要的好处就是脱离了传统的interrupt pin的约束,中断的数目也不再受到限制。
2.PCI规范中的MSI设计
msi以及msi-x的相关数据作为pci配置空间的一个capability structure来实现的。
msi的capability structure比较简单。
这是一个最简单的32为message 地址以及16位message 数据的msi capability structure。根据message control中的标记,地址可以是64位,还可能存在masking/pending域,用于对msi中断的屏蔽进行管理。
msi-x为了扩展支持更多的中断向量,其capability structure包含的是中断向量表的地址(由设备的bar来确定)。
(address和data的格式根据不同的体系结构有不同的实现方式。最简单的实现就可以是address就是一个固定的地址,data就是为其分配的中断向量号,这样root complex在发现对这个address写了数据,就会通知CPU对应的msi中断到了。当然,我们也可以细分这些数据结构,达到中断检查等目的)
3.Linux 2.6.26中的msi中断处理
以X86为例。
一般的流程是,设备驱动里面检查自己是否具有msi或者msi能力,如果有的话,调用driver/pci/msi.c中的pci_enable_msi或者pci_enable_msix。
这里以msi为例,msix稍微复杂一些,但原理类似。
--------------------------------------------------------------------------------
int pci_enable_msi(struct pci_dev* dev)
{
int status;
status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
if (status)
return status;
WARN_ON(!!dev->msi_enabled);
/* Check whether driver already requested for MSI-X irqs */
if (dev->msix_enabled) {
printk(KERN_INFO "PCI: %s: Can't enable MSI. "
"Device already has MSI-X enabled\n",
pci_name(dev));
return -EINVAL;
}
status = msi_capability_init(dev);
return status;
--------------------------------------------------------------------------------
开始时检查,重点看msi_capability_init
--------------------------------------------------------------------------------
static int msi_capability_init(struct pci_dev *dev)
{
struct msi_desc *entry;
int pos, ret;
u16 control;
msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
pci_read_config_word(dev, msi_control_reg(pos), &control);
/* MSI Entry Initialization */
entry = alloc_msi_entry();
if (!entry)
return -ENOMEM;
entry->msi_attrib.type = PCI_CAP_ID_MSI;
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.masked = 1;
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry->msi_attrib.pos = pos;
if (is_mask_bit_support(control)) {
entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos,
is_64bit_address(control));
}
entry->dev = dev;
if (entry->msi_attrib.maskbit) {
unsigned int maskbits, temp;
/* All MSIs are unmasked by default, Mask them all */
pci_read_config_dword(dev,
msi_mask_bits_reg(pos, is_64bit_address(control)),
&maskbits);
temp = (1 << multi_msi_capable(control));
temp = ((temp - 1) & ~temp);
maskbits |= temp;
pci_write_config_dword(dev,
msi_mask_bits_reg(pos, is_64bit_address(control)),
maskbits);
entry->msi_attrib.maskbits_mask = temp;
}
list_add_tail(&entry->list, &dev->msi_list);
/* Configure MSI capability structure */
ret = arch_setup_msi_irqs(dev, 1, PCI_CAP_ID_MSI);
if (ret) {
msi_free_irqs(dev);
return ret;
}
/* Set MSI enabled bits */
pci_intx_for_msi(dev, 0);
msi_set_enable(dev, 1);
dev->msi_enabled = 1;
dev->irq = entry->irq;
return 0;
}
--------------------------------------------------------------------------------
首先根据message control中的一些标记填写 msi_desc 数据结构。最重要的两个数据结构 address 和data是在arch_setup_msi_irqs中填充的。
对应到x86架构中,这个函数在arch/x86/kernel/io_apic_32.c中。
arch_setup_msi_irq
--------------------------------------------------------------------------------
int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
struct msi_msg msg;
int irq, ret;
irq = create_irq();
if (irq < 0)
return irq;
ret = msi_compose_msg(dev, irq, &msg);
if (ret < 0) {
destroy_irq(irq);
return ret;
}
set_irq_msi(irq, desc);
write_msi_msg(irq, &msg);
set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq,
"edge");
return 0;
}
--------------------------------------------------------------------------------
首先申请一个设备终端号---create_irq();
然后根据设备终端号构建了data—— msi_compose_msg
看看这个函数里面,可以看到x86下实现的一些属性。
msg->address_hi = MSI_ADDR_BASE_HI;
msg->address_lo = MSI_ADDR_BASE_LO | ((INT_DEST_MODE == 0) ?MSI_ADDR_DEST_MODE_PHYSICAL:
MSI_ADDR_DEST_MODE_LOGICAL) |((INT_DELIVERY_MODE != dest_LowestPrio) ?
MSI_ADDR_REDIRECTION_CPU:MSI_ADDR_REDIRECTION_LOWPRI) |MSI_ADDR_DEST_ID(dest);
msg->data = MSI_DATA_TRIGGER_EDGE | MSI_DATA_LEVEL_ASSERT | ((INT_DELIVERY_MODE !=
dest_LowestPrio) ?MSI_DATA_DELIVERY_FIXED:MSI_DATA_DELIVERY_LOWPRI)
|MSI_DATA_VECTOR (vector);
不同架构下可能上述的赋值是不一样的。主要的不同体现在属性上。
然后写到相关的数据结构中。
set_irq_msi
write_msi_msg
set_irq_chip_and_handler_name//这里会提供一个入口,在中断的总入口之后,msi设备中断会进入handle_edge_irq。
至此,msi中断的处理流程介绍完毕。