migration/cgs-tdx: add tdx_mig_setup

tdx_mig_setup is assigned to the cgs migration framework's
savevm_state_setup API (invoked on the source side) and loadvm_state_setup
API (invoked on the destination side). The setup work includes:
- create a kvm_device from the tdx-mig driver in KVM. The device fd is
  returned for later communication with the device.
- negotiate with the driver for the size if the memory to map, this
  includes:
  -- KVM_SET_DEVICE_ATTR: sets the configurable attr (only the migration
     buffer size currently) of the device to KVM. The migration flow
     currently finds and send dirty pages one by one, so the migration
     buffer size set to the driver is 4KB (TAGET_PAGE_SIZE);
  -- KVM_GET_DEVICE_ATTR: gets the negotiated kvm_device's attr. This
     obtains from KVM the sizes of the 4 parts (i.e. mbmd buffer size,
     migration buffer size, mac list buffer size, and gpa list buffer
     size) of shared memory.
- map the 4 parts of shared memory.

Signed-off-by: Wei Wang <wei.w.wang@intel.com>
This commit is contained in:
Wei Wang
2022-11-18 22:55:00 +08:00
committed by Nikolay Borisov
parent b8fc99d9d6
commit e4049cd161
3 changed files with 115 additions and 0 deletions

View File

@@ -690,4 +690,20 @@ struct kvm_tdx_get_migration_info {
__u8 pad[6]; __u8 pad[6];
}; };
#define KVM_DEV_TDX_MIG_ATTR 0x1
struct kvm_dev_tdx_mig_attr {
#define KVM_DEV_TDX_MIG_ATTR_VERSION 0
__u32 version;
/* 4KB buffer can hold 512 entries at most */
#define TDX_MIG_BUF_LIST_PAGES_MAX 512
__u32 buf_list_pages;
__u32 max_migs;
};
#define TDX_MIG_STREAM_MBMD_MAP_OFFSET 0
#define TDX_MIG_STREAM_GPA_LIST_MAP_OFFSET 1
#define TDX_MIG_STREAM_MAC_LIST_MAP_OFFSET 2
#define TDX_MIG_STREAM_BUF_LIST_MAP_OFFSET 4
#endif /* _ASM_X86_KVM_H */ #endif /* _ASM_X86_KVM_H */

View File

@@ -1511,6 +1511,8 @@ enum kvm_device_type {
#define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE #define KVM_DEV_TYPE_XIVE KVM_DEV_TYPE_XIVE
KVM_DEV_TYPE_ARM_PV_TIME, KVM_DEV_TYPE_ARM_PV_TIME,
#define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME #define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME
KVM_DEV_TYPE_TDX_MIG_STREAM,
#define KVM_DEV_TYPE_TDX_MIG_STREAM KVM_DEV_TYPE_TDX_MIG_STREAM
KVM_DEV_TYPE_MAX, KVM_DEV_TYPE_MAX,
}; };

View File

@@ -34,6 +34,101 @@ static bool tdx_mig_is_ready(void)
return tdx_premig_is_done(); return tdx_premig_is_done();
} }
static int tdx_mig_stream_create(TdxMigStream *stream)
{
int ret;
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_TDX_MIG_STREAM, false);
if (ret < 0) {
error_report("Failed to create stream due to %s", strerror(ret));
return ret;
}
stream->fd = ret;
return 0;
}
static int tdx_mig_stream_setup(void)
{
TdxMigStream *stream = &tdx_mig.streams[0];
struct kvm_dev_tdx_mig_attr tdx_mig_attr;
struct kvm_device_attr attr = {
.group = KVM_DEV_TDX_MIG_ATTR,
.addr = (uint64_t)&tdx_mig_attr,
.attr = sizeof(struct kvm_dev_tdx_mig_attr),
};
size_t map_size;
off_t map_offset;
int ret;
ret = tdx_mig_stream_create(stream);
if (ret) {
return ret;
}
/*
* Tell the tdx_mig driver the number of pages to add to buffer list for
* TD private page export/import. Currently, TD private pages are migrated
* one by one.
*/
tdx_mig_attr.buf_list_pages = 1;
tdx_mig_attr.version = KVM_DEV_TDX_MIG_ATTR_VERSION;
if (kvm_device_ioctl(stream->fd, KVM_SET_DEVICE_ATTR, &attr) < 0) {
return -EIO;
}
memset(&tdx_mig_attr, 0, sizeof(struct kvm_dev_tdx_mig_attr));
tdx_mig_attr.version = KVM_DEV_TDX_MIG_ATTR_VERSION;
if (kvm_device_ioctl(stream->fd, KVM_GET_DEVICE_ATTR, &attr) < 0) {
return -EIO;
}
map_offset = TDX_MIG_STREAM_MBMD_MAP_OFFSET;
map_size = (TDX_MIG_STREAM_GPA_LIST_MAP_OFFSET -
TDX_MIG_STREAM_MBMD_MAP_OFFSET) * TARGET_PAGE_SIZE;
stream->mbmd = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
stream->fd, map_offset);
if (stream->mbmd == MAP_FAILED) {
ret = -errno;
error_report("Failed to map mbmd due to %s", strerror(ret));
return ret;
}
map_offset = TDX_MIG_STREAM_GPA_LIST_MAP_OFFSET * TARGET_PAGE_SIZE;
map_size = (TDX_MIG_STREAM_MAC_LIST_MAP_OFFSET -
TDX_MIG_STREAM_GPA_LIST_MAP_OFFSET) * TARGET_PAGE_SIZE;
stream->gpa_list = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
stream->fd, map_offset);
if (stream->gpa_list == MAP_FAILED) {
ret = -errno;
error_report("Failed to map gpa list due to %s", strerror(ret));
return ret;
}
map_offset = TDX_MIG_STREAM_MAC_LIST_MAP_OFFSET * TARGET_PAGE_SIZE;
map_size = (TDX_MIG_STREAM_BUF_LIST_MAP_OFFSET -
TDX_MIG_STREAM_MAC_LIST_MAP_OFFSET) * TARGET_PAGE_SIZE;
stream->mac_list = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
stream->fd, map_offset);
if (stream->mac_list == MAP_FAILED) {
ret = -errno;
error_report("Failed to map mac list due to %s", strerror(ret));
return ret;
}
map_offset = TDX_MIG_STREAM_BUF_LIST_MAP_OFFSET * TARGET_PAGE_SIZE;
map_size = tdx_mig_attr.buf_list_pages * TARGET_PAGE_SIZE;
stream->buf_list = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
stream->fd, map_offset);
if (stream->buf_list == MAP_FAILED) {
ret = -errno;
error_report("Failed to map buf list due to %s", strerror(ret));
return ret;
}
return 0;
}
void tdx_mig_init(CgsMig *cgs_mig, uint32_t nr_channels) void tdx_mig_init(CgsMig *cgs_mig, uint32_t nr_channels)
{ {
/* Only support 1 migration channel currently */ /* Only support 1 migration channel currently */
@@ -42,4 +137,6 @@ void tdx_mig_init(CgsMig *cgs_mig, uint32_t nr_channels)
tdx_mig.streams = g_malloc0(sizeof(struct TdxMigStream) * nr_channels); tdx_mig.streams = g_malloc0(sizeof(struct TdxMigStream) * nr_channels);
cgs_mig->is_ready = tdx_mig_is_ready; cgs_mig->is_ready = tdx_mig_is_ready;
cgs_mig->savevm_state_setup = tdx_mig_stream_setup;
cgs_mig->loadvm_state_setup = tdx_mig_stream_setup;
} }