Skip to content

Linux Kernel KVM 中断与设备模拟机制分析

目录

  1. IRQ Chip 模拟
  2. Local APIC (LAPIC)
  3. IOAPIC
  4. Eventfd 集成
  5. VFIO 集成
  6. 中断流程图

1. IRQ Chip 模拟

1.1 核心数据结构

struct kvm_kernel_irq_routing_entry (include/linux/kvm_host.h:666)

c
struct kvm_kernel_irq_routing_entry {
    u32 gsi;                    // Global System Interrupt number
    u32 type;                   // 路由类型
    int (*set)(...);            // IRQ 设置回调函数
    union {
        struct {
            unsigned irqchip;   // IRQ 控制器编号
            unsigned pin;       // 引脚编号
        } irqchip;
        struct {
            u32 address_lo;
            u32 address_hi;
            u32 data;
            u32 flags;
            u32 devid;
        } msi;                  // MSI 消息
        struct kvm_hv_sint hv_sint;
        struct kvm_xen_evtchn xen_evtchn;
    };
    struct hlist_node link;
};

struct kvm_irq_routing_table (include/linux/kvm_host.h:692)

c
struct kvm_irq_routing_table {
    int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS];  // chip[irqchip][pin] -> GSI 映射
    u32 nr_rt_entries;                                 // 路由条目数量
    struct hlist_head map[] __counted_by(nr_rt_entries); // GSI -> 路由条目列表
};

1.2 kvm_set_irq() - IRQ 设置函数

位置: virt/kvm/irqchip.c:70-97

c
/*
 * 返回值:
 *  < 0   中断被忽略 (被屏蔽或其他原因未投递)
 *  = 0   中断被合并 (上一个 IRQ 仍挂起)
 *  > 0   中断投递到的 CPU 数量
 */
int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
                bool line_status)
{
    struct kvm_kernel_irq_routing_entry irq_set[KVM_NR_IRQCHIPS];
    int ret = -1, i, idx;

    trace_kvm_set_irq(irq, level, irq_source_id);

    // 同时设置 PIC 和 IOAPIC, 客户机将忽略未使用的那个
    idx = srcu_read_lock(&kvm->irq_srcu);
    i = kvm_irq_map_gsi(kvm, irq_set, irq);  // 查找 GSI 对应的路由条目
    srcu_read_unlock(&kvm->irq_srcu, idx);

    while (i--) {
        int r;
        // 调用具体的 set 回调 (kvm_pic_set_irq 或 kvm_ioapic_set_irq)
        r = irq_set[i].set(&irq_set[i], kvm, irq_source_id, level, line_status);
        if (r < 0)
            continue;
        ret = r + ((ret < 0) ? 0 : ret);
    }
    return ret;
}

1.3 kvm_irq_map_gsi() - GSI 映射查找

位置: virt/kvm/irqchip.c:21-38

c
int kvm_irq_map_gsi(struct kvm *kvm,
                    struct kvm_kernel_irq_routing_entry *entries, int gsi)
{
    struct kvm_irq_routing_table *irq_rt;
    struct kvm_kernel_irq_routing_entry *e;
    int n = 0;

    irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
                                    lockdep_is_held(&kvm->irq_lock));
    if (irq_rt && gsi < irq_rt->nr_rt_entries) {
        hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
            entries[n] = *e;
            ++n;
        }
    }
    return n;
}

1.4 kvm_set_msi() - MSI 中断设置

位置: arch/x86/kvm/irq.c:225-239

c
int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
                struct kvm *kvm, int irq_source_id, int level, bool line_status)
{
    struct kvm_lapic_irq irq;

    if (kvm_msi_route_invalid(kvm, e))
        return -EINVAL;

    if (!level)
        return -1;

    kvm_msi_to_lapic_irq(kvm, e, &irq);  // 将 MSI 消息转换为 LAPIC IRQ

    return kvm_irq_delivery_to_apic(kvm, NULL, &irq);
}

2. Local APIC (LAPIC)

2.1 struct kvm_lapic 结构

位置: arch/x86/kvm/lapic.h:62-89

