kernel-3.10.0内核 virtio-block设备驱动的中断处理函数 virtblk_done 的分析

本文分析了QEMU虚拟机中virtblk_done函数的触发流程,详细介绍了从中断处理到函数调用的具体过程。揭示了CPUVector与IRQID、IRQID与struct irq_desc对象之间的对应关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文主要分析qemu vm中触发 virtblk_done 函数的流程分析,下面列出此函数的函数调用栈信息。

 0xffffffffa0167000 : virtblk_done+0x0/0x100 [virtio_blk]
 0xffffffffa003e148 : vring_interrupt+0x38/0x90 [virtio_ring]
 0xffffffff8111f83e : handle_irq_event_percpu+0x3e/0x1e0 [kernel]
 0xffffffff8111fa1d : handle_irq_event+0x3d/0x60 [kernel]
 0xffffffff811226b7 : handle_edge_irq+0x77/0x130 [kernel]
 0xffffffff81017ee2 : handle_irq+0xe2/0x1a0 [kernel]
 0xffffffff81650c3f : do_IRQ+0x4f/0xf0 [kernel]

本文主要是探索virtblk_done中断函数的以下几个关系

  1. CPU Vector 与 IRQ 之间的对应关系
  2. IRQ 与 struct irq_desc对象之间的关系
  3. struct irq_desc对象中irq_data成员的解析

1 CPU Vector 与 IRQ ID之间的对应关系

do_IRQ函数将调用handle_irq函数,中断handle_irq函数得到如下信息

Breakpoint 8, handle_irq (irq=32, regs=0xffffffff8194be28 <init_thread_union+15912>) at arch/x86/kernel/irq_64.c:84

我们可以看出virtblk_done函数的irq很可能就是32,此时regs就是do_IRQ函数传入的寄存器信息。我们把它打印出来。

(gdb) p *regs
$40 = {
  r15 = 18446744071588593208,
  r14 = 18446744071579552066,
  r13 = 18446744071588593296,
  r12 = 18446744071579778376,
  bp = 18446744071588593360,
  bx = 382049427668,
  r11 = 0,
  r10 = 0,
  r9 = 0,
  r8 = 238,
  ax = 4295049345,
  cx = 0,
  dx = 18446612134457592096,
  si = 134,
  di = 134,
  orig_ax = 18446744073709551533,
  ip = 18446744071579779908,
  cs = 16,
  flags = 514,
  sp = 18446744071588593360,
  ss = 24
}

do_IRQ函数实现如下

unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);

    /* high bit used in ret_from_ code  */
    unsigned vector = ~regs->orig_ax;
    unsigned irq;

    irq_enter();
    exit_idle();

    irq = __this_cpu_read(vector_irq[vector]);

    if (!handle_irq(irq, regs)) {
        ack_APIC_irq();

        if (irq != VECTOR_RETRIGGERED) {
            pr_emerg_ratelimited("%s: %d.%d No irq handler for vector (irq %d)\n",
                         __func__, smp_processor_id(),
                         vector, irq);
        } else {
            __this_cpu_write(vector_irq[vector], VECTOR_UNDEFINED);
        }
    }

    irq_exit();

    set_irq_regs(old_regs);
    return 1;
}

do_IRQ函数计算得到了此IRQ对应的vector," unsigned vector = ~regs->orig_ax ",上面我们已经得到了所有的regs信息,下面我们就可以得到 【IRQ 32】对应的 CPU Vector值是 82

(gdb) p ~regs->orig_ax
$41 = 82

在do_IRQ函数中也给出了计算 IRQ的方法,通过计算得到的 CPU Vector 值,在当前CPU的vector_irq全局变量数组中找到vector对应的成员即为期望的IRQ。计算方法是 irq = __this_cpu_read(vector_irq[vector ]);
至此我们找到了一个对应关系 Vector:82 <----> IRQ:32

2 IRQ ID 与 struct irq_desc 对象之间的对应关系

继续运行gdb,我们可以得到 handle_edge_irq 函数的调用栈信息

(gdb) bt
#0  handle_edge_irq (irq=898403584, desc=0xffff8800358c8d00) at kernel/irq/chip.c:495
#1  0x0000000000000019 in irq_stack_union ()
#2  0xffff88007c1a3de8 in ?? ()
#3  0xffff88007fd03f70 in ?? ()
#4  0xffffffff81017ee2 in generic_handle_irq_desc (desc=<optimized out>, irq=898403584) at include/linux/irqdesc.h:114
#5  handle_irq (irq=898403584, regs=0xffff8800358c8d84) at arch/x86/kernel/irq_64.c:93
Backtrace stopped: frame did not save the PC

handle_edge_irq函数的irq是个非法值,因为此irq值不做使用,所以它非法我们也就不再细究了,我们来看下第二个参数desc的信息,如下

