文章目录
以 e100_intr 为例:
一、硬中断
1.1 e100_intr
网卡触发中断,os 进入中断处理程序 e100_intr
,该函数通过 request_irq
注册到中断处理系统中。
static irqreturn_t e100_intr(int irq, void *dev_id)
{
struct net_device *netdev = dev_id;
struct nic *nic = netdev_priv(netdev);
u8 stat_ack = readb(&nic->csr->scb.stat_ack);
DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);
if(stat_ack == stat_ack_not_ours || /* Not our interrupt */
stat_ack == stat_ack_not_present) /* Hardware is ejected */
return IRQ_NONE;
/* Ack interrupt(s) */
writeb(stat_ack, &nic->csr->scb.stat_ack);
/* We hit Receive No Resource (RNR); restart RU after cleaning */
if(stat_ack & stat_ack_rnr)
nic->ru_running = RU_SUSPENDED;
if(likely(netif_rx_schedule_prep(netdev))) {
e100_disable_irq(nic);
__netif_rx_schedule(netdev);
}
return IRQ_HANDLED;
}
- 首先对硬中断进行检查,即是否由网络设备激活等
netif_rx_schedule_prep
设置网卡 state 的RX_SCHED
标志。如果设置成功,则e100_disable_irq(dev)
关闭网络设备的硬中断 (即当前设备再收到包也不会触发中断信号,区别于关闭 cpu 的中断响应。关闭后会在驱动的 poll 函数中重新打开,表示驱动完整的收完一轮包),防止网卡触发大量硬中断。如果RX_SCHED
已经设置过了,则直接返回,因为设备已经加入poll_list
且触发过软中断了),然后__netif_rx_schedule
触发软中断。
1.2 __netif_rx_schedule
__netif_rx_schedule
void __netif_rx_schedule(struct net_device *dev)
{
unsigned long flags;
local_irq_save(flags); // 保存并关闭 cpu 中断响应
dev_hold(dev);
list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
if (dev->quota < 0)
dev->quota += dev->weight;
else
dev->quota = dev->weight;
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
local_irq_restore(flags);
}
EXPORT_SYMBOL(__netif_rx_schedule);
- 保存当前 cpu 的中断响应状态并关闭 cpu 的中断响应(有可能在中断处理中递归被中断,所以这里不能简单的关闭中断或者开启中断,而是保存状态并关闭:
local_irq_save(flags)
) - 将 dev 插入到当前 cpu 的
softnet_data
的 poll_list 等待软中断处理(list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
)- 这里
softnet_data
是 cpu 级别的数据结构,用来记录有哪些网卡设备将中断捅给了当前 cpu,或者当前 cpu 有哪些包需要发给哪些网卡
- 这里
- 为
dev->quota
赋值,即分配 napi 可收取的包数 - 发起软中断(
__raise_softirq_irqoff(NET_RX_SOFTIRQ)
) - 恢复 cpu 中断响应状态(
local_irq_restore(flags);
)
content_copy
1.3 补充:
local_irq_save(flags)
函数实现:
// 保存 cpu 中断响应标志的函数,保存的同时会关闭 cpu 的中断响应
#define local_irq_save(flags) \
do {
raw_local_irq_save(flags); trace_hardirqs_off(); } while (0)
// include/asm-x86_64/irqflags.h
#define raw_local_irq_save(flags) \
do {
(flags) = __raw_local_irq_save(); } while (0)
static inline unsigned long __raw_local_irq_save(void)
{
unsigned long flags = __raw_local_save_flags();
raw_local_irq_disable()