c
struct kvm_lapic {
    unsigned long base_address;       // LAPIC 基地址
    struct kvm_io_device dev;          // I/O 设备接口
    struct kvm_timer lapic_timer;     // LAPIC 定时器
    u32 divide_count;                 // 时钟分频系数
    struct kvm_vcpu *vcpu;             // 关联的 vCPU
    bool apicv_active;                // APICv 激活状态
    bool sw_enabled;                  // 软件使能
    bool irr_pending;                 // IRR 挂起标志
    bool lvt0_in_nmi_mode;
    bool guest_apic_protected;
    s16 isr_count;                    // ISR 中置位的位数
    int highest_isr_cache;             // ISR 最高向量缓存
    void *regs;                        // APIC 寄存器页 (MMIO 访问)
    gpa_t vapic_addr;
    struct gfn_to_hva_cache vapic_cache;
    unsigned long pending_events;
    unsigned int sipi_vector;
    int nr_lvt_entries;
};

2.2 lapic_set_affinity() - 设置亲和性

LAPIC 亲和性设置通过修改 LDR (Logical Destination Register) 和 DFR (Destination Format Register) 实现。

关键路径 (arch/x86/kvm/lapic.c:327-391):

c
static void kvm_recalculate_logical_map(struct kvm_apic_map *new,
                                        struct kvm_vcpu *vcpu)
{
    struct kvm_lapic *apic = vcpu->arch.apic;
    enum kvm_apic_logical_mode logical_mode;
    struct kvm_lapic **cluster;
    u16 mask;
    u32 ldr;

    if (new->logical_mode == KVM_APIC_MODE_MAP_DISABLED)
        return;

    if (!kvm_apic_sw_enabled(apic))
        return;

    ldr = kvm_lapic_get_reg(apic, APIC_LDR);
    if (!ldr)
        return;

    // 根据模式设置 logical_mode
    if (apic_x2apic_mode(apic)) {
        logical_mode = KVM_APIC_MODE_X2APIC;
    } else {
        ldr = GET_APIC_LOGICAL_ID(ldr);
        if (kvm_lapic_get_reg(apic, APIC_DFR) == APIC_DFR_FLAT)
            logical_mode = KVM_APIC_MODE_XAPIC_FLAT;
        else
            logical_mode = KVM_APIC_MODE_XAPIC_CLUSTER;
    }
    // ...
}

2.3 kvm_apic_send_ipi() - 发送 IPI

位置: arch/x86/kvm/lapic.c:1645-1651

c
void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high)
{
    struct kvm_lapic_irq irq;

    kvm_icr_to_lapic_irq(apic, icr_low, icr_high, &irq);

    trace_kvm_apic_ipi(icr_low, irq.dest_id);

    kvm_irq_delivery_to_apic(apic->vcpu->kvm, apic, &irq);
}

2.4 定时器中断

定时器注入 (arch/x86/kvm/lapic.c:3144-3152):

c
void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu)
{
    struct kvm_lapic *apic = vcpu->arch.apic;

    if (atomic_read(&apic->lapic_timer.pending) > 0) {
        kvm_apic_inject_pending_timer_irqs(apic);
        atomic_set(&apic->lapic_timer.pending, 0);
    }
}

定时器启动 (arch/x86/kvm/lapic.c:2291-2306):

c
static void start_sw_timer(struct kvm_lapic *apic)
{
    struct kvm_timer *ktimer = &apic->lapic_timer;

    WARN_ON(preemptible());
    if (apic->lapic_timer.hv_timer_in_use)
        cancel_hv_timer(apic);
    if (!apic_lvtt_period(apic) && atomic_read(&ktimer->pending))
        return;

    if (apic_lvtt_period(apic) || apic_lvtt_oneshot(apic))
        start_sw_period(apic);
    else if (apic_lvtt_tscdeadline(apic))
        start_sw_tscdeadline(apic);
    trace_kvm_hv_timer_state(apic->vcpu->vcpu_id, false);
}

2.5 kvm_irq_delivery_to_apic() - IRQ 投递到 LAPIC

位置: arch/x86/kvm/lapic.h:125-129 和 lapic.c:1334-1391

c
// lapic.h 中的内联包装
static inline int kvm_irq_delivery_to_apic(struct kvm *kvm,
                                           struct kvm_lapic *src,
                                           struct kvm_lapic_irq *irq)
{
    return __kvm_irq_delivery_to_apic(kvm, src, irq, NULL);
}

// lapic.c 中的实际实现
int __kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
                               struct kvm_lapic_irq *irq,
                               struct rtc_status *rtc_status)
{
    int r = -1;
    struct kvm_vcpu *vcpu, *lowest = NULL;
    unsigned long i, dest_vcpu_bitmap[BITS_TO_LONGS(KVM_MAX_VCPUS)];
    unsigned int dest_vcpus = 0;

