xen-softirq

目的

主要了解一下xen项目中软中断是怎么使用,如何实现的。同时也对自己学习过程的一次记录。

学习链接

文章链接
中断子系统之softirqhttp://www.wowotech.net/irq_subsystem/soft-irq.html
Linux的中断处理机制 [四] - softirq(1)https://zhuanlan.zhihu.com/p/80371745
Linux的中断处理机制 [五] - softirq(2)https://zhuanlan.zhihu.com/p/80680484
Linux中断子系统(三)-softirq和tasklethttps://www.cnblogs.com/LoyenWang/p/13124803.html

xen代码

include/xen/softirq.h     // 数据结构
include/asm-arm/hardirq.h // 数据结构
include/xen/irq_cpustat.h // 数据结构
common/softirq.c          // softirq具体实现

前提

softir不支持动态分配,这和内核的原则是一致的,因为是静态分配,自然会优先是用数组管理所有的softirq实例,其关键的数据提描述如下,可以类比硬件中断去理解softirq。

数据结构

软中断类型

/*支持的软中断类型,逻辑上的软中断号,其中从上到下优先级递减 */
enum {
    TIMER_SOFTIRQ = 0,   /* timer 定时器软中断 优先级最高 */
    RCU_SOFTIRQ,         /* rcu 定时器软中断 */   
    SCHED_SLAVE_SOFTIRQ, /* 调度软中断 */
    SCHEDULE_SOFTIRQ,    /* 调度软中断 */
    NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQ, /* 没用上 */
    TASKLET_SOFTIRQ,    /* tasklet 软中断 */
    NR_COMMON_SOFTIRQS  /* 数组size */
};

软中断描述符表

typedef void (*softirq_handler)(void); /* handler函数指针 */

/* 软中断描述表,实际就是一个handler函数指针的数组
 * 维护的是软中断号与softirq handlers的关系表
 */
softirq_handler softirq_handlers[NR_SOFTIRQS];

根据实际的注册情况,软中断描述符的结果如下:

序号软中断号处理函数
0TIMER_SOFTIRQtimer_softirq_action
1RCU_SOFTIRQrcu_process_callbacks
2SCHED_SLAVE_SOFTIRQsched_slave
3SCHEDULE_SOFTIRQschedule
4NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQNULL
5TASKLET_SOFTIRQtasklet_softirq_action

软中断状态描述

类似于硬件中断,需要一些机制表示中断触发标志,软中断需要软件的一些变量去维护每个类型软中断触发的标志。

irq_cpustat_t irq_stat[NR_CPUS]

typedef struct {
        unsigned long __softirq_pending;
        unsigned int __local_irq_count;
} __cacheline_aligned irq_cpustat_t;
/*
 * 当某个软中断触发时, __softirq_pending会置位对应的bit.
 * 每cpu都会保存一份软中断的状态描述。在运行的时候cpu只会handler自己
 * __cacheline_aligned保证cache对齐,防止伪共享问题,提高性能
 */

通过以上对数据结构的描述,我们可以得出一些简单的结论。如下:

  1. softirq_handlers[]数组类似linux中的中断描述符表irq_desc[],通过软中号可以找到对应的handler进行处理,例如,通过SCHEDULE_SOFTIRQ可以查到schedule,并且执行schedule函数。
  2. 软中断可以在不同的cpu上并行运行(需防止并发),在同一个cpu上只能是串行运行的。
  3. 每个CPU维护irq_cpustat_t状态结构,当某个软中断需要进行处理,会在结构体的__softirq_pending置对应位(raise_softirq实现该功能)。

流程分析

在看文章的时候,知道一个软中断,需要关心如下几点:

  • 软中断注册
  • 软中断触发
  • 软中断执行

软中断注册

软中断通过open_softirq接口来注册,代码很简单。就是把软中断服务函数写到softirq_handlers[]软中断描述符表中,保存软中断号都服务函数的一一对应关系。

void open_softirq(int nr, softirq_handler handler)
{
    ASSERT(nr < NR_SOFTIRQS); /* 数组越界检查 */
    softirq_handlers[nr] = handler;
}

举个软中断注册的例子,来看看注册是有多简单。

void __init timer_init(void)
{
    ......
    open_softirq(TIMER_SOFTIRQ, timer_softirq_action);
    ......
}

软中断触发

软中断触发通过如下接口实现,根据接口名称都可以知道接口的具体区别如何。

raise_softirq

void raise_softirq(unsigned int nr)
{
    set_bit(nr, &softirq_pending(smp_processor_id()));
}

#define softirq_pending(cpu)	__IRQ_STAT((cpu), __softirq_pending)

#define __IRQ_STAT(cpu, member)	(irq_stat[cpu].member)
/*
 * softirq_pengding()拓展后的结果如下:
 * irq_stat[local_cpu].__softirq_pending
 * 符合之前的结论,触发的状态就是保存在irq_stat,每cpu都会保存
 */

cpu_raise_softirq

void cpu_raise_softirq(unsigned int cpu, unsigned int nr)
{
    // 1: 获取本地cpu,当前接口就有两个cpu id成员了
    unsigned int this_cpu = smp_processor_id();

	/* test_and_set_bit就是设置具体cpu上softirq的pending
     *
	 * 如下条件之一成立就不会往下执行
	 * a. cpu上已经存在softirq pending,
	 * b. 设置的cpu == local cpu
	 * c. arch_skip_send_event_check(cpu)为0,绝对不会成立
	 */
    if ( test_and_set_bit(nr, &softirq_pending(cpu))
         || (cpu == this_cpu)
         || arch_skip_send_event_check(cpu) )
        return;
    /*
     * batching不为空或者在硬中断时,触发核间中断:
     * send_SGI_mask(mask, GIC_SGI_EVENT_CHECK)
     * 核间中断的重要是要处理的核在WFI触发的低功耗阶段,需要使用中断来把它唤醒,再处理软中断
     *
     * 判断是false时,就是保存batch_mask
     */
    if ( !per_cpu(batching, this_cpu) || in_irq() )
        smp_send_event_check_cpu(cpu);
    else
        __cpumask_set_cpu(cpu, &per_cpu(batch_mask, this_cpu));
}
/*
 * 该接口看起来就是raise_softirq,只是可以设置所有cpu
 * 同时通过核间中断让cpu退出wfi触发的低功耗模式
 */

