首先每一个 vdpa 设备都有自己的基础特性,举例如下两个设备,其中是基本的环境信息
版本 | |
---|---|
virsh | 8.0.0 |
qemu | qemu-kvm-6.2.0-53.module+el8.10.0+1882+0c885e98.2 |
guest os | 4.18.0-348.el8.x86_64 |
那我们看一下 feature 的几个来源以及其中的作用
设备的硬件feature
下面是两种卡的 feature 的信息
hw特性 | guest feature | |
---|---|---|
CX6 | 0x114c60180b | 0x140201803 |
DPU | 0x20003700cb982f | 0x100029803 |
vdpa 对 featue 的修改
从上面看 guest features 是不同的,如果两种设备之间互相热迁移将会导致迁移失败,那我们如何提前知道设备之间可以热迁移呢,我们先看一下 vdpa 是如何协商 features 的
首先经过 vdpa app 获取到 hw feature 之后,我们是有可能修改设备的 features 的,并不是完全的透传给 qemu,具体代码如下:
vdpa_hw_notify_addr_init(hw: &vadapter->hw, nr_vrings: vadapter->max_vrings); /* Remove IOMMU support and add vhost-user related feature bits. */ vadapter->features = vadapter->hw.hw_features & ~(1ULL << VIRTIO_F_IOMMU_PLATFORM); // 关闭了 iommu_paltform vadapter->features |= vdpa_types[vadapter->hw.device_type].features; // 使能了 VIRTIO_NET_F_GUEST_ANNOUNCE VDPA_LOG(INFO, "vadapter->features: 0x%" PRIx64 ".", vadapter->features);
所以即使hw 看起来没有的feature,也可能在 vdpa-app 里面使能该feature
qemu 对 feature 的校验和修改
首先是在设备会走到这个函数
uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { │ return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), │ │ features); }
其中 vhost_net_get_feature_bits
主要是根据vhost-user 还是 vhost-kernel 获取 qemu 对设备的 feature 支持
hw/net/vhost_net.c /* Features supported by others. */ static const int user_feature_bits[] = { │ VIRTIO_F_NOTIFY_ON_EMPTY, │ VIRTIO_RING_F_INDIRECT_DESC, │ VIRTIO_RING_F_EVENT_IDX, │ │ VIRTIO_F_ANY_LAYOUT, │ VIRTIO_F_VERSION_1, │ VIRTIO_NET_F_CSUM, │ VIRTIO_NET_F_GUEST_CSUM, │ VIRTIO_NET_F_GSO, │ VIRTIO_NET_F_GUEST_TSO4, │ VIRTIO_NET_F_GUEST_TSO6, │ VIRTIO_NET_F_GUEST_ECN, │ VIRTIO_NET_F_GUEST_UFO, │ VIRTIO_NET_F_HOST_TSO4, │ VIRTIO_NET_F_HOST_TSO6, │ VIRTIO_NET_F_HOST_ECN, │ VIRTIO_NET_F_HOST_UFO, │ VIRTIO_NET_F_MRG_RXBUF, │ VIRTIO_NET_F_MTU, │ VIRTIO_F_IOMMU_PLATFORM, │ VIRTIO_F_RING_PACKED, │ VIRTIO_NET_F_RSS, │ VIRTIO_NET_F_HASH_REPORT, │ │ /* This bit implies RARP isn't sent by QEMU out of band */ │ VIRTIO_NET_F_GUEST_ANNOUNCE, │ │ VIRTIO_NET_F_MQ, │ │ VHOST_INVALID_FEATURE_BIT };
vhost_get_features
uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, │ uint64_t features /* 这里是vhost user 提供的能力集 */) { │ const int *bit = feature_bits; │ while (*bit != VHOST_INVALID_FEATURE_BIT) { │ │ uint64_t bit_mask = (1ULL << *bit); │ │ if (!(hdev->features & bit_mask)) { │ │ │ features &= ~bit_mask; │ │ } │ │ bit++; │ } │ return features; }
这个是 qemu 根据 qemu 支持的 feature 和 host feature 最终协商出来的一个 features,继续看 guest 的驱动提供的 feature 是如何参与进来的我们少啰嗦直接看代码 vhost_net_ack_features
void vhost_net_ack_features(struct vhost_net *net, uint64_t features /* 这里面是 guest os 提供的能力集 */) { │ net->dev.acked_features = net->dev.backend_features; │ vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); }
这个是 qemu 根据 guest feature 和 host feature 最终协商出来的一个 features
void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, │ uint64_t features) { │ const int *bit = feature_bits; │ while (*bit != VHOST_INVALID_FEATURE_BIT) { │ │ uint64_t bit_mask = (1ULL << *bit); │ │ if (features & bit_mask) { │ │ │ hdev->acked_features |= bit_mask; │ │ } │ │ bit++; │ } }
总结
到这里我们就清楚了最总协商起来的 feature 是三者的集合 vhost & qemu & guest driver