    // 首先尝试快速投递路径
    if (__kvm_irq_delivery_to_apic_fast(kvm, src, irq, &r, rtc_status))
        return r;

    // 遍历所有 vCPU, 查找匹配的目标
    kvm_for_each_vcpu(i, vcpu, kvm) {
        if (!kvm_apic_present(vcpu))
            continue;

        if (!kvm_apic_match_dest(vcpu, src, irq->shorthand,
                                 irq->dest_id, irq->dest_mode))
            continue;

        if (!kvm_lowest_prio_delivery(irq)) {
            // 固定优先级投递
            if (r < 0)
                r = 0;
            r += kvm_apic_set_irq(vcpu, irq, rtc_status);
        } else if (kvm_apic_sw_enabled(vcpu->arch.apic)) {
            // 最低优先级投递 - 找优先级最高的 CPU
            if (!lowest)
                lowest = vcpu;
            else if (kvm_apic_compare_prio(vcpu, lowest) < 0)
                lowest = vcpu;
            // ...
        }
    }
    // ...
}

3. IOAPIC

3.1 struct kvm_ioapic 结构

位置: arch/x86/kvm/ioapic.h:71-89

c
struct kvm_ioapic {
    u64 base_address;                      // IOAPIC 基地址 (默认 0xFEC00000)
    u32 ioregsel;                          // I/O 寄存器选择器
    u32 id;                                // IOAPIC ID
    u32 irr;                               // 中断请求寄存器 (Interrupt Request Register)
    u32 pad;
    union kvm_ioapic_redirect_entry redirtbl[IOAPIC_NUM_PINS];  // 重定向表
    unsigned long irq_states[IOAPIC_NUM_PINS];                   // 每个引脚的状态
    struct kvm_io_device dev;              // I/O 设备接口
    struct kvm *kvm;
    spinlock_t lock;
    struct rtc_status rtc_status;
    struct delayed_work eoi_inject;         // EOI 延迟注入工作队列
    u32 irq_eoi[IOAPIC_NUM_PINS];          // EOI 计数
    u32 irr_delivered;                     // 已投递的 IRR

    /* reads protected by irq_srcu, writes by irq_lock */
    struct hlist_head mask_notifier_list;
};

3.2 ioapic_set_irq() - IOAPIC 中断设置

位置: arch/x86/kvm/ioapic.c:187-245

c
static int ioapic_set_irq(struct kvm_ioapic *ioapic, unsigned int irq,
                           int irq_level, bool line_status)
{
    union kvm_ioapic_redirect_entry entry;
    u32 mask = 1 << irq;
    u32 old_irr;
    int edge, ret;

    entry = ioapic->redirtbl[irq];
    edge = (entry.fields.trig_mode == IOAPIC_EDGE_TRIG);  // 判断边沿/电平触发

    if (!irq_level) {
        ioapic->irr &= ~mask;  // 清除 IRR
        ret = 1;
        goto out;
    }

    // 边沿触发且 AVIC 激活时的特殊处理
    if (edge && kvm_apicv_activated(ioapic->kvm))
        ioapic_lazy_update_eoi(ioapic, irq);

    // RTC 特殊处理 - 跟踪 EOI
    if (irq == RTC_GSI && line_status && rtc_irq_check_coalesced(ioapic)) {
        ret = 0;  // 被合并
        goto out;
    }

    old_irr = ioapic->irr;
    ioapic->irr |= mask;  // 设置 IRR
    if (edge) {
        ioapic->irr_delivered &= ~mask;
        if (old_irr == ioapic->irr) {
            ret = 0;  // 中断被合并
            goto out;
        }
    }

    ret = ioapic_service(ioapic, irq, line_status);  // 实际投递中断

out:
    trace_kvm_ioapic_set_irq(entry.bits, irq, ret == 0);
    return ret;
}

3.3 边沿触发 vs 电平触发

边沿触发 (Edge-triggered):

  • 当检测到电压/电平变化时触发中断
  • irr_delivered 标志用于追踪已投递的边沿中断
  • 相同向量不会重复投递

电平触发 (Level-triggered):

  • 只要电平保持高/低就一直触发
  • remote_irr 字段追踪远程 CPU 是否在等待 EOI
  • EOI 后若电平仍 assert, 需要重新注入

