查看: 82|回复: 0

深入剖析Linux内核网络收包角度—浅入中断(2)

[复制链接]

5

主题

8

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2023-6-24 17:50:25 | 显示全部楼层 |阅读模式
上文深入剖析Linux内核网络收包角度——浅入中断(1)主要从针对e1000网卡收包开始,分析了硬中断注册、处理过程,网卡收包后硬中断的过程非常简洁,处理过程比较简单,主要是将网卡的napi->poll_list加入到CPU的softnet_data->poll_list里,然后发起软中断,就结束了,下面的就交给软中断进行数据包的接收处理,所以收包任务最大程度地交给软中断处理,最大程度简化硬中断处理。总结以上描述中,上篇博文的具体细节步骤:
字节终面:CPU 是如何读写内存的?
全网最牛Linux内核分析--Intel CPU体系结构
一文让你读懂Linux五大模块内核源码,内核整体架构设计(超详细)
嵌入式前景真的好吗?那有点悬!
一文教你如何使用GDB+Qemu调试Linux内核
Linux内核必读五本书籍(强烈推荐)
全网独一无二Linux内核Makefle系统文件详解(一)(纯文字代码)
带你深度了解Linux内核架构和工作原理!
如何读懂GDB底层实现原理(从这几点入手~)
一文彻底理解Memory barrier(内存屏障)
一篇文带你搞懂,虚拟内存、内存分页、分段、段页式内存管理(超详细)


  • 内核启动网卡,为网卡分配(Ring Buffer,内核通过网卡驱动将DMA内存地址信息写入网卡寄存器,使得网卡获得DMA内存信息),并通过request_irq 内核 APi注册硬中断服务例程e1000_intr
  • 网卡收到数据包,通过DMA将数据包写入内存
  • 网卡触发硬中断,通过CPU接收数据
  • CPU中断当前的进程,跳转到异常向量表的中断异常向量处
  • 保存现场
  • 调用irq_handler
  • 对于ARM64,irq_handler将调用gic_handle_irq
  • gic_handle_irq首先读取中断寄存器得到硬件中断号,调用handle_domain_irq函数
  • handle_domain_irq->irq_enter进入中断上下文
  • handle_domain_irq->irq_find_mapping通过硬件中断号获取IRQ Number
  • handle_domain_irq->generic_handle_irq进入中断通用层处理
  • generic_handle_irq->irq_to_desc通过IRQ Number获取对应的中断描述符
  • generic_handle_irq->generic_handle_irq_desc->__handle_irq_event_percpu:遍历中断描述符中的action链表,依次执行每个action中回调函数action->handler,对应e1000网卡驱动,执行e1000_intr中断服务例程
  • e1000_intr->ew32(IMC, ~0)禁止网卡中断,避免频繁硬中断,降低内核的工作效率
  • e1000_intr->napi_schedule激活NAPI, napi_struct.poll_list 挂在 softnet_data.poll_list 上,方便后面软中断调用 napi_struct.poll 获取网卡数据。然后设置NET_RX_SOFTIRQ 软中断标识位。
  • handle_domain_irq->irq_exit():退出中断上下文,触发软中断执行收包流程。
  • 恢复现场
上文深入剖析Linux内核网络收包角度——浅入中断(1)重点在于通过网卡收包分析了硬中断,本文从网络收包角度分析和学习软中断过程。软中断的种类、定义(Kernel 4.15)
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the
       numbering. Sigh! */
RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

NR_SOFTIRQS
}
以上是内核默认定义的几种软中断,硬中断是在CPU每个指令周期后,会去判断是否有硬中断产生,根据硬中断号找到对应的IRQ Number,执行对应的中断服务例程,而软中断会有内核线程,轮询一组标志位,如果标志位有值,那去这个标志位对应的软中断向量表,找到中断处理函数执行。
软中断的注册全局的软中断向量数组,即软中断描述符表
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

const char * const softirq_to_name[NR_SOFTIRQS] = {
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
"TASKLET", "SCHED", "HRTIMER", "RCU"
}
数组中的内容就是软中断触发时的钩子函数:

struct softirq_action
{
void (*action)(struct softirq_action *);
}
如网络收发包的软中断处理函数在网络子系统初始化时注册的:
static int __init net_dev_init(void)
{
  ......
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
  ......
}注册软中断回调函数:
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!