(gdb) p *desc
$4 = {
  irq_data = {
    irq = 32,
    hwirq = 0,
    node = 4294967295,
    state_use_accessors = 36864,
    chip = 0xffffffff81972860 <msi_chip>,
    domain = 0x0 <irq_stack_union>,
    handler_data = 0x0 <irq_stack_union>,
    chip_data = 0xffff880035bb66c0,
    msi_desc = 0xffff880035b12b40,
    affinity = 0xffff8800356a9c00
  },
  kstat_irqs = 0x20b30,
  handle_irq = 0xffffffff81122640 <handle_simple_irq+64>,
  action = 0xffff8800357eb680,
  status_use_accessors = 16384,
  core_internal_state__do_not_mess_with_it = 0,
  depth = 0,
  wake_depth = 0,
  irq_count = 102,
  last_unhandled = 0,
  irqs_unhandled = 0,
  lock = {
    raw_lock = {
      {
        head_tail = 29491648,
        tickets = {
          head = 448,
          tail = 450
        }
      }
    }
  },
  percpu_enabled = 0x0 <irq_stack_union>,
  affinity_hint = 0x0 <irq_stack_union>,
  affinity_notify = 0x0 <irq_stack_union>,
  pending_mask = 0xffff8800356aac00,
  threads_oneshot = 0,
  threads_active = {
    counter = 0
  },
  wait_for_threads = {
    lock = {
      {
        rlock = {
          raw_lock = {
            {
              head_tail = 0,
              tickets = {
                head = 0,
                tail = 0
              }
            }
          }
        }
      }
    },
    task_list = {
      next = 0xffff8800358c8dc0,
      prev = 0xffff8800358c8dc0
    }
  },
  dir = 0xffff8800357eb480,
  parent_irq = 0,
  owner = 0x0 <irq_stack_union>,
  name = 0xffffffff818e60d1 "edge"
}

我们可以看到中断IRQ 32对应的 chip和struct irq_cfg对象分别是

desc->irq_data.chip 是 msi_chip
desc->irq_data.chip_data 就是 struct irq_cfg对象

IRQ 32对应的 desc->irq_data.chip 详细信息如下

(gdb) p *desc->irq_data.chip
$16 = {
  name = 0xffffffff8185d68d "PCI-MSI",
  irq_startup = 0x0 <irq_stack_union>,
  irq_shutdown = 0x0 <irq_stack_union>,
  irq_enable = 0x0 <irq_stack_union>,
  irq_disable = 0x0 <irq_stack_union>,
  irq_ack = 0xffffffff8150a840 <setup_hpet_msi_remapped+16>,
  irq_mask = 0xffffffff81346600 <pci_enable_msix+832>,
  irq_mask_ack = 0x0 <irq_stack_union>,
  irq_unmask = 0xffffffff81346620 <pci_enable_msix+864>,
  irq_eoi = 0xffffffff8150ac70 <compose_remapped_msi_msg+128>,
  irq_set_affinity = 0xffffffff8150a7d0 <setup_ioapic_remapped_entry+16>,
  irq_retrigger = 0xffffffff8104d070 <ioapic_retrigger_irq>,
  irq_set_type = 0x0 <irq_stack_union>,
  irq_set_wake = 0x0 <irq_stack_union>,
  irq_bus_lock = 0x0 <irq_stack_union>,
  irq_bus_sync_unlock = 0x0 <irq_stack_union>,
  irq_cpu_online = 0x0 <irq_stack_union>,
  irq_cpu_offline = 0x0 <irq_stack_union>,
  irq_suspend = 0x0 <irq_stack_union>,
  irq_resume = 0x0 <irq_stack_union>,
  irq_pm_shutdown = 0x0 <irq_stack_union>,
  irq_print_chip = 0xffffffff8150aca0 <ir_ack_apic_level+16>,
  flags = 0
}

IRQ 32对应的 desc->irq_data.chip_data,也就是struct irq_cfg对象详细信息如下

(gdb) p *(struct irq_cfg *)desc->irq_data.chip_data
$17 = {
  irq_2_pin = 0x0 <irq_stack_union>,
  domain = 0xffff88003520b400,
  old_domain = 0xffff88003520b000,
  vector = 82 'R',
  move_in_progress = 0 '\000',
  remapped = 1 '\001',
  {
    irq_2_iommu = {
      iommu = 0xffff88007d130600,
      irte_index = 32,
      sub_handle = 1,
      irte_mask = 0 '\000'
    },
    irq_2_irte = {
      devid = 1536,
      index = 32019
    }
  }
}

IRQ 32对应的 desc->irq_data.chip_data->irq_2_iommu->iommu对象 详细信息如下

(gdb) p *(struct intel_iommu *)0xffff88007d130600
$19 = {
  reg = 0xffffc90000302000,
  reg_phys = 4275634176,
  reg_size = 4096,
  cap = 5067151449129478,
  ecap = 15732494,
  gcmd = 2264924160,
  register_lock = {
    raw_lock = {
      {
        head_tail = 1835036,
        tickets = {
          head = 28,
          tail = 28
        }
      }
    }
  },
  seq_id = 0,
  agaw = 1,
  msagaw = 1,
  irq = 24,
  segment = 0,
  name = "dmar0\000\000\000\000\000\000\000",
  domain_ids = 0xffff880077d2a000,
  domains = 0xffff880077780000,
  lock = {
    {
      rlock = {
        raw_lock = {
          {
            head_tail = 3145776,
            tickets = {
              head = 48,
              tail = 48
            }
          }
        }
      }
    }
  },
  root_entry = 0xffff880077737000,
  flush = {
    flush_context = 0xffffffff81503520 <qi_global_iec+64>,
    flush_iotlb = 0xffffffff815035a0 <qi_flush_context+96>
  },
  qi = 0xffff88007d132800,
  iommu_state = 0x0 <irq_stack_union>,
  ir_table = 0xffff88007d002390,
  iommu_dev = 0xffff88007be14c00,
  node = -1
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值