level-triggered interrupt & edge-triggered interrupt (电平触发中断 与 边沿触发中断)

(谢谢 wanpin Li对这篇博客的反馈,让我重新发现了一些问题)

两种触发方式,是为了在多个设备共享中断线时,实现流量控制。

  • 所谓边沿触发,就是电平发生变化(通常是从高到低)时触发的中断 (CPU忙时中断可能丢失)
  • 所谓电平触发,就是设备保持中断请求引脚(中断线)处于预设的有效触发电频(通常为高),中断会一直被请求(CPU忙时中断也不会丢失)。为了避免中断请求被重复处理,需要在处理前 标记 irq,然后 ACK irq(复位中断请求引脚为无效电频,以便能够接受其他中断请求),处理中断,然后 取消 irq 标记。

KVM 最先只支持 edge-triggered 中断,使用一个irqfd通知guest,电频下降边沿可在注入模拟中一次完成。看下面代码连续调用两个kvm_set_irq(),倒数第二个参数为电平。

虚拟机 level-triggered 的注入仍然依赖的是userspace,在QEMU里模拟了 PCI / IOapic /LAPIC(8259)。KVM_IRQFD ioctl 是中断注入的接口,唯一参数 kvm_irqfd 结构体包含了一个irqfd、gsi(全局系统中断),flags。

KVM 在 2012 年也添加了 level-triggered 中断模拟 [1],添加了一个 resamplefd (为eventfd) 到 kvm_irqfd 结构体,用它来通知guest取消 irq 标记。来自所有guests、设备的请求被放在链表中管理,所有的 resample irqfds使用同一个 IRQ源ID,所以 de-assert(复位中断请求引脚为无效电平,也就是清除中断状态寄存器–ISR)只需要一次,而每个irqfd都需要单独通知 guest 一次(用于通知新的resampler使用这个gsi)。

static void
irqfd_inject(struct work_struct *work)
{
	struct _irqfd *irqfd = container_of(work, struct _irqfd, inject);
	struct kvm *kvm = irqfd->kvm;

	if (!irqfd->resampler) {  //edge-triggered
		kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1, false);
		kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0, false);
	} else //level-triggered
		kvm_set_irq(kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
			    irqfd->gsi, 1, false); //assert irq
}
/*
 * Since resampler irqfds share an IRQ source ID, we de-assert once
 * then notify all of the resampler irqfds using this GSI.  We can't
 * do multiple de-asserts or we risk racing with incoming re-asserts.
 */
static void
irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
{
	...
	kvm_set_irq(resampler->kvm, KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
		    resampler->notifier.gsi, 0, false); //de-assert irq for level-triggered interrupt

	list_for_each_entry_rcu(irqfd, &resampler->list, resampler_link)
		eventfd_signal(irqfd->resamplefd, 1); //notify resimplers
	...
}

[1] commit 7a84428af  [PATCH] KVM: Add resampling irqfds for level triggered interrupts

Reference:
linux/virt/kvm/eventfd.c:
  irqfd_inject(struct work_struct *work)
  irqfd_shutdown(struct _irqfd *irqfd)
  irqfd_resampler_ack(struct kvm_irq_ack_notifier *kian)
  struct kvm_irqfd
  struct _irqfd
  struct _irqfd_resampler
linux/virt/kvm/irqchip.c:
  kvm_set_irq(kvm, source_id, level, line_status)

linux/virt/kvm/irq_comm.c:
  kvm_reqeust_irq_source_id(struct kvm *kvm)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.