3.4 ioapic_service() - 中断服务

位置: arch/x86/kvm/ioapic.c:457-498

c
static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status)
{
    union kvm_ioapic_redirect_entry *entry = &ioapic->redirtbl[irq];
    struct kvm_lapic_irq irqe;
    int ret;

    // 检查是否被屏蔽或远程 IRR 置位
    if (entry->fields.mask ||
        (entry->fields.trig_mode == IOAPIC_LEVEL_TRIG &&
         entry->fields.remote_irr))
        return -1;

    // 构造 LAPIC IRQ
    irqe.dest_id = entry->fields.dest_id;
    irqe.vector = entry->fields.vector;
    irqe.dest_mode = kvm_lapic_irq_dest_mode(!!entry->fields.dest_mode);
    irqe.trig_mode = entry->fields.trig_mode;
    irqe.delivery_mode = entry->fields.delivery_mode << 8;
    irqe.level = 1;
    irqe.shorthand = APIC_DEST_NOSHORT;
    irqe.msi_redir_hint = false;

    // 边沿触发的特殊处理
    if (irqe.trig_mode == IOAPIC_EDGE_TRIG)
        ioapic->irr_delivered |= 1 << irq;

    // 投递到 LAPIC
    if (irq == RTC_GSI && line_status)
        ret = __kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe,
                                         &ioapic->rtc_status);
    else
        ret = kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe);

    // 电平触发中断设置 remote_irr
    if (ret && irqe.trig_mode == IOAPIC_LEVEL_TRIG)
        entry->fields.remote_irr = 1;

    return ret;
}

4. Eventfd 集成

4.1 struct kvm_kernel_irqfd 结构

位置: include/linux/kvm_host.h (前向声明在 2402, 实现分布在 eventfd.c)

c
struct kvm_kernel_irqfd {
    struct list_head list;              // 链接到 kvm->irqfds.items
    struct eventfd_ctx *eventfd;        // 关联的 eventfd
    struct eventfd_ctx *resamplefd;      // resample 模式的 resamplefd
    struct work_struct inject;           // 注入工作队列
    struct work_struct shutdown;         // 关闭工作队列
    wait_queue_entry_t wait;             // 等待队列条目
    struct kvm *kvm;
    u32 gsi;                             // 全局系统中断号
    struct seqcount_spinlock irq_entry_sc;  // 路由条目的序列计数器
    struct kvm_kernel_irq_routing_entry irq_entry;  // 当前路由条目
    struct kvm_kernel_irqfd_resampler *resampler;  // resampler 引用
    struct irq_bypass_consumer consumer;  // IRQ bypass 消费者
    struct irq_bypass_producer *producer; // IRQ bypass 生产者
    struct kvm_vcpu *irq_bypass_vcpu;    // bypass 目标 vCPU
};

4.2 kvm_irqfd() - IRQFD 机制

位置: virt/kvm/eventfd.c:369-520

c
static int kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
{
    struct kvm_kernel_irqfd *irqfd;
    struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
    // ...

    irqfd = kzalloc_obj(*irqfd, GFP_KERNEL_ACCOUNT);
    // ...

    // 获取 eventfd 上下文
    eventfd = eventfd_ctx_fileget(fd_file(f));
    irqfd->eventfd = eventfd;

    // 处理 RESAMPLE 模式
    if (args->flags & KVM_IRQFD_FLAG_RESAMPLE) {
        resamplefd = eventfd_ctx_fdget(args->resamplefd);
        irqfd->resamplefd = resamplefd;
        // 创建或加入现有的 resampler
        // ...
    }

    // 注册到 eventfd 的等待队列
    idx = srcu_read_lock(&kvm->irq_srcu);
    irqfd_pt.irqfd = irqfd;
    irqfd_pt.kvm = kvm;
    init_poll_funcptr(&irqfd_pt.pt, kvm_irqfd_register);
    events = vfs_poll(fd_file(f), &irqfd_pt.pt);

    if (events & EPOLLIN)  // eventfd 已有信号, 立即注入
        schedule_work(&irqfd->inject);

    // 注册 IRQ bypass 消费者
    if (kvm_arch_has_irq_bypass()) {
        irqfd->consumer.add_producer = kvm_arch_irq_bypass_add_producer;
        irqfd->consumer.del_producer = kvm_arch_irq_bypass_del_producer;
        irq_bypass_register_consumer(&irqfd->consumer, irqfd->eventfd);
    }
    // ...
}

