searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

鲲鹏云主机vfio-user热迁移guest中断问题排查

2024-09-23 09:43:23
58
0

背景

在鲲鹏云主机适配lava项目中,测试发现带lava盘(qemu与spdk存储后端通过vfio-user连接)的虚拟机,在热迁移后,guest里面nvme盘不能响应中断。

关键词:
鲲鹏​:ARMv8架构
vfio-user​: qemu与spdk通过vfio-user协议连接

测试与现象

  1. 热迁移源端,目的端都没有明显错误打印。
  2. 目的端guest内nvme驱动正常加载,lsblk正常显示nvme设备。
  3. 命令file -s /dev/nvme0n1会卡住,过一会内核打印 image.png之后正常显示文件信息。这个时间比较久,严重影响用户体验。
  4. 虚拟机内reboot操后,nvme盘恢复正常。
  5. x86架构下相同条件vm热迁移没有发生这个问题。

分析与排查

从上面第三条信息看,虽然获取文件信息的时间比较长,但是最后还是获取成功了,说明guest nvme驱动,qemu, spdk之间的数据通路应该是正常的。guest nvme驱动通知后端的路径是正常的,但是spdk后端通知guest的路径可能出了问题。为啥这里出了问题file命令还能得到正确输出呢?存储同学说nvmeq驱动在request发生timeout之后还是会尝试捞一把数据,正确输出说明数据的确是送过来了,是中断没有正常送上去。

中断是如何投递给guest的

spdk和qemu在不同的地址空间,所以spdk在完成request后,想要快速得投递中断给guest,必须使用irqfd。irqfd是qemu这边创建的,创建之后会通过ioctl注册到kvm, 再通过vfio-user msg注册到spdk。我们可以追踪这个过程是否正常执行了。因为irqfd作为一个文件描述符,通过sendmsg接口传输给另一个进程,fd号是会发生变化的,所以无法根据fd号判断是否正常,但只要recvmsg没有报错,大概率就是正常的。接着需要确认spdk侧再处理完request之后是否正常写了对应queue的irqfd。kvm是否正常执行了kvm_set_irq函数。如果正常执行了kvm_set_irq函数,guest里面还没有收到中断,那可能就是arm中断控制器(vgic)流程有问题。

kvm_set_irq函数执行了吗

比较关键的是在guest触发读写nvme请求后,负责给guest注入中断的host kvm的kvm_set_irq函数有没有正常执行到。

对于kvm_set_irq,内核是有一个tracepoint的,我们可以方便得看到这个函数有没有被执行到。当内核执行到这个函数后,会打印gsi号,level电平值,以及irq_source_id。我们可以使能这个tracepoint, 然后在guest里触发对nvme盘的读写操作,然后查看kvm_set_irq函数有没有执行到。

#if defined(CONFIG_HAVE_KVM_IRQFD)
TRACE_EVENT(kvm_set_irq,
	TP_PROTO(unsigned int gsi, int level, int irq_source_id),
	TP_ARGS(gsi, level, irq_source_id),

	TP_STRUCT__entry(
		__field(	unsigned int,	gsi		)
		__field(	int,		level		)
		__field(	int,		irq_source_id	)
	),

	TP_fast_assign(
		__entry->gsi		= gsi;
		__entry->level		= level;
		__entry->irq_source_id	= irq_source_id;
	),

	TP_printk("gsi %u level %d source %d",
		  __entry->gsi, __entry->level, __entry->irq_source_id)
);
#endif /* defined(CONFIG_HAVE_KVM_IRQFD) */

因为这个gsi号是vm内唯一的,最好把其他虚拟机停掉,避免干扰。为了避免虚拟机内其他中断的干扰,我们把qemu里vfio设备注册中断的gsi都打印出来。

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 38023f79e5..f452d6391b 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2043,6 +2043,7 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq,
         return -ENOSYS;
     }
 
+    printf("assign irqfd: fd %d, gsi %d, flags %d\n", irqfd.fd, irqfd.gsi, irqfd.flags);
     return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
 }
 
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 9c08252948..597ae028bb 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -418,8 +418,10 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
             if (vdev->msi_vectors[i].virq < 0 ||
                 (msix && msix_is_masked(&vdev->pdev, i))) {
                 fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt);
-            } else {
+               printf("debug1: interrupt fd %d \n", fd);
+           } else {
                 fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt);
+               printf("debug2: kvm_interrupt fd %d \n", fd);
             }
         }
 
@@ -427,6 +429,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
     }
 
     ret = VDEV_SET_IRQS(&vdev->vbasedev, irq_set);
