diff options
Diffstat (limited to 'samples/client.c')
-rw-r--r-- | samples/client.c | 309 |
1 files changed, 167 insertions, 142 deletions
diff --git a/samples/client.c b/samples/client.c index d4abd21..121122a 100644 --- a/samples/client.c +++ b/samples/client.c @@ -62,6 +62,8 @@ static char const *irq_to_str[] = { [VFU_DEV_REQ_IRQ] = "REQ" }; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + void vfu_log(UNUSED vfu_ctx_t *vfu_ctx, UNUSED int level, const char *fmt, ...) @@ -217,9 +219,7 @@ static bool get_region_vfio_caps(struct vfio_info_cap_header *header, struct vfio_region_info_cap_sparse_mmap **sparse) { - struct vfio_region_info_cap_type *type; unsigned int i; - bool migr = false; while (true) { switch (header->id) { @@ -234,16 +234,6 @@ get_region_vfio_caps(struct vfio_info_cap_header *header, (ull_t)(*sparse)->areas[i].size); } break; - case VFIO_REGION_INFO_CAP_TYPE: - type = (struct vfio_region_info_cap_type*)header; - if (type->type != VFIO_REGION_TYPE_MIGRATION || - type->subtype != VFIO_REGION_SUBTYPE_MIGRATION) { - errx(EXIT_FAILURE, "bad region type %d/%d", type->type, - type->subtype); - } - migr = true; - printf("client: migration region\n"); - break; default: errx(EXIT_FAILURE, "bad VFIO cap ID %#x", header->id); } @@ -252,7 +242,7 @@ get_region_vfio_caps(struct vfio_info_cap_header *header, } header = (struct vfio_info_cap_header*)((char*)header + header->next - sizeof(struct vfio_region_info)); } - return migr; + return false; } static void @@ -347,8 +337,7 @@ get_device_region_info(int sock, uint32_t index) if (get_region_vfio_caps((struct vfio_info_cap_header*)(region_info + 1), &sparse)) { if (sparse != NULL) { - assert((index == VFU_PCI_DEV_BAR1_REGION_IDX && nr_fds == 2) || - (index == VFU_PCI_DEV_MIGR_REGION_IDX && nr_fds == 1)); + assert((index == VFU_PCI_DEV_BAR1_REGION_IDX && nr_fds == 2)); assert(nr_fds == sparse->nr_areas); mmap_sparse_areas(fds, region_info, sparse); } @@ -386,7 +375,7 @@ get_device_info(int sock, struct vfio_user_device_info *dev_info) err(EXIT_FAILURE, "failed to get device info"); } - if (dev_info->num_regions != 10) { + if (dev_info->num_regions != 9) { errx(EXIT_FAILURE, "bad number of device regions %d", dev_info->num_regions); } @@ -471,7 +460,6 @@ access_region(int sock, int region, bool is_write, uint64_t offset, .iov_len = data_len } }; - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; struct vfio_user_region_access *recv_data; size_t nr_send_iovecs, recv_data_len; int op, ret; @@ -526,6 +514,114 @@ access_region(int sock, int region, bool is_write, uint64_t offset, return 0; } +static int +set_migration_state(int sock, uint32_t state) +{ + static int msg_id = 0xfab1; + struct vfio_user_device_feature req = { + .argsz = 16, + .flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE + }; + struct vfio_user_device_feature_mig_state change_state = { + .device_state = state, + .data_fd = 0 + }; + struct iovec send_iovecs[3] = { + [1] = { + .iov_base = &req, + .iov_len = sizeof(req) + }, + [2] = { + .iov_base = &change_state, + .iov_len = sizeof(change_state) + } + }; + void* response = malloc(sizeof(req) + sizeof(change_state)); + + pthread_mutex_lock(&mutex); + int ret = tran_sock_msg_iovec(sock, msg_id--, VFIO_USER_DEVICE_FEATURE, + send_iovecs, 3, NULL, 0, NULL, + response, sizeof(req) + sizeof(change_state), + NULL, 0); + pthread_mutex_unlock(&mutex); + + if (ret < 0) { + return -1; + } + + assert(memcmp(&req, response, sizeof(req)) == 0); + assert(memcmp(&change_state, response + sizeof(req), + sizeof(change_state)) == 0); + + return ret; +} + +static int +read_migr_data(int sock, void *buf, size_t len) +{ + static int msg_id = 0x6904; + struct vfio_user_mig_data_without_data req = { + .argsz = 12, + .size = len + }; + struct iovec send_iovecs[2] = { + [1] = { + .iov_base = &req, + .iov_len = sizeof(req) + } + }; + struct vfio_user_mig_data_with_data *res = calloc(1, sizeof(req) + len); + + pthread_mutex_lock(&mutex); + int ret = tran_sock_msg_iovec(sock, msg_id--, VFIO_USER_MIG_DATA_READ, + send_iovecs, 2, NULL, 0, NULL, + res, sizeof(req) + len, NULL, 0); + pthread_mutex_unlock(&mutex); + + if (ret < 0) { + free(res); + return -1; + } + + memcpy(buf, res->data, res->size); + + free(res); + + return res->size; +} + +static int +write_migr_data(int sock, void *buf, size_t len) +{ + static int msg_id = 0x2023; + struct vfio_user_mig_data_with_data req = { + .argsz = 12 + len, + .size = len + }; + struct iovec send_iovecs[3] = { + [1] = { + .iov_base = &req, + .iov_len = sizeof(req) + }, + [2] = { + .iov_base = buf, + .iov_len = len + } + }; + + pthread_mutex_lock(&mutex); + int ret = tran_sock_msg_iovec(sock, msg_id--, VFIO_USER_MIG_DATA_WRITE, + send_iovecs, 3, NULL, 0, NULL, + &req, sizeof(req), NULL, 0); + pthread_mutex_unlock(&mutex); + + if (ret < 0) { + return -1; + } + + return ret; +} + static void access_bar0(int sock, time_t *t) { @@ -735,79 +831,37 @@ usage(char *argv0) basename(argv0)); } -/* - * Normally each time the source client (QEMU) would read migration data from - * the device it would send them to the destination client. However, since in - * our sample both the source and the destination client are the same process, - * we simply accumulate the migration data of each iteration and apply it to - * the destination server at the end. - * - * Performs as many migration loops as @nr_iters or until the device has no - * more migration data (pending_bytes is zero), which ever comes first. The - * result of each migration iteration is stored in @migr_iter. @migr_iter must - * be at least @nr_iters. - * - * @returns the number of iterations performed - */ static size_t -do_migrate(int sock, size_t nr_iters, struct iovec *migr_iter) +do_migrate(int sock, size_t max_iters, size_t max_iter_size, + struct iovec *migr_iter) { int ret; - uint64_t pending_bytes, data_offset, data_size; - size_t i = 0; - - assert(nr_iters > 0); - - /* XXX read pending_bytes */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - offsetof(struct vfio_user_migration_info, pending_bytes), - &pending_bytes, sizeof(pending_bytes)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to read pending_bytes"); - } - - for (i = 0; i < nr_iters && pending_bytes > 0; i++) { + size_t i; + bool is_more = true; - /* XXX read data_offset and data_size */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - offsetof(struct vfio_user_migration_info, data_offset), - &data_offset, sizeof(data_offset)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to read data_offset"); - } + for (i = 0; i < max_iters && is_more; i++) { - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - offsetof(struct vfio_user_migration_info, data_size), - &data_size, sizeof(data_size)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to read data_size"); - } + migr_iter[i].iov_len = max_iter_size; + migr_iter[i].iov_base = malloc(migr_iter[i].iov_len); - migr_iter[i].iov_len = data_size; - migr_iter[i].iov_base = malloc(data_size); if (migr_iter[i].iov_base == NULL) { err(EXIT_FAILURE, "failed to allocate migration buffer"); } /* XXX read migration data */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - data_offset, - (char *)migr_iter[i].iov_base, data_size); + ret = read_migr_data(sock, migr_iter[i].iov_base, migr_iter[i].iov_len); if (ret < 0) { err(EXIT_FAILURE, "failed to read migration data"); } - /* FIXME send migration data to the destination client process */ - - /* - * XXX read pending_bytes again to indicate to the server that the - * migration data have been consumed. - */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - offsetof(struct vfio_user_migration_info, pending_bytes), - &pending_bytes, sizeof(pending_bytes)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to read pending_bytes"); + if (ret < (int)migr_iter[i].iov_len) { + // FIXME is it pointless shuffling stuff around? + void* buf = malloc(ret); + memcpy(buf, migr_iter[i].iov_base, ret); + free(migr_iter[i].iov_base); + migr_iter[i].iov_base = buf; + migr_iter[i].iov_len = ret; + is_more = false; } } return i; @@ -844,7 +898,7 @@ fake_guest(void *arg) if (ret != 0) { err(EXIT_FAILURE, "fake guest failed to write garbage to BAR1"); } - crc = rte_hash_crc(buf, fake_guest_data->bar1_size, crc); + crc = rte_hash_crc(buf, fake_guest_data->bar1_size, 0); __sync_synchronize(); } while (!fake_guest_data->done); @@ -859,7 +913,6 @@ migrate_from(int sock, size_t *nr_iters, struct iovec **migr_iters, { uint32_t device_state; int ret; - size_t _nr_iters; pthread_t thread; struct fake_guest_data fake_guest_data = { .sock = sock, @@ -868,13 +921,15 @@ migrate_from(int sock, size_t *nr_iters, struct iovec **migr_iters, .crcp = crcp }; + size_t max_iter_size = 4096; + ret = pthread_create(&thread, NULL, fake_guest, &fake_guest_data); if (ret != 0) { errno = ret; err(EXIT_FAILURE, "failed to create pthread"); } - *nr_iters = 2; + *nr_iters = 8; *migr_iters = malloc(sizeof(struct iovec) * *nr_iters); if (*migr_iters == NULL) { err(EXIT_FAILURE, NULL); @@ -884,16 +939,17 @@ migrate_from(int sock, size_t *nr_iters, struct iovec **migr_iters, * XXX set device state to pre-copy. This is technically optional but any * VMM that cares about performance needs this. */ - device_state = VFIO_DEVICE_STATE_V1_SAVING | VFIO_DEVICE_STATE_V1_RUNNING; - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, device_state), - &device_state, sizeof(device_state)); + device_state = VFIO_DEVICE_STATE_PRE_COPY; + ret = set_migration_state(sock, device_state); if (ret < 0) { err(EXIT_FAILURE, "failed to write to device state"); } - _nr_iters = do_migrate(sock, 1, *migr_iters); - assert(_nr_iters == 1); + ret = do_migrate(sock, *nr_iters, max_iter_size, *migr_iters); + if (ret < 0) { + err(EXIT_FAILURE, "failed to do migration in pre-copy state"); + } + printf("client: stopping fake guest thread\n"); fake_guest_data.done = true; __sync_synchronize(); @@ -905,31 +961,25 @@ migrate_from(int sock, size_t *nr_iters, struct iovec **migr_iters, printf("client: setting device state to stop-and-copy\n"); - device_state = VFIO_DEVICE_STATE_V1_SAVING; - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, device_state), - &device_state, sizeof(device_state)); + device_state = VFIO_DEVICE_STATE_STOP_COPY; + ret = set_migration_state(sock, device_state); if (ret < 0) { err(EXIT_FAILURE, "failed to write to device state"); } - _nr_iters += do_migrate(sock, 1, (*migr_iters) + _nr_iters); - if (_nr_iters != 2) { - errx(EXIT_FAILURE, - "expected 2 iterations instead of %zu while in stop-and-copy state", - _nr_iters); + size_t iters = do_migrate(sock, *nr_iters, max_iter_size, *migr_iters); + if (ret < 0) { + err(EXIT_FAILURE, "failed to do migration in stop-and-copy state"); } /* XXX read device state, migration must have finished now */ - device_state = VFIO_DEVICE_STATE_V1_STOP; - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, device_state), - &device_state, sizeof(device_state)); + device_state = VFIO_DEVICE_STATE_STOP; + ret = set_migration_state(sock, device_state); if (ret < 0) { err(EXIT_FAILURE, "failed to write to device state"); } - return _nr_iters; + return iters; } static int @@ -941,8 +991,7 @@ migrate_to(char *old_sock_path, int *server_max_fds, int ret, sock; char *sock_path; struct stat sb; - uint32_t device_state = VFIO_DEVICE_STATE_V1_RESUMING; - uint64_t data_offset, data_len; + uint32_t device_state = VFIO_DEVICE_STATE_RESUMING; size_t i; uint32_t dst_crc; char buf[bar1_size]; @@ -960,9 +1009,10 @@ migrate_to(char *old_sock_path, int *server_max_fds, if (ret == -1) { err(EXIT_FAILURE, "failed to fork"); } - if (ret > 0) { /* child (destination server) */ + if (ret == 0) { /* child (destination server) */ char *_argv[] = { path_to_server, + (char *)"-r", // start in VFIO_DEVICE_STATE_RESUMING (char *)"-v", sock_path, NULL @@ -992,57 +1042,23 @@ migrate_to(char *old_sock_path, int *server_max_fds, negotiate(sock, server_max_fds, server_max_data_xfer_size, pgsize); - /* XXX set device state to resuming */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, device_state), - &device_state, sizeof(device_state)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to set device state to resuming"); - } - for (i = 0; i < nr_iters; i++) { - /* XXX read data offset */ - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, false, - offsetof(struct vfio_user_migration_info, data_offset), - &data_offset, sizeof(data_offset)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to read migration data offset"); - } - /* XXX write migration data */ - /* - * TODO write half of migration data via regular write and other half via - * memopy map. - */ - printf("client: writing migration device data %#llx-%#llx\n", - (ull_t)data_offset, - (ull_t)(data_offset + migr_iters[i].iov_len - 1)); - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - data_offset, migr_iters[i].iov_base, - migr_iters[i].iov_len); + printf("client: writing migration device data iter %zu\n", i); + ret = write_migr_data(sock, migr_iters[i].iov_base, + migr_iters[i].iov_len); if (ret < 0) { err(EXIT_FAILURE, "failed to write device migration data"); } - - /* XXX write data_size */ - data_len = migr_iters[i].iov_len; - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, data_size), - &data_len, sizeof(data_len)); - if (ret < 0) { - err(EXIT_FAILURE, "failed to write migration data size"); - } } - /* XXX set device state to running */ - device_state = VFIO_DEVICE_STATE_V1_RUNNING; - ret = access_region(sock, VFU_PCI_DEV_MIGR_REGION_IDX, true, - offsetof(struct vfio_user_migration_info, device_state), - &device_state, sizeof(device_state)); + /* XXX set device state to stop to finish the transfer */ + device_state = VFIO_DEVICE_STATE_STOP; + ret = set_migration_state(sock, device_state); if (ret < 0) { - err(EXIT_FAILURE, "failed to set device state to running"); + err(EXIT_FAILURE, "failed to set device state to stop"); } /* validate contents of BAR1 */ @@ -1056,6 +1072,15 @@ migrate_to(char *old_sock_path, int *server_max_fds, if (dst_crc != src_crc) { fprintf(stderr, "client: CRC mismatch: %u != %u\n", src_crc, dst_crc); abort(); + } else { + fprintf(stdout, "client: CRC match, we did it! :)\n"); + } + + /* XXX set device state to running */ + device_state = VFIO_DEVICE_STATE_RUNNING; + ret = set_migration_state(sock, device_state); + if (ret < 0) { + err(EXIT_FAILURE, "failed to set device state to running"); } return sock; |