4.3 irqfd_wakeup() - 事件唤醒处理

位置: virt/kvm/eventfd.c:201-270

c
static int irqfd_wakeup(wait_queue_entry_t *wait, unsigned mode, int sync, void *key)
{
    struct kvm_kernel_irqfd *irqfd = container_of(wait, struct kvm_kernel_irqfd, wait);
    __poll_t flags = key_to_poll(key);
    struct kvm_kernel_irq_routing_entry irq;
    // ...

    if (flags & EPOLLIN) {
        eventfd_ctx_do_read(irqfd->eventfd, &cnt);  // 消费 eventfd 信号

        idx = srcu_read_lock(&kvm->irq_srcu);
        do {
            seq = read_seqcount_begin(&irqfd->irq_entry_sc);
            irq = irqfd->irq_entry;
        } while (read_seqcount_retry(&irqfd->irq_entry_sc, seq));

        // 注入中断
        if (unlikely(!irqfd_is_active(irqfd)) ||
            kvm_arch_set_irq_inatomic(&irq, kvm,
                                      KVM_USERSPACE_IRQ_SOURCE_ID, 1,
                                      false) == -EWOULDBLOCK)
            schedule_work(&irqfd->inject);
        srcu_read_unlock(&kvm->irq_srcu, idx);
        ret = 1;
    }

    if (flags & EPOLLHUP) {
        // eventfd 关闭, 从 KVM 分离
        spin_lock_irqsave(&kvm->irqfds.lock, iflags);
        if (irqfd_is_active(irqfd))
            irqfd_deactivate(irqfd);
        spin_unlock_irqrestore(&kvm->irqfds.lock, iflags);
    }
    return ret;
}

4.4 Resampler 机制

位置: virt/kvm/eventfd.c:58-116

Resampler 用于电平触发中断的重新采样, 当 Guest 发送 EOI 时重新触发中断。

c
// EOI 确认回调
static void irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
{
    struct kvm_kernel_irqfd_resampler *resampler;
    struct kvm *kvm;
    int idx;

    resampler = container_of(kian, struct kvm_kernel_irqfd_resampler, notifier);
    kvm = resampler->kvm;

    // 解除中断断言
    kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
                resampler->notifier.gsi, 0, false);

    idx = srcu_read_lock(&kvm->irq_srcu);
    // 通知所有 resampler 上的 irqfd 重新采样
    irqfd_resampler_notify(resampler);
    srcu_read_unlock(&kvm->irq_srcu, idx);
}

5. VFIO 集成

5.1 struct kvm_vfio 结构

位置: virt/kvm/vfio.c:24-36

c
struct kvm_vfio_file {
    struct list_head node;
    struct file *file;
#ifdef CONFIG_SPAPR_TCE_IOMMU
    struct iommu_group *iommu_group;
#endif
};

struct kvm_vfio {
    struct list_head file_list;    // VFIO 文件列表
    struct mutex lock;
    bool noncoherent;               // 非一致性 DMA 标志
};

5.2 kvm_vfio_ops - VFIO 操作接口

位置: virt/kvm/vfio.c:347-353

c
static const struct kvm_device_ops kvm_vfio_ops = {
    .name = "kvm-vfio",
    .create = kvm_vfio_create,
    .release = kvm_vfio_release,
    .set_attr = kvm_vfio_set_attr,
    .has_attr = kvm_vfio_has_attr,
};

5.3 kvm_vfio_file_add() - 添加设备组

位置: virt/kvm/vfio.c:143-186

c
static int kvm_vfio_file_add(struct kvm_device *dev, unsigned int fd)
{
    struct kvm_vfio *kv = dev->private;
    struct kvm_vfio_file *kvf;
    struct file *filp;
    int ret = 0;

    filp = fget(fd);
    if (!filp)
        return -EBADF;

    // 验证是 VFIO FD
    if (!kvm_vfio_file_is_valid(filp)) {
        ret = -EINVAL;
        goto out_fput;
    }

    mutex_lock(&kv->lock);

    // 检查是否已存在
    list_for_each_entry(kvf, &kv->file_list, node) {
        if (kvf->file == filp) {
            ret = -EEXIST;
            goto out_unlock;
        }
    }

    kvf = kzalloc_obj(*kvf, GFP_KERNEL_ACCOUNT);
    kvf->file = get_file(filp);
    list_add_tail(&kvf->node, &kv->file_list);

    // 设置 KVM 关联
    kvm_vfio_file_set_kvm(kvf->file, dev->kvm);
    kvm_vfio_update_coherency(dev);

out_unlock:
    mutex_unlock(&kv->lock);
out_fput:
    fput(filp);
    return ret;
}