+    printf("!!!!!VDEV_SET_IRQS: index = %d, count = %d, flags = %d\n", irq_set->index, irq_set->count, irq_set->flags);
+    for (i = 0; i < vdev->nr_vectors; i++) {
+       printf("%d ", fds[i]);
+    }
+    printf("\n");
+
 
     g_free(irq_set);

我们期望看到qemu正常使用了irqfd(打印kvm_interrupt fd),并且得到了vfio设备(当前只有这个lava盘)的irqfd对应的gsi号。
image.png

然后我们在guest里面执行file -s /dev/nvme0n1, 观察内核的trace,看到多了以下两条打印信息,这个gsi号和之前qemu里打印的vfio设备注册的gsi号是可以对应起来的,证明正常得触发了KVM的中断注入函数kvm_set_irq,即spdk在完成request之后正常写了irqfd。
image.png

追踪kvm_set_irq流程

kvm_set_irq都执行了,guest还没有收到中断,只能追踪内核流程看有无线索。函数调用链

kvm_set_irq
	kvm_set_msi
		kvm_populate_msi   //这里获取msi信息
			vgic_its_inject_msi
				vgic_its_trigger_msi(kvm, its, msi->devid, msi->data);  //这里传入msi->devid是0
					vgic_its_resolve_lpi
						find_ite
							find_its_device //return NULL

通过添加内核打印发现:

kvm_set_irq未能成功执行直接原因:find_its_device返回NULL

/*
 * Find and returns a device in the device table for an ITS.
 * Must be called with the its_lock mutex held.
 */
static struct its_device *find_its_device(struct vgic_its *its, u32 device_id)
{
	struct its_device *device;

	list_for_each_entry(device, &its->device_list, dev_list)
		if (device_id == device->device_id)
			return device;

	return NULL;
}

为啥找不到这个设备呢?这个设备是在哪里添加到链表中的?是不是热迁移后表象没有正常恢复?另外打印发现传入的device_id是0,这里也值得怀疑。

/* Must be called with its_lock mutex held */
static struct its_device *vgic_its_alloc_device(struct vgic_its *its,
						u32 device_id, gpa_t itt_addr,
						u8 num_eventid_bits)
{
	struct its_device *device;

	device = kzalloc(sizeof(*device), GFP_KERNEL);
	if (!device)
		return ERR_PTR(-ENOMEM);

	device->device_id = device_id;
	device->itt_addr = itt_addr;
	device->num_eventid_bits = num_eventid_bits;
	INIT_LIST_HEAD(&device->itt_head);

	list_add_tail(&device->dev_list, &its->device_list);
	return device;
}

我们在这个函数里面添加打印,打印出注册设备的device_id,发现都不是0, 所以问题出在传入的device_id是0上。

这个device_id是从哪里拿到的呢?从之前的函数调用链找,是从irq_routing_entry拿到的。

static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e,
			     struct kvm_msi *msi)
{
	msi->address_lo = e->msi.address_lo;
	msi->address_hi = e->msi.address_hi;
	msi->data = e->msi.data;
	msi->flags = e->msi.flags;
	msi->devid = e->msi.devid;
}

所以应该是qemu注册irq_routing表项的时候传入了错误的devid;

int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
                                 PCIDevice *dev)
{
    struct kvm_irq_routing_entry kroute = {};

    if (kvm_gsi_direct_mapping()) {
        return 0;
    }

    if (!kvm_irqchip_in_kernel()) {
        return -ENOSYS;
    }

    kroute.gsi = virq;
    kroute.type = KVM_IRQ_ROUTING_MSI;
    kroute.flags = 0;
    kroute.u.msi.address_lo = (uint32_t)msg.address;
    kroute.u.msi.address_hi = msg.address >> 32;
    kroute.u.msi.data = le32_to_cpu(msg.data);
    if (pci_available && kvm_msi_devid_required()) {
        kroute.flags = KVM_MSI_VALID_DEVID;
        kroute.u.msi.devid = pci_requester_id(dev);
    }
    if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data, dev)) {
        return -EINVAL;
    }

    trace_kvm_irqchip_update_msi_route(virq);

    return kvm_update_routing_entry(s, &kroute);
}

看到kroute.u.msi.devid = pci_requester_id(dev);为啥迁移到目的端,这里这个pci_requester_id(dev)就返回0了呢?看一下这个函数的实现。

pci_requester_id ->
	pci_req_id_cache_extract ->
		pci_get_bdf

pci_requester_id是根据pci device的bdf得到的。bdf应该是pci conf信息的一部分,应该在热迁移前后被保存与恢复,为啥目的端变成0了呢?

