You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在QEMU中测试SHPC驱动的request_threaded_irq修改?

如何在QEMU中测试SHPC驱动的request_threaded_irq修改?

看起来你已经顺利完成了SHPC驱动的中断处理改造——把原来的request_irq()换成request_threaded_irq(),还把单函数拆成了中断上下文的shpc_isr()和线程上下文的shpc_ist(),这个思路完全符合内核中断处理的最佳实践!不过测试这个修改确实有点头疼,因为QEMU对SHPC的模拟支持确实有限。我先理下你当前的处境,再给你几个可行的测试方案:

你的修改回顾

先明确下你的核心改动(方便其他读者理解):你把原来的单中断处理函数拆成了两个职责明确的部分:

  • shpc_isr():在中断上下文快速执行,只做基础检查(比如判断是否忽略热插拔),然后返回IRQ_WAKE_THREAD唤醒处理线程
  • shpc_ist():在线程上下文执行完整的热插拔逻辑,避免长时间占用中断上下文影响系统响应

你的完整diff如下:

diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 012b9e3fe5b0..b82d2bc4b777 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -166,6 +166,8 @@
 #define SLOT_SERR_INT_MASK 0x3
 
 static irqreturn_t shpc_isr(int irq, void *dev_id);
+static irqreturn_t shpc_ist(int irq, void *dev_id);
+
 static void start_int_poll_timer(struct controller *ctrl, int sec);
 
 static inline u8 shpc_readb(struct controller *ctrl, int reg)
@@ -746,7 +748,7 @@ int shpchp_set_bus_speed_mode(struct slot *slot, enum pci_bus_speed value)
    return retval;
 }
 
-static irqreturn_t shpc_isr(int irq, void *dev_id)
+static irqreturn_t shpc_ist(int irq, void *dev_id)
 {
    struct controller *ctrl = (struct controller *)dev_id;
    u32 serr_int, slot_reg, intr_loc, intr_loc2;
@@ -825,6 +827,21 @@ static irqreturn_t shpc_isr(int irq, void *dev_id)
    return IRQ_HANDLED;
 }
 
+static irqreturn_t shpc_isr(int irq, void *dev_id)
+{
+   pr_info("%s: isr for interrupt handler\n", __func__);
+
+   struct controller *ctrl = dev_id;
+   struct pci_dev *pdev = ctrl->pci_dev;
+
+   if (pdev->ignore_hotplug) {
+       pr_info("ignoring hotplug");
+       return IRQ_HANDLED;
+   }
+
+   return IRQ_WAKE_THREAD;
+}
+
 static int shpc_get_max_bus_speed(struct controller *ctrl)
 {
    int retval = 0;
@@ -1001,8 +1018,9 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
            pci_set_master(pdev);
        }
 
-       rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
-                MY_NAME, (void *)ctrl);
+       rc = request_threaded_irq(ctrl->pci_dev->irq, shpc_isr, shpc_ist,
+               IRQF_SHARED, MY_NAME, (void *)ctrl);
+
        ctrl_dbg(ctrl, "request_irq %d (returns %d)\n",
             ctrl->pci_dev->irq, rc);
        if (rc) {

要应用这个diff,你可以用:

git apply <path-to-diff-file>

你遇到的测试困境

你已经尝试了几乎所有常规的虚拟化测试路径:

  • 用-device pci-bridge,shpc=on创建SHPC桥,但它是根桥,不接管热插拔控制,没法触发中断
  • 尝试在QEMU monitor里添加二级桥、e1000等设备,要么不支持SHPC,要么还是没法触发中断
  • 物理机(Dell Latitude E7450)也没有SHPC兼容设备
  • 其他虚拟化方案(Libvirt、VMware、VirtualBox)也没有合适的支持

可行的测试方案

1. 手动触发中断逻辑(最快验证)

既然没法用模拟设备触发,咱们可以直接在代码里或用户空间手动调用处理函数:

  • 代码内强制触发:在shpc_init()成功注册中断后,直接调用shpc_isr()和shpc_ist(),验证逻辑是否正常执行。比如:
    // 在shpc_init()中request_threaded_irq成功后添加
    if (!rc) {
        pr_emerg("Manually triggering SHPC ISR and IST for testing\n");
        // 触发ISR
        shpc_isr(ctrl->pci_dev->irq, ctrl);
        // 直接调用IST验证逻辑
        shpc_ist(ctrl->pci_dev->irq, ctrl);
    }
    
    启动内核后,你就能看到自己加的日志,确认两个函数都能正常运行,变量访问、逻辑分支都没有问题。
  • 用debugfs触发:给驱动加一个debugfs节点,用户空间写入命令时触发线程。比如:
    // 在驱动初始化时注册debugfs文件
    static struct dentry *shpc_debug_dir;
    static ssize_t shpc_trigger_ist(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    {
        struct controller *ctrl = file->private_data;
        shpc_ist(ctrl->pci_dev->irq, ctrl);
        return count;
    }
    static const struct file_operations shpc_trigger_fops = {
        .write = shpc_trigger_ist,
        .open = simple_open,
        .llseek = default_llseek,
    };
    // 在shpc_init()中添加
    shpc_debug_dir = debugfs_create_dir("shpc", NULL);
    debugfs_create_file("trigger_ist", 0200, shpc_debug_dir, ctrl, &shpc_trigger_fops);
    
    启动内核后,执行echo 1 > /sys/kernel/debug/shpc/trigger_ist就能触发shpc_ist()。

2. 验证中断线程是否正确创建

即使没法触发真实中断,你可以先确认线程是否成功注册:

  • 查看/proc/interrupts:找到对应irq号的行,后面会显示线程名称(就是你用的MY_NAME)
  • 用ps aux | grep shpchp:能看到对应的中断线程进程

3. 改造QEMU模拟SHPC中断(进阶)

如果想模拟真实的硬件触发,你可以修改QEMU的代码:

  • 找到QEMU的hw/pci-bridge/shpc.c文件,添加一个monitor命令(比如shpc-trigger-irq)
  • 在命令处理函数中,模拟SHPC控制器产生中断,这样内核就能收到中断并调用你的shpc_isr()

4. 寻找真实SHPC硬件

如果有条件,老的服务器级机器(比如HP ProLiant、Dell PowerEdge的老型号)通常带SHPC兼容的PCI扩展槽,插/拔设备就能触发真实的热插拔中断,完美验证你的修改。

5. 用内核irq_sim框架(内核5.11+)

内核的irq_sim框架专门用于模拟中断,适合驱动测试:

  • 初始化一个irq_sim实例,申请模拟irq号
  • 把模拟irq号传给request_threaded_irq()
  • 用irq_sim_fire()触发中断,就能完整模拟从硬件中断到线程处理的流程

备注:内容来源于stack exchange,提问作者Guilherme Giácomo Simoes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 09:42:59