5.4 Passthrough 设备访问

VFIO 集成允许将物理设备直接传递给 Guest, 主要功能包括:

  1. 设备组管理: 通过 iommu_group 跟踪共享 IOMMU 域的设备
  2. DMA 一致性处理: kvm_vfio_update_coherency() 检查所有设备的一致性
  3. 用户空间中断: 通过 eventfd 机制将设备中断注入 Guest

6. 中断流程图

6.1 外部中断注入流程

物理设备 IRQ


irqfd_wakeup() / kvm_set_irq()


kvm_irq_map_gsi() ──查找 GSI 路由──▶ kvm_kernel_irq_routing_entry


e->set() 回调 (kvm_ioapic_set_irq / kvm_pic_set_irq / kvm_set_msi)

    ├─▶ ioapic_set_irq()
    │       │
    │       ▼
    │       ioapic_service()
    │           │
    │           ▼
    │           kvm_irq_delivery_to_apic()
    │               │
    │               ▼
    │           kvm_apic_set_irq()
    │               │
    │               ▼
    │           __apic_accept_irq()
    │               │
    │               ▼
    │           apic_set_isr() + 注入到 vCPU

    └─▶ kvm_set_msi() ──▶ kvm_msi_to_lapic_irq() ──▶ kvm_irq_delivery_to_apic()

6.2 LAPIC IPI 发送流程

Guest 写 ICR (Interrupt Command Register)


kvm_x2apic_icr_write() / __kvm_x2apic_icr_write()


kvm_icr_to_lapic_irq() ──构造 kvm_lapic_irq 结构


kvm_apic_send_ipi()


kvm_irq_delivery_to_apic()


__kvm_irq_delivery_to_apic()

    ├─▶ __kvm_irq_delivery_to_apic_fast() (使用 apic_map 优化)

    └─▶ 遍历所有 vCPU 匹配目标


        kvm_apic_set_irq() ──▶ __apic_accept_irq()

6.3 Eventfd (IRQFD) 流程

用户空间 eventfd_signal()


eventfd 等待队列唤醒


irqfd_wakeup()

    ├─ EPOLLIN:
    │       │
    │       ▼
    │       kvm_arch_set_irq_inatomic() 或 schedule_work(&irqfd->inject)
    │               │
    │               ▼
    │       kvm_set_irq() ──▶ 中断注入流程

    └─ EPOLLHUP:


        irqfd_deactivate()


        irqfd_shutdown() (延迟执行, 等待中的工作完成)

6.4 EOI 与 Resampler 流程

Guest 执行 EOI 写操作


kvm_apic_update_eoi()


kvm_ioapic_update_eoi_one()


kvm_notify_acked_irq() ──通知 ACK ──▶ irqfd_resampler_ack()
    │                                               │
    │                                               ▼
    │                                           eventfd_signal(resamplefd)
    │                                               │
    │                                               ▼
    │                                           重新注入中断


entry->fields.remote_irr = 0 (电平触发)


ioapic_service() (如果 IRR 仍挂起则重新投递)

关键常量

常量说明
KVM_NR_IRQCHIPS3IRQ 控制器数量 (PIC master, PIC slave, IOAPIC)
KVM_IRQCHIP_NUM_PINS24每个控制器的引脚数
KVM_MAX_IRQ_ROUTES256最大 IRQ 路由数
IOAPIC_NUM_PINS24IOAPIC 引脚数
LAPIC_MMIO_LENGTH0x1000LAPIC MMIO 空间大小

参考文件

文件路径说明
virt/kvm/irqchip.cIRQ 路由核心实现
virt/kvm/eventfd.cIRQFD 和 ioeventfd 实现
virt/kvm/vfio.cVFIO 集成实现
arch/x86/kvm/irq.cx86 架构 IRQ 处理
arch/x86/kvm/lapic.cLocal APIC 实现
arch/x86/kvm/ioapic.cIOAPIC 实现
include/linux/kvm_host.hKVM 核心数据结构
arch/x86/kvm/lapic.hLAPIC 头文件
arch/x86/kvm/ioapic.hIOAPIC 头文件

基于 VitePress 构建