点击报名免费内核学习直播课程:


软中断在内核中的整体如下所示,pending为软中断标志位



其中标志位在内核中使用irq_cpustat来表示:

typedef struct {
unsigned int __softirq_pending;
#ifdef CONFIG_SMP
unsigned int ipi_irqs[NR_IPI];
#endif
} ____cacheline_aligned irq_cpustat_t;
}其中__softirq_pending为软中断标志位,该类型为int类型,最多支持32位软中断,ipi_rrq位cpu与cpu之间的中断。
在Linux内核中,定义NR_CPUS个该结构,即每个CPU有一个32bit的位图,来维护本cpu上的软中断是否激活
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;软中断的激活在上文深入剖析Linux内核网络收包角度——浅入中断(1)中,硬中断的中断服务例程e1000_intr执行:
e1000_intr->__napi_schedule:
void __napi_schedule(struct napi_struct *n)
{
unsigned long flags;

local_irq_save(flags);//禁用中断
____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);//恢复中断
}__napi_schedule(this_cpu_ptr(&softnet_data),n):
static inline void ____napi_schedule(struct softnet_data *sd,
         struct napi_struct *napi)
{
list_add_tail(&napi->poll_list, &sd->poll_list);
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}最终调用__raise_soft_irqoff设置软中断标志位,nr为NET_RX_SOFTIRQ
void __raise_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);
}
可以看到在执行__raise_soft_irqoff函数前,在__napi_schedule中,先是通过local_irq_save(flags)禁用了中断,因为置位位图是一个竞争操作,所有硬中断都可以做,所以要保证关中断的情况下完成,等pending置位结束后调用local_irq_restore(flags)恢复中断。内核线程ksoftirqd是如何监测到软中断发生的:
static void run_ksoftirqd(unsigned int cpu)
{
local_irq_disable();
if (local_softirq_pending()) {
  /*
   * We can safely run softirq on inline stack, as we are not deep
   * in the task stack here.
   */
  __do_softirq();
  local_irq_enable();
  cond_resched_rcu_qs();
  return;
}
local_irq_enable();
}上面函数先关中断查看本cpu的pending置位情况,如果有则进行进一步软中断处理。
软中断的入口函数__do_softirq:
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
  ......
pending = local_softirq_pending();  //将32位的pending集合取出来
account_irq_enter_time(current);

__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
in_hardirq = lockdep_softirq_start();

restart:

set_softirq_pending(0);//pending清空

local_irq_enable();  //使能中断

h = softirq_vec; //软中断处理向量表,里面是软中断处理函数指针

while ((softirq_bit = ffs(pending))) { //获取最高位置1的位数
  unsigned int vec_nr;
  int prev_count;

  h += softirq_bit - 1;

  vec_nr = h - softirq_vec;//指针直接相减,得到软件中断号
  prev_count = preempt_count();

  kstat_incr_softirqs_this_cpu(vec_nr);//对当前处理的软中断进行统计基数

  trace_softirq_entry(vec_nr);
  h->action(h);  //回调中断处理函数
  trace_softirq_exit(vec_nr);
  if (unlikely(prev_count != preempt_count())) {
   pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
          vec_nr, softirq_to_name[vec_nr], h->action,
          prev_count, preempt_count());
   preempt_count_set(prev_count);
  }
  h++;
  pending >>= softirq_bit;
}

rcu_bh_qs();
local_irq_disable();//关中断

pending = local_softirq_pending(); //重新获取pending集合(开中断到关中断的过程中可能有新的硬中断到来,这时候需要重新处理一遍)
if (pending) {  //如果pengding中又有软中断置位
  if (time_before(jiffies, end) && !need_resched() &&
      --max_restart)
   goto restart;

  wakeup_softirqd(); //唤醒软中断处理线程处理
}

lockdep_softirq_end(in_hardirq);
account_irq_exit_time(current);
__local_bh_enable(SOFTIRQ_OFFSET);
WARN_ON_ONCE(in_interrupt());
current_restore_flags(old_flags, PF_MEMALLOC);
}对于数据包接收h->action()将执行:网络子系统注册的的回调函数net_rx_action:
static int __init net_dev_init(void)
{
  ......
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
  ......
}关于net_rx_action涉及到软中断处理中进一步使用NAPI机制收包,在接下来的文章中进行分析和学习。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表