在读e1000代码的时候,分析到中断处理函数。对于NAPI模式跟踪发现,只有在poll函数末尾开中断,但是没有关中断的地方,看注释发现有一个叫做interrupt auto-mask的东西,查了一下intel的手册,分析了一下。
了解几个寄存器的作用:
Interrupt Cause Read Register
ICR (000C0H; R)
这个寄存器包括以太网控制器中所有的中断条件。每次一个中断事件发生的时候,寄存器中相应的中断位(0-19)就会被设置。当每次寄存器中的中断位被设置后,就会生成一个PCIe*中断,
当然需要通过IMS和ITR寄存器的允许才可以发生。
每次中断事件发生后,所有定时器延迟中断被清除,并且他们的事件被设置在ICR寄存器中。
ICR寄存器的读倒错,会有下面的几种可能:
1.中断屏蔽寄存器(IMS)=0000h(全部屏蔽)
ICR寄存器的内容被清空。
2.中断断言(ICR.INT_ASSERTED = 1b)
ICR寄存器内容被清空,Auto-Mask被激活(IAM寄存器被写到IMC寄存器)。
3.中断没有断言(ICR.INT_ASSERTED = 0b)
ICR寄存器不会被清空
向这个寄存器中的任何一位写1,都会清空这个位;向任何一位写0对这一位没有任何影响。INT_ASSERTED位是一个特例,向它写1或0都没有影响。只有在所有的中断源都被清空之后,它才会被清空。
当CPU向量被清空后,第7,16,17位也会被清空。
注意:如果IMS=0b,ICR寄存器总是在读取后自动清除。如果IMS没有被设置为0b,但是一些ICR位被设置但是对应的IMS位没有设置的情况,读取操作不会清空ICR寄存器。
例如,IMS=10101010b ICR=01010101b,读ICR寄存器不会清空它。
IMS=10101010b ICR=0101011b,读ICR寄存器就会整个的清空它(ICR.INT_ASSERTED=1b)。
INT_ASSERTED:Interrupt Asserted
中断断言位:当网络接口上有一个等待的中断,这个位就会被设置。如果在PCIe*的配置空间里面中断被允许的话,发生一个中断。
Interrupt Mask Set/Read Register
IMS (000D0h; R/W)
当相应的位被设置为1的时候,一个中断被允许;同理设置为0,将会屏蔽一个中断。当这个寄存器中的一个位被设置,并且在相应的中断条件发生的时候,就会产生一个PCIe*中断。中断条件的发生通过设置ICR中相应位。
通过向这个寄存器相应屏蔽位写1来允许一个中断。写0将不会变化。因此,如果想屏蔽一个刚刚允许的中断,需要向IMC寄存器相应位写1,而不是向这个这个寄存器写0。
通过读这个寄存器,可以返回已经设置的中断屏蔽位。
Interrupt Mask Clear Register
IMC (000D8h; W)
可以使用这个寄存器来屏蔽一个中断。中断只有当mask位设置为1并且cause位设置为1的时候,才会提交到总线接口上。mask位在IMS中设置,cause位在ICR中设置。
可以通过清除对应的mask位的方法来屏蔽中断。这可以通过向这个寄存器相应的位写1来实现。如果写0的话,mask位不会发生变化。
为了确保兼容性,我们应当向保留的位也写1。因为当向对应的位写1可以屏蔽中断,向保留位也写1可以确保软件不响应中断。
Interrupt Acknowledge Auto Mask Register
IAM (000E0h; R/W)
每次当CTRL_EXT.IAME被设置,并且ICR.INT_ASSERT为1时,ICR的访问事件会有一个副作用,就是将IAM寄存器的值写到IMC寄存器中。
分析e1000-7.6.5代码:
在e1000_probe中:
/* Hardware features, flags and workarounds */
if (adapter->hw.mac.type >= e1000_82571) {
adapter->flags.int_assert_auto_mask = 1;
#ifdef CONFIG_PCI_MSI
adapter->flags.has_msi = 1;
#endif
adapter->flags.has_manc2h = 1;
}
在e1000_configure_rx中:
if (hw->mac.type >= e1000_82571) {
ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
/* Reset delay timers after every interrupt */
ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR;
#ifdef CONFIG_E1000_NAPI /*设置Auto-Mask*/
/* Auto-Mask interrupts upon ICR access */
ctrl_ext |= E1000_CTRL_EXT_IAME;
E1000_WRITE_REG(hw, E1000_IAM, 0xffffffff);
#endif
E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
E1000_WRITE_FLUSH(hw);
}
在e1000_intr中:
#ifdef CONFIG_E1000_NAPI
/* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
* not set, then the adapter didn't send an interrupt */
if (adapter->flags.int_assert_auto_mask &&
!(icr & E1000_ICR_INT_ASSERTED))
return IRQ_NONE; /*这里出现的这种情况,可能是共享中断导致的,一个非网卡中断进入了这个中断处理函数*/
/* Interrupt Auto-Mask...upon reading ICR,
* interrupts are masked. No need for the
* IMC write, but it does mean we should
* account for it ASAP. */
if (likely(hw->mac.type >= e1000_82571))
atomic_inc(&adapter->irq_sem);
#endif
......
#ifdef CONFIG_E1000_NAPI
if (hw->mac.type < e1000_82571) {
/* disable interrupts, without the synchronize_irq bit */
atomic_inc(&adapter->irq_sem);
E1000_WRITE_REG(hw, E1000_IMC, ~0);
E1000_WRITE_FLUSH(hw);
}
if (likely(netif_rx_schedule_prep(netdev))) {
adapter->total_tx_bytes = 0;
adapter->total_tx_packets = 0;
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
__netif_rx_schedule(netdev);
} else {
atomic_dec(&adapter->irq_sem);
}
#else
e1000驱动的poll函数e1000_clean:
e1000_clean:
if ((tx_clean_complete && (work_done == 0)) ||
!netif_running(poll_dev)) {
quit_polling:
if (likely(adapter->itr_setting & 3))
e1000_set_itr(adapter);
netif_rx_complete(poll_dev);
if (test_bit(__E1000_DOWN, &adapter->state))
atomic_dec(&adapter->irq_sem);
else
e1000_irq_enable(adapter);
return 0;
}
分析e1000的interrupt auto-mask只有在NAPI的时候开启(82571之后的芯片才支持)。
NAPI下auto-mask流程:
1.将CTRL_EXT.IAME置为1,启动Auto-Mask功能。
2.设置IAM寄存器值为0xffffffff。
3.接受到数据包,发生中断后,会设置ICR.INT_ASSERTED位。
4.如果ICR.INT_ASSERTED位为1,将IAM寄存器的值赋给IMC寄存器。在这里,就是将0xffffffff赋给IMC寄存器,也就是起到了关中断的作用。
5.在NAPI的poll函数末尾,需要开中断,调用e1000_irq_enable,设置IMS寄存器。
注意:
这种寄存器一读取就清空的策略,对于软件设计人员来将感觉很不适应。
通过使用"set"和"clear-on-read"方式,而不是"read-modify-write",来进行线程安全("thread-safe")的访问。
==========
另外还有两个中断相关的寄存器:
Interrupt Throttling Rate
ITR (000C4h; R/W)
中断上限速率寄存器:
最小内部中断间隔,间隔是以256ns进行步进的。设为0将禁止中断上限速率逻辑。
中断速率与间隔值之间的公式:
interrupts/second = (256×10^(-9) × interval)^(-1)
同理,我们也可以得出间隔值与中断速率之间的公式:
inter-interrupt interval = (256×10^(–9) × interrupts/sec)^(-1)
例如,中断间隔被设置为500d,那么以太网控制器能够保证CPU在128us内不会再被网卡中断;最大中断速率不超过7813interrupts/secon。
这个寄存器的设定由各个系统而定,推荐设定范围为651-5580 (28Bh - 15CCh).
Interrupt Cause Set Register
ICS (000C8h; W)
中断设置寄存器
软件可以设置这个寄存器来引起相应的中断。这个寄存器上设置相应的位为1,就触发相应的中断。结果就是在ICR寄存器中相应的位也被设置了(相应的中断发生了)。如果IMS寄存器允许,将会产生一个PCIe*中断。
写0不产生任何变化。
阅读(2672) | 评论(4) | 转发(0) |