首先每一个 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