aboutsummaryrefslogtreecommitdiff
path: root/samples/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'samples/client.c')
-rw-r--r--samples/client.c309
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;