内核协议栈源码阅读(一) --- rx方向驱动与内核交互

发布于:2025-07-26 ⋅ 阅读:(16) ⋅ 点赞:(0)


以 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()

网站公告

今日签到

点亮在社区的每一天
去签到