static inline uint16_t pci_get_bdf(PCIDevice *dev)
{
    return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn);
}

static inline PCIBus *pci_get_bus(const PCIDevice *dev)
{
    return PCI_BUS(qdev_get_parent_bus(DEVICE(dev)));
}

BusState *qdev_get_parent_bus(DeviceState *dev)
{
    return dev->parent_bus;
}

添加打印看,目的端bus号变成了0;从上面代码猜测,bus号是根据parent pcidevice的信息得到的。是否是需要保存parent pci设备的config信息?另外热迁移后的virtio设备是正常的,我们看一下virtio设备热迁移有没有save, load parent dev信息的操作

virtio_save
	k->save_config(qbus->parent, f)   //virtio_pci_save_config
		pci_device_save(&proxy->pci_dev, f);  //save parent pci dev

所以我们对vfio设备也做类似的操作

--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2472,6 +2472,10 @@ const VMStateDescription vmstate_vfio_pci_config = {
 static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f)
 {
     VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
+    PCIDevice *pdev = &vdev->pdev;
+    BusState *qbus = qdev_get_parent_bus(DEVICE(pdev));
+
+    pci_device_save(PCI_DEVICE(qbus->parent), f);
 
     vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL);
 }
@@ -2480,8 +2484,14 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
 {
     VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
     PCIDevice *pdev = &vdev->pdev;
+    BusState *qbus = qdev_get_parent_bus(DEVICE(pdev));
     int ret;
 
+    ret = pci_device_load(PCI_DEVICE(qbus->parent), f);
+    if (ret) {
+        return ret;
+    }
+
     ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
     if (ret) {
         return ret;

修改后目的端guest nvme设备能正常收到中断。问题得到解决。

另外,为啥x86上没有这个问题?

/**
 * kvm_msi_devid_required:
 * Returns: true if KVM requires a device id to be provided while
 * defining an MSI routing entry.
 */
#define kvm_msi_devid_required() (kvm_msi_use_devid)

这个参数默认是false, 目前只有arm使能了。

0条评论
0 / 1000
陈胡冠申
2文章数
0粉丝数
陈胡冠申
2 文章 | 0 粉丝
陈胡冠申
2文章数
0粉丝数
陈胡冠申
2 文章 | 0 粉丝
原创

鲲鹏云主机vfio-user热迁移guest中断问题排查

2024-09-23 09:43:23
58
0

背景

在鲲鹏云主机适配lava项目中,测试发现带lava盘(qemu与spdk存储后端通过vfio-user连接)的虚拟机,在热迁移后,guest里面nvme盘不能响应中断。

关键词:
鲲鹏​:ARMv8架构
vfio-user​: qemu与spdk通过vfio-user协议连接

测试与现象

  1. 热迁移源端,目的端都没有明显错误打印。
  2. 目的端guest内nvme驱动正常加载,lsblk正常显示nvme设备。
  3. 命令file -s /dev/nvme0n1会卡住,过一会内核打印 image.png之后正常显示文件信息。这个时间比较久,严重影响用户体验。
  4. 虚拟机内reboot操后,nvme盘恢复正常。
  5. x86架构下相同条件vm热迁移没有发生这个问题。

分析与排查

从上面第三条信息看,虽然获取文件信息的时间比较长,但是最后还是获取成功了,说明guest nvme驱动,qemu, spdk之间的数据通路应该是正常的。guest nvme驱动通知后端的路径是正常的,但是spdk后端通知guest的路径可能出了问题。为啥这里出了问题file命令还能得到正确输出呢?存储同学说nvmeq驱动在request发生timeout之后还是会尝试捞一把数据,正确输出说明数据的确是送过来了,是中断没有正常送上去。

中断是如何投递给guest的

spdk和qemu在不同的地址空间,所以spdk在完成request后,想要快速得投递中断给guest,必须使用irqfd。irqfd是qemu这边创建的,创建之后会通过ioctl注册到kvm, 再通过vfio-user msg注册到spdk。我们可以追踪这个过程是否正常执行了。因为irqfd作为一个文件描述符,通过sendmsg接口传输给另一个进程,fd号是会发生变化的,所以无法根据fd号判断是否正常,但只要recvmsg没有报错,大概率就是正常的。接着需要确认spdk侧再处理完request之后是否正常写了对应queue的irqfd。kvm是否正常执行了kvm_set_irq函数。如果正常执行了kvm_set_irq函数,guest里面还没有收到中断,那可能就是arm中断控制器(vgic)流程有问题。

kvm_set_irq函数执行了吗

比较关键的是在guest触发读写nvme请求后,负责给guest注入中断的host kvm的kvm_set_irq函数有没有正常执行到。

对于kvm_set_irq,内核是有一个tracepoint的,我们可以方便得看到这个函数有没有被执行到。当内核执行到这个函数后,会打印gsi号,level电平值,以及irq_source_id。我们可以使能这个tracepoint, 然后在guest里触发对nvme盘的读写操作,然后查看kvm_set_irq函数有没有执行到。

#if defined(CONFIG_HAVE_KVM_IRQFD)
TRACE_EVENT(kvm_set_irq,
	TP_PROTO(unsigned int gsi, int level, int irq_source_id),
	TP_ARGS(gsi, level, irq_source_id),

	TP_STRUCT__entry(
		__field(	unsigned int,	gsi		)
		__field(	int,		level		)
		__field(	int,		irq_source_id	)
	),

	TP_fast_assign(
		__entry->gsi		= gsi;
		__entry->level		= level;
		__entry->irq_source_id	= irq_source_id;
	),

	TP_printk("gsi %u level %d source %d",
		  __entry->gsi, __entry->level, __entry->irq_source_id)
);
#endif /* defined(CONFIG_HAVE_KVM_IRQFD) */

因为这个gsi号是vm内唯一的,最好把其他虚拟机停掉,避免干扰。为了避免虚拟机内其他中断的干扰,我们把qemu里vfio设备注册中断的gsi都打印出来。

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 38023f79e5..f452d6391b 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2043,6 +2043,7 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq,
         return -ENOSYS;
     }
 
+    printf("assign irqfd: fd %d, gsi %d, flags %d\n", irqfd.fd, irqfd.gsi, irqfd.flags);
     return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd);
 }
 
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 9c08252948..597ae028bb 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -418,8 +418,10 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
             if (vdev->msi_vectors[i].virq < 0 ||
                 (msix && msix_is_masked(&vdev->pdev, i))) {
                 fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt);
