MSI中断与Linux

 

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中断的处理流程介绍完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值