cpumask_raise_softirq

大致实现和cpu_raise_softirq类似,只是使用cpumask去配置对应cpu的softirq的pending。

软中断执行

执行入口

软中断执行的总入口为__do_softirq,由如下两个函数的调用

  • process_pending_softirqs
  • do_softirq

讨论的时候去掉了关于rcu的处理,后期在rcu的时候再统一讨论

static void __do_softirq(unsigned long ignore_mask)
{
    unsigned int i, cpu;
    unsigned long pending;

    for ( ; ; )
    {
        // 1: 获取本地cpu id,pengding的讨论都与cpu相关
        cpu = smp_processor_id(); 

        // 2: 一直循环检查是否还有pending,没就退出循环
        if ( ((pending = softirq_pending(cpu)) == 0)
             || cpu_is_offline(cpu) )
            break;
        // 3:查找pengding的中断号,从优先级高到低
        i = find_first_set_bit(pending);
        // 4: 处理handler前清掉pending,与硬件中断类似
        clear_bit(i, &softirq_pending(cpu)); 
        // 5: 执行软中断服务函数
        (*softirq_handlers[i])();
    }
}
/* 结论
 * 比linxu的处理逻辑要简单,没有很多的权衡代码
 * 处理时还需要照顾rcu,后期补上相关联的逻辑
 */

执行时机

我们已经讨论了软中断怎么去执行到每个被触发的softirq处理函数。那我们需要处理另外一个问题,__do_softirq的执行点在哪里。

主动调度
  1. idle_loop
异常退出
--> exit 
    --> leave_hypervisor_to_guest
        --> check_for_pcpu_work
            --> do_softirq()
<think>嗯,用户想加载xen-pciback模块,但遇到了问题。首先,我需要回忆一下Xen相关的知识。xen-pciback模块用于PCI设备的透传(Passthrough),需要正确配置才能使用。用户可能是在尝试设置PCI设备透传时遇到了模块加载的问题。 根据引用[1],用户之前可能已经配置了xenfs,这可能和Xen环境有关。但问题在于加载xen-pciback。首先应该确认模块是否存在。可能需要检查内核是否包含该模块。如果模块不存在,可能需要安装特定的内核模块或重新编译内核。 然后,用户可能没有正确指定模块参数。xen-pciback需要知道哪些PCI设备要隐藏,通常使用hide参数,比如`modprobe xen-pciback hide=(BDF)`。这里的BDF是设备的Bus、Device、Function编号,如引用[3]提到的。用户需要先找到设备的BDF,可以通过lspci命令查看。 另外,可能需要修改启动参数。比如在GRUB配置中添加`xen-pciback.hide`参数。这需要编辑/etc/default/grub文件,然后更新grub配置。 还有可能,当前内核没有包含xen-pciback模块,尤其是在使用domU内核时。引用[2]提到需要Xen pcifront驱动,可能用户的内核没有正确配置,需要确保内核编译时启用了CONFIG_XEN_PCIDEV_BACKEND选项。 还有,权限问题也可能导致加载失败,需要检查是否以root用户运行modprobe。另外,模块依赖是否满足,使用depmod和modprobe检查依赖关系。 如果所有方法都试过了还是不行,可能需要考虑升级Xen或内核版本,或者寻求社区支持。</think>### 如何加载 xen-pciback 模块 #### 步骤 1:确认模块存在性 首先检查当前内核是否包含 `xen-pciback` 模块: ```bash modinfo xen-pciback ``` 若输出模块信息(如版本、依赖项),则继续;若提示`未找到`,需安装含该模块的内核或重新编译内核[^2]。 --- #### 步骤 2:通过 modprobe 加载模块 **正确语法需指定 PCI 设备的 BDF 编号**(格式为 `DDDD:bb:dd.f`[^3]): ```bash modprobe xen-pciback hide=(BDF) # 示例:hide=0000:0a:00.0 ``` 需通过 `lspci` 获取目标设备的 BDF 值: ```bash lspci -nn | grep -i "设备名称" ``` --- #### 步骤 3:配置 GRUB 启动参数(持久化) 编辑 `/etc/default/grub`,在 `GRUB_CMDLINE_LINUX` 添加: ```bash xen-pciback.hide=(BDF) ``` 更新 GRUB 并重启: ```bash update-grub && reboot ``` --- #### 步骤 4:验证模块加载 重启后检查模块状态: ```bash lsmod | grep xen_pciback dmesg | grep pciback # 查看内核日志是否识别设备隔离 ``` --- #### 常见错误解决 1. **模块未找到** - 安装 `linux-image-xen` 或重新编译内核时启用 `CONFIG_XEN_PCIDEV_BACKEND` 选项[^2] 2. **参数格式错误** 确保 BDF 格式为 `xxxx:xx:xx.x`,可通过 `virsh nodedev-list` 交叉验证 3. **设备已被占用** 使用 `lspci -v` 检查设备当前驱动,并通过 `echo BDF > /sys/bus/pci/drivers/xxx/unbind` 解绑原驱动 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值