-            } else {
+               printf("debug1: interrupt fd %d \n", fd);
+           } else {
                 fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt);
+               printf("debug2: kvm_interrupt fd %d \n", fd);
             }
         }
 
@@ -427,6 +429,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
     }
 
     ret = VDEV_SET_IRQS(&vdev->vbasedev, irq_set);
+    printf("!!!!!VDEV_SET_IRQS: index = %d, count = %d, flags = %d\n", irq_set->index, irq_set->count, irq_set->flags);
+    for (i = 0; i < vdev->nr_vectors; i++) {
+       printf("%d ", fds[i]);
+    }
+    printf("\n");
+
 
     g_free(irq_set);

我们期望看到qemu正常使用了irqfd(打印kvm_interrupt fd),并且得到了vfio设备(当前只有这个lava盘)的irqfd对应的gsi号。
image.png

然后我们在guest里面执行file -s /dev/nvme0n1, 观察内核的trace,看到多了以下两条打印信息,这个gsi号和之前qemu里打印的vfio设备注册的gsi号是可以对应起来的,证明正常得触发了KVM的中断注入函数kvm_set_irq,即spdk在完成request之后正常写了irqfd。
image.png

追踪kvm_set_irq流程

kvm_set_irq都执行了,guest还没有收到中断,只能追踪内核流程看有无线索。函数调用链

kvm_set_irq
	kvm_set_msi
		kvm_populate_msi   //这里获取msi信息
			vgic_its_inject_msi
				vgic_its_trigger_msi(kvm, its, msi->devid, msi->data);  //这里传入msi->devid是0
					vgic_its_resolve_lpi
						find_ite
							find_its_device //return NULL

通过添加内核打印发现:

kvm_set_irq未能成功执行直接原因:find_its_device返回NULL

/*
 * Find and returns a device in the device table for an ITS.
 * Must be called with the its_lock mutex held.
 */
static struct its_device *find_its_device(struct vgic_its *its, u32 device_id)
{
	struct its_device *device;

	list_for_each_entry(device, &its->device_list, dev_list)
		if (device_id == device->device_id)
			return device;

	return NULL;
}

为啥找不到这个设备呢?这个设备是在哪里添加到链表中的?是不是热迁移后表象没有正常恢复?另外打印发现传入的device_id是0,这里也值得怀疑。

