linux中的MSI中断。。其实真相是个悲剧

前边说到想用MSI,因为MSI能够提供多样化的中断信息,也就是说一个中断号,却能传递多种信息。这样美好的特性当然是大家所向往的。

可惜,上周看MSI用法的时候,“最终知道真相的我眼泪掉下来”。。T T,真相就是目前linux的内核不支持这种多重MSI(Mutiple MSI)!也就是说MSI使用起来在效果上和传统的irq没区别,都是只能处理一种中断。

看看代码里怎么说。在msi-HOWTO.txt里提到了,要使用msi机制,需要先调用pci_enable_msi接口。这个接口在pci.h中定义

 
  
#define pci_enable_msi(pdev) pci_enable_msi_block(pdev, 1)

这里看到,实际上它调用了pci_enable_msi_block接口。同样在msi-HOWTO中也提到了这个函数,有如下说明:

 
  
int pci_enable_msi_block( struct pci_dev * dev, int count)

This variation on the above call allows a device driver to request multiple
MSIs. The MSI specification only allows interrupts to be allocated
in
powers of two, up to a maximum of
2 ^ 5 ( 32 ).

这里提到了,这个函数其实是用来申请mutiple MSIs的。当时看到这里的时候,我真心想,我的病有救了啊!于是继续深入,位于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;
}

互略前边设备检查的部分,重点就在于 status = msi_capability_init(dev, nvec);这一句。

这个函数做了什么呢。。

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

可以看到,主要还是先分配和初始化了一些数据结构,并添加到内核维护的链表上。

狗血的高潮在于 ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI);

字面来看这个函数应该是根据特定architecture建立起msi中断,应该已经接近这番探索的终点了。而事实上也确实是这样。。

代码如下:

 
  
#ifndef arch_setup_msi_irqs
int arch_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
看到了吗。。。。当nvec>1时候直接就return 1.。。。解释就是如果想支持mutiple MSI,请客观您自行重载该函数。。。

转载于:https://www.cnblogs.com/garychen2272/archive/2011/03/05/1971464.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值