/* Must be called with its_lock mutex held */
static struct its_device *vgic_its_alloc_device(struct vgic_its *its,
						u32 device_id, gpa_t itt_addr,
						u8 num_eventid_bits)
{
	struct its_device *device;

	device = kzalloc(sizeof(*device), GFP_KERNEL);
	if (!device)
		return ERR_PTR(-ENOMEM);

	device->device_id = device_id;
	device->itt_addr = itt_addr;
	device->num_eventid_bits = num_eventid_bits;
	INIT_LIST_HEAD(&device->itt_head);

	list_add_tail(&device->dev_list, &its->device_list);
	return device;
}

我们在这个函数里面添加打印,打印出注册设备的device_id,发现都不是0, 所以问题出在传入的device_id是0上。

这个device_id是从哪里拿到的呢?从之前的函数调用链找,是从irq_routing_entry拿到的。

static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e,
			     struct kvm_msi *msi)
{
	msi->address_lo = e->msi.address_lo;
	msi->address_hi = e->msi.address_hi;
	msi->data = e->msi.data;
	msi->flags = e->msi.flags;
	msi->devid = e->msi.devid;
}

所以应该是qemu注册irq_routing表项的时候传入了错误的devid;

int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg,
                                 PCIDevice *dev)
{
    struct kvm_irq_routing_entry kroute = {};

    if (kvm_gsi_direct_mapping()) {
        return 0;
    }

    if (!kvm_irqchip_in_kernel()) {
        return -ENOSYS;
    }

    kroute.gsi = virq;
    kroute.type = KVM_IRQ_ROUTING_MSI;
    kroute.flags = 0;
    kroute.u.msi.address_lo = (uint32_t)msg.address;
    kroute.u.msi.address_hi = msg.address >> 32;
    kroute.u.msi.data = le32_to_cpu(msg.data);
    if (pci_available && kvm_msi_devid_required()) {
        kroute.flags = KVM_MSI_VALID_DEVID;
        kroute.u.msi.devid = pci_requester_id(dev);
    }
    if (kvm_arch_fixup_msi_route(&kroute, msg.address, msg.data, dev)) {
        return -EINVAL;
    }

    trace_kvm_irqchip_update_msi_route(virq);

    return kvm_update_routing_entry(s, &kroute);
}

看到kroute.u.msi.devid = pci_requester_id(dev);为啥迁移到目的端,这里这个pci_requester_id(dev)就返回0了呢?看一下这个函数的实现。

pci_requester_id ->
	pci_req_id_cache_extract ->
		pci_get_bdf

pci_requester_id是根据pci device的bdf得到的。bdf应该是pci conf信息的一部分,应该在热迁移前后被保存与恢复,为啥目的端变成0了呢?

static inline uint16_t pci_get_bdf(PCIDevice *dev)
{
    return PCI_BUILD_BDF(pci_bus_num(pci_get_bus(dev)), dev->devfn);
}

static inline PCIBus *pci_get_bus(const PCIDevice *dev)
{
    return PCI_BUS(qdev_get_parent_bus(DEVICE(dev)));
}

BusState *qdev_get_parent_bus(DeviceState *dev)
{
    return dev->parent_bus;
}

添加打印看,目的端bus号变成了0;从上面代码猜测,bus号是根据parent pcidevice的信息得到的。是否是需要保存parent pci设备的config信息?另外热迁移后的virtio设备是正常的,我们看一下virtio设备热迁移有没有save, load parent dev信息的操作

virtio_save
	k->save_config(qbus->parent, f)   //virtio_pci_save_config
		pci_device_save(&proxy->pci_dev, f);  //save parent pci dev

所以我们对vfio设备也做类似的操作

--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2472,6 +2472,10 @@ const VMStateDescription vmstate_vfio_pci_config = {
 static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f)
 {
     VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
+    PCIDevice *pdev = &vdev->pdev;
+    BusState *qbus = qdev_get_parent_bus(DEVICE(pdev));
+
+    pci_device_save(PCI_DEVICE(qbus->parent), f);
 
     vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL);
 }
@@ -2480,8 +2484,14 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
 {
     VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
     PCIDevice *pdev = &vdev->pdev;
+    BusState *qbus = qdev_get_parent_bus(DEVICE(pdev));
     int ret;
 
+    ret = pci_device_load(PCI_DEVICE(qbus->parent), f);
+    if (ret) {
+        return ret;
+    }
+
     ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
     if (ret) {
         return ret;

修改后目的端guest nvme设备能正常收到中断。问题得到解决。

另外,为啥x86上没有这个问题?

/**
 * kvm_msi_devid_required:
 * Returns: true if KVM requires a device id to be provided while
 * defining an MSI routing entry.
 */
#define kvm_msi_devid_required() (kvm_msi_use_devid)

这个参数默认是false, 目前只有arm使能了。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0