aboutsummaryrefslogtreecommitdiff
path: root/lib/libmuser.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libmuser.c')
-rw-r--r--lib/libmuser.c469
1 files changed, 342 insertions, 127 deletions
diff --git a/lib/libmuser.c b/lib/libmuser.c
index 547a318..0aa3443 100644
--- a/lib/libmuser.c
+++ b/lib/libmuser.c
@@ -112,6 +112,8 @@ struct lm_ctx {
int client_max_fds;
+ size_t migration_pgsize;
+
lm_irqs_t irqs; /* XXX must be last */
};
@@ -270,14 +272,20 @@ __free_s(char **p)
}
int
-send_vfio_user_msg(int sock, uint16_t msg_id, bool is_reply,
- enum vfio_user_command cmd, void *data, int len,
+_send_vfio_user_msg(int sock, uint16_t msg_id, bool is_reply,
+ enum vfio_user_command cmd,
+ struct iovec *iovecs, size_t nr_iovecs,
int *fds, int count)
{
int ret;
struct vfio_user_header hdr = {.msg_id = msg_id};
- struct iovec iov[2];
struct msghdr msg;
+ size_t i;
+
+ if (nr_iovecs == 0) {
+ iovecs = alloca(sizeof(*iovecs));
+ nr_iovecs = 1;
+ }
memset(&msg, 0, sizeof(msg));
@@ -288,23 +296,15 @@ send_vfio_user_msg(int sock, uint16_t msg_id, bool is_reply,
hdr.flags.type = VFIO_USER_F_TYPE_COMMAND;
}
- if (data != NULL && len == 0) {
- return -EINVAL;
- }
-
- hdr.msg_size = sizeof(hdr) + len;
+ iovecs[0].iov_base = &hdr;
+ iovecs[0].iov_len = sizeof(hdr);
- iov[0].iov_base = &hdr;
- iov[0].iov_len = sizeof(hdr);
- msg.msg_iovlen = 1;
-
- if (data != NULL) {
- msg.msg_iovlen++;
- iov[1].iov_base = data;
- iov[1].iov_len = len;
+ for (i = 0; i < nr_iovecs; i++) {
+ hdr.msg_size += iovecs[i].iov_len;
}
- msg.msg_iov = iov;
+ msg.msg_iovlen = nr_iovecs;
+ msg.msg_iov = iovecs;
if (fds != NULL) {
size_t size = count * sizeof *fds;
@@ -329,26 +329,43 @@ send_vfio_user_msg(int sock, uint16_t msg_id, bool is_reply,
}
int
+send_vfio_user_msg(int sock, uint16_t msg_id, bool is_reply,
+ enum vfio_user_command cmd,
+ void *data, size_t data_len,
+ int *fds, size_t count) {
+
+ struct iovec iovecs[2] = {
+ [1] = {
+ .iov_base = data,
+ .iov_len = data_len
+ }
+ };
+ return _send_vfio_user_msg(sock, msg_id, is_reply, cmd, iovecs,
+ ARRAY_SIZE(iovecs), fds, count);
+}
+
+int
send_version(int sock, int major, int minor, uint16_t msg_id, bool is_reply,
char *caps)
{
int ret;
- char *data __attribute__((__cleanup__(__free_s))) = NULL;
+ char *data;
- ret = asprintf(&data, "{version: {\"major\": %d, \"minor\": %d}, capabilities: %s}",
+ ret = asprintf(&data,
+ "{version: {\"major\": %d, \"minor\": %d}, capabilities: %s}",
major, minor, caps != NULL ? caps : "{}");
if (ret == -1) {
- data = NULL;
return -1;
}
-
- return send_vfio_user_msg(sock, msg_id, is_reply, VFIO_USER_VERSION, data,
- ret, NULL, 0);
+ ret = send_vfio_user_msg(sock, msg_id, is_reply, VFIO_USER_VERSION, data,
+ ret, NULL, 0);
+ free(data);
+ return ret;
}
int
recv_vfio_user_msg(int sock, struct vfio_user_header *hdr, bool is_reply,
- uint16_t *msg_id, void *data, int *len)
+ uint16_t *msg_id, void *data, size_t *len)
{
int ret;
@@ -388,7 +405,7 @@ recv_vfio_user_msg(int sock, struct vfio_user_header *hdr, bool is_reply,
if (ret < 0) {
return ret;
}
- if (*len != ret) { /* FIXME we should allow receiving less */
+ if (*len != (size_t)ret) { /* FIXME we should allow receiving less */
return -EINVAL;
}
*len = ret;
@@ -398,7 +415,7 @@ recv_vfio_user_msg(int sock, struct vfio_user_header *hdr, bool is_reply,
int
recv_version(int sock, int *major, int *minor, uint16_t *msg_id, bool is_reply,
- int *max_fds)
+ int *max_fds, size_t *pgsize)
{
int ret;
struct vfio_user_header hdr;
@@ -424,23 +441,23 @@ recv_version(int sock, int *major, int *minor, uint16_t *msg_id, bool is_reply,
/* FIXME use proper parsing */
ret = sscanf(data,
- "{version: {\"major\": %d, \"minor\": %d}, capabilities: {max_fds: %d}}",
- major, minor, max_fds);
- if (ret != 3) {
+ "{version: {\"major\": %d, \"minor\": %d}, capabilities: {max_fds: %d, migration: {pgsize: %lu}}}",
+ major, minor, max_fds, pgsize);
+ if (ret != 4) {
return -EINVAL;
}
return 0;
}
int
-send_recv_vfio_user_msg(int sock, uint16_t msg_id, enum vfio_user_command cmd,
- void *send_data, int send_len,
- int *send_fds, int fd_count,
- struct vfio_user_header *hdr,
- void *recv_data, int recv_len)
-{
- int ret = send_vfio_user_msg(sock, msg_id, false, cmd, send_data, send_len,
- send_fds, fd_count);
+_send_recv_vfio_user_msg(int sock, uint16_t msg_id, enum vfio_user_command cmd,
+ struct iovec *iovecs, size_t nr_iovecs,
+ int *send_fds, size_t fd_count,
+ struct vfio_user_header *hdr,
+ void *recv_data, size_t recv_len)
+{
+ int ret = _send_vfio_user_msg(sock, msg_id, false, cmd, iovecs, nr_iovecs,
+ send_fds, fd_count);
if (ret < 0) {
return ret;
}
@@ -450,6 +467,24 @@ send_recv_vfio_user_msg(int sock, uint16_t msg_id, enum vfio_user_command cmd,
return recv_vfio_user_msg(sock, hdr, true, &msg_id, recv_data, &recv_len);
}
+int
+send_recv_vfio_user_msg(int sock, uint16_t msg_id, enum vfio_user_command cmd,
+ void *send_data, size_t send_len,
+ int *send_fds, size_t fd_count,
+ struct vfio_user_header *hdr,
+ void *recv_data, size_t recv_len)
+{
+ struct iovec iovecs[2] = {
+ [1] = {
+ .iov_base = send_data,
+ .iov_len = send_len
+ }
+ };
+ return _send_recv_vfio_user_msg(sock, msg_id, cmd, iovecs,
+ ARRAY_SIZE(iovecs), send_fds, fd_count,
+ hdr, recv_data, recv_len);
+}
+
static int
set_version(lm_ctx_t *lm_ctx, int sock)
{
@@ -458,7 +493,8 @@ set_version(lm_ctx_t *lm_ctx, int sock)
uint16_t msg_id = 0;
char *server_caps;
- ret = asprintf(&server_caps, "{max_fds: %d}", MAX_FDS);
+ ret = asprintf(&server_caps, "{max_fds: %d, migration: {pgsize: %ld}}",
+ MAX_FDS, sysconf(_SC_PAGESIZE));
if (ret == -1) {
return -ENOMEM;
}
@@ -471,7 +507,7 @@ set_version(lm_ctx_t *lm_ctx, int sock)
}
ret = recv_version(sock, &client_mj, &client_mn, &msg_id, true,
- &lm_ctx->client_max_fds);
+ &lm_ctx->client_max_fds, &lm_ctx->migration_pgsize);
if (ret < 0) {
lm_log(lm_ctx, LM_DBG, "failed to receive version: %s", strerror(-ret));
goto out;
@@ -482,7 +518,18 @@ set_version(lm_ctx_t *lm_ctx, int sock)
LIB_MUSER_VFIO_USER_VERS_MJ, LIB_MUSER_VFIO_USER_VERS_MN,
client_mj, client_mn);
ret = -EINVAL;
+ goto out;
+ }
+ if (lm_ctx->migration_pgsize == 0) {
+ lm_log(lm_ctx, LM_ERR, "bad migration page size");
+ ret = -EINVAL;
+ goto out;
}
+
+ /* FIXME need to check max_fds */
+
+ lm_ctx->migration_pgsize = MIN(lm_ctx->migration_pgsize,
+ sysconf(_SC_PAGESIZE));
out:
free(server_caps);
return ret;
@@ -932,23 +979,33 @@ dev_get_irqinfo(lm_ctx_t *lm_ctx, struct vfio_irq_info *irq_info)
* points accordingly.
*/
static int
-dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg,
+dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg, int reg_index,
struct vfio_region_info **vfio_reg, bool is_kernel)
{
+ struct vfio_info_cap_header *header;
+ struct vfio_region_info_cap_type *type = NULL;
struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
struct lm_sparse_mmap_areas *mmap_areas;
int nr_mmap_areas, i;
- size_t sparse_size;
+ size_t type_size = 0;
+ size_t sparse_size = 0;
+ size_t cap_size;
ssize_t ret;
void *cap_ptr;
- if (lm_reg->mmap_areas == NULL) {
- lm_log(lm_ctx, LM_DBG, "bad mmap_areas\n");
- return -EINVAL;
+ if (reg_index == LM_DEV_MIGRATION_REG_IDX) {
+ type_size = sizeof(struct vfio_region_info_cap_type);
+ }
+
+ if (lm_reg->mmap_areas != NULL) {
+ nr_mmap_areas = lm_reg->mmap_areas->nr_mmap_areas;
+ sparse_size = sizeof(*sparse) + (nr_mmap_areas * sizeof(*sparse->areas));
}
- nr_mmap_areas = lm_reg->mmap_areas->nr_mmap_areas;
- sparse_size = sizeof(*sparse) + (nr_mmap_areas * sizeof(*sparse->areas));
+ cap_size = type_size + sparse_size;
+ if (cap_size == 0) {
+ return 0;
+ }
/*
* If vfio_reg does not have enough space to accommodate sparse info then
@@ -956,54 +1013,79 @@ dev_get_sparse_mmap_cap(lm_ctx_t *lm_ctx, lm_reg_info_t *lm_reg,
* is only for kernel/muser.ko, where the request comes from kernel/vfio.
*/
- if ((*vfio_reg)->argsz < sparse_size + sizeof(**vfio_reg) && is_kernel) {
+ if ((*vfio_reg)->argsz < cap_size + sizeof(**vfio_reg) && is_kernel) {
lm_log(lm_ctx, LM_DBG, "vfio_reg too small=%d\n", (*vfio_reg)->argsz);
- (*vfio_reg)->argsz = sparse_size + sizeof(**vfio_reg);
+ (*vfio_reg)->argsz = cap_size + sizeof(**vfio_reg);
(*vfio_reg)->cap_offset = 0;
return 0;
}
- sparse = calloc(1, sparse_size);
- if (sparse == NULL)
+ /* TODO deosn't need to be calloc, we overwrite it entirely */
+ header = calloc(1, cap_size);
+ if (header == NULL) {
return -ENOMEM;
- sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
- sparse->header.version = 1;
- sparse->header.next = 0;
- sparse->nr_areas = nr_mmap_areas;
+ }
- lm_log(lm_ctx, LM_DBG, "%s: capsize %llu, nr_mmap_areas %u", __func__,
- sparse_size, nr_mmap_areas);
- mmap_areas = lm_reg->mmap_areas;
- for (i = 0; i < nr_mmap_areas; i++) {
- sparse->areas[i].offset = mmap_areas->areas[i].start;
- sparse->areas[i].size = mmap_areas->areas[i].size;
- lm_log(lm_ctx, LM_DBG, "%s: area %d offset %#lx size %llu", __func__,
- i, sparse->areas[i].offset, sparse->areas[i].size);
+ if (reg_index == LM_DEV_MIGRATION_REG_IDX) {
+ type = (struct vfio_region_info_cap_type*)header;
+ type->header.id = VFIO_REGION_INFO_CAP_TYPE;
+ type->header.version = 1;
+ type->header.next = 0;
+ type->type = VFIO_REGION_TYPE_MIGRATION;
+ type->subtype = VFIO_REGION_SUBTYPE_MIGRATION;
+ (*vfio_reg)->cap_offset = sizeof(struct vfio_region_info);
}
+ if (lm_reg->mmap_areas != NULL) {
+ if (type != NULL) {
+ type->header.next = (*vfio_reg)->cap_offset + sizeof(struct vfio_region_info_cap_type);
+ sparse = (struct vfio_region_info_cap_sparse_mmap*)(type + 1);
+ } else {
+ (*vfio_reg)->cap_offset = sizeof(struct vfio_region_info);
+ sparse = (struct vfio_region_info_cap_sparse_mmap*)header;
+ }
+ sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+ sparse->header.version = 1;
+ sparse->header.next = 0;
+ sparse->nr_areas = nr_mmap_areas;
+
+ lm_log(lm_ctx, LM_DBG, "%s: capsize %llu, nr_mmap_areas %u", __func__,
+ sparse_size, nr_mmap_areas);
+ mmap_areas = lm_reg->mmap_areas;
+ for (i = 0; i < nr_mmap_areas; i++) {
+ sparse->areas[i].offset = mmap_areas->areas[i].start;
+ sparse->areas[i].size = mmap_areas->areas[i].size;
+ lm_log(lm_ctx, LM_DBG, "%s: area %d offset %#lx size %llu", __func__,
+ i, sparse->areas[i].offset, sparse->areas[i].size);
+ }
+ }
+
+ /*
+ * FIXME VFIO_REGION_INFO_FLAG_MMAP is valid if the region is
+ * memory-mappable in general, not only if it supports sparse mmap.
+ */
(*vfio_reg)->flags |= VFIO_REGION_INFO_FLAG_MMAP | VFIO_REGION_INFO_FLAG_CAPS;
- (*vfio_reg)->cap_offset = sizeof(**vfio_reg);
if (is_kernel) {
/* write the sparse mmap cap info to vfio-client user pages */
- ret = write(lm_ctx->conn_fd, sparse, sparse_size);
- if (ret != (ssize_t)sparse_size) {
- free(sparse);
+ ret = write(lm_ctx->conn_fd, header, cap_size);
+ if (ret != (ssize_t)cap_size) {
+ free(header);
return -EIO;
}
} else {
- (*vfio_reg)->argsz = sparse_size + sizeof(**vfio_reg);
+ (*vfio_reg)->argsz = cap_size + sizeof(**vfio_reg);
*vfio_reg = realloc(*vfio_reg, (*vfio_reg)->argsz);
if (*vfio_reg == NULL) {
- free(sparse);
+ free(header);
return -ENOMEM;
}
cap_ptr = (char *)*vfio_reg + (*vfio_reg)->cap_offset;
- memcpy(cap_ptr, sparse, sparse_size);
+ memcpy(cap_ptr, header, cap_size);
}
- free(sparse);
+ free(header);
return 0;
}
@@ -1073,11 +1155,10 @@ dev_get_reginfo(lm_ctx_t *lm_ctx, struct vfio_region_info **vfio_reg,
(*vfio_reg)->flags = lm_reg->flags;
(*vfio_reg)->size = lm_reg->size;
- if (lm_reg->mmap_areas != NULL) {
- err = dev_get_sparse_mmap_cap(lm_ctx, lm_reg, vfio_reg, is_kernel);
- if (err) {
- return err;
- }
+ err = dev_get_sparse_mmap_cap(lm_ctx, lm_reg, (*vfio_reg)->index, vfio_reg,
+ is_kernel);
+ if (err) {
+ return err;
}
lm_log(lm_ctx, LM_DBG, "region_info[%d] offset %#lx flags %#x size %llu "
@@ -1089,8 +1170,9 @@ dev_get_reginfo(lm_ctx_t *lm_ctx, struct vfio_region_info **vfio_reg,
}
static long
-dev_get_info(struct vfio_device_info *dev_info)
+dev_get_info(lm_ctx_t *lm_ctx, struct vfio_device_info *dev_info)
{
+ assert(lm_ctx != NULL);
assert(dev_info != NULL);
// Ensure provided argsz is sufficiently big.
@@ -1114,7 +1196,7 @@ do_muser_ioctl(lm_ctx_t *lm_ctx, struct muser_cmd_ioctl *cmd_ioctl, void *data)
assert(lm_ctx != NULL);
switch (cmd_ioctl->vfio_cmd) {
case VFIO_DEVICE_GET_INFO:
- err = dev_get_info(&cmd_ioctl->data.dev_info);
+ err = dev_get_info(lm_ctx, &cmd_ioctl->data.dev_info);
break;
case VFIO_DEVICE_GET_REGION_INFO:
reg_info = &cmd_ioctl->data.reg_info;
@@ -1737,7 +1819,7 @@ static int handle_device_get_info(lm_ctx_t *lm_ctx,
return -errno;
}
- ret = dev_get_info(dev_info);
+ ret = dev_get_info(lm_ctx, dev_info);
if (ret < 0) {
return ret;
}
@@ -1919,7 +2001,7 @@ handle_device_reset(lm_ctx_t *lm_ctx)
static int
handle_region_access(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr,
- void **data, int *len)
+ void **data, size_t *len)
{
struct vfio_user_region_access region_access;
struct muser_cmd muser_cmd = {};
@@ -1973,6 +2055,105 @@ handle_region_access(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr,
return 0;
}
+static int
+handle_dirty_pages_get(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr,
+ struct iovec **iovecs, size_t *nr_iovecs)
+{
+ int size, ret;
+ size_t i;
+ struct vfio_iommu_type1_dirty_bitmap_get *ranges;
+
+ assert(lm_ctx != NULL);
+ assert(hdr != NULL);
+ assert(iovecs != NULL);
+ assert(nr_iovecs != NULL);
+
+ size = hdr->msg_size - sizeof(*hdr) - sizeof(struct vfio_iommu_type1_dirty_bitmap);
+ if (size % sizeof(struct vfio_iommu_type1_dirty_bitmap_get) != 0) {
+ return -EINVAL;
+ }
+ ranges = malloc(size);
+ if (ranges == NULL) {
+ return -errno;
+ }
+ ret = recv(lm_ctx->conn_fd, ranges, size, 0);
+ if (ret == -1) {
+ ret = -errno;
+ goto out;
+ }
+ if (ret != size) {
+ ret = -EINVAL;
+ goto out;
+ }
+ *nr_iovecs = 1 + size / sizeof(struct vfio_iommu_type1_dirty_bitmap_get);
+ *iovecs = malloc(*nr_iovecs);
+ if (*iovecs == NULL) {
+ ret = -errno;
+ goto out;
+ }
+
+ for (i = 1; i < *nr_iovecs; i++) {
+ struct vfio_iommu_type1_dirty_bitmap_get *r = &ranges[(i - 1)]; /* FIXME ugly indexing */
+ ret = dma_controller_dirty_page_get(lm_ctx->dma, r->iova, r->size,
+ r->bitmap.pgsize, r->bitmap.size,
+ (char**)&((*iovecs)[i].iov_base));
+ if (ret != 0) {
+ goto out;
+ }
+ (*iovecs)[i].iov_len = r->bitmap.size;
+ }
+out:
+ if (ret != 0) {
+ if (*iovecs != NULL) {
+ free(*iovecs);
+ *iovecs = NULL;
+ }
+ }
+ free(ranges);
+ return ret;
+}
+
+static int
+handle_dirty_pages(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr,
+ struct iovec **iovecs, size_t *nr_iovecs)
+{
+ struct vfio_iommu_type1_dirty_bitmap dirty_bitmap;
+ int ret;
+
+ assert(lm_ctx != NULL);
+ assert(hdr != NULL);
+ assert(iovecs != NULL);
+ assert(nr_iovecs != NULL);
+
+ if (hdr->msg_size - sizeof *hdr < sizeof dirty_bitmap) {
+ lm_log(lm_ctx, LM_ERR, "invalid header size %lu", hdr->msg_size);
+ return -EINVAL;
+ }
+
+ /* FIXME must also check argsz */
+
+ ret = recv(lm_ctx->conn_fd, &dirty_bitmap, sizeof dirty_bitmap, 0);
+ if (ret == -1) {
+ return -errno;
+ }
+ if ((size_t)ret < sizeof dirty_bitmap) {
+ return -EINVAL;
+ }
+
+ if (dirty_bitmap.flags & VFIO_IOMMU_DIRTY_PAGES_FLAG_START) {
+ ret = dma_controller_dirty_page_logging_start(lm_ctx->dma,
+ lm_ctx->migration_pgsize);
+ } else if (dirty_bitmap.flags & VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP) {
+ ret = dma_controller_dirty_page_logging_stop(lm_ctx->dma);
+ } else if (dirty_bitmap.flags & VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP) {
+ ret = handle_dirty_pages_get(lm_ctx, hdr, iovecs, nr_iovecs);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
/*
* FIXME return value is messed up, sometimes we return -1 and set errno while
* other times we return -errno. Fix.
@@ -1988,9 +2169,9 @@ process_request(lm_ctx_t *lm_ctx)
struct vfio_irq_info irq_info;
struct vfio_device_info dev_info;
struct vfio_region_info *dev_reg_info = NULL;
- void *data = NULL;
- bool free_data = false;
- int len = 0;
+ struct iovec _iovecs[2] = {0}, *iovecs = NULL;
+ size_t nr_iovecs = 0;
+ bool free_iovec_data = true;
assert(lm_ctx != NULL);
@@ -2034,6 +2215,12 @@ process_request(lm_ctx_t *lm_ctx)
return -EINVAL;
}
+ /* FIXME in most of the following function we check that hdr.count is >=
+ * than the command-specific struct and there is an additional recv(2) for
+ * that data. We should eliminate duplicating this common code and move it
+ * here.
+ */
+
switch (hdr.cmd) {
case VFIO_USER_DMA_MAP:
case VFIO_USER_DMA_UNMAP:
@@ -2044,23 +2231,28 @@ process_request(lm_ctx_t *lm_ctx)
case VFIO_USER_DEVICE_GET_INFO:
ret = handle_device_get_info(lm_ctx, &hdr, &dev_info);
if (ret == 0) {
- data = &dev_info;
- len = dev_info.argsz;
+ _iovecs[1].iov_base = &dev_info;
+ _iovecs[1].iov_len = dev_info.argsz;
+ iovecs = _iovecs;
+ nr_iovecs = 2;
}
break;
case VFIO_USER_DEVICE_GET_REGION_INFO:
ret = handle_device_get_region_info(lm_ctx, &hdr, &dev_reg_info);
if (ret == 0) {
- data = dev_reg_info;
- len = dev_reg_info->argsz;
- free_data = true;
+ _iovecs[1].iov_base = dev_reg_info;
+ _iovecs[1].iov_len = dev_reg_info->argsz;
+ iovecs = _iovecs;
+ nr_iovecs = 2;
}
break;
case VFIO_USER_DEVICE_GET_IRQ_INFO:
ret = handle_device_get_irq_info(lm_ctx, &hdr, &irq_info);
if (ret == 0) {
- data = &irq_info;
- len = sizeof irq_info;
+ _iovecs[1].iov_base = &irq_info;
+ _iovecs[1].iov_len = sizeof irq_info;
+ iovecs = _iovecs;
+ nr_iovecs = 2;
}
break;
case VFIO_USER_DEVICE_SET_IRQS:
@@ -2068,12 +2260,20 @@ process_request(lm_ctx_t *lm_ctx)
break;
case VFIO_USER_REGION_READ:
case VFIO_USER_REGION_WRITE:
- ret = handle_region_access(lm_ctx, &hdr, &data, &len);
- free_data = true;
+ iovecs = _iovecs;
+ ret = handle_region_access(lm_ctx, &hdr, &iovecs[1].iov_base,
+ &iovecs[1].iov_len);
+ nr_iovecs = 2;
break;
case VFIO_USER_DEVICE_RESET:
ret = handle_device_reset(lm_ctx);
break;
+ case VFIO_USER_DIRTY_PAGES:
+ ret = handle_dirty_pages(lm_ctx, &hdr, &iovecs, &nr_iovecs);
+ if (ret >= 0) {
+ free_iovec_data = false;
+ }
+ break;
default:
lm_log(lm_ctx, LM_ERR, "bad command %d", hdr.cmd);
return -EINVAL;
@@ -2083,14 +2283,23 @@ process_request(lm_ctx_t *lm_ctx)
* TODO: In case of error during command handling set errno respectively
* in the reply message.
*/
- ret = send_vfio_user_msg(lm_ctx->conn_fd, hdr.msg_id, true,
- 0, data, len, NULL, 0);
+ if (ret < 0) {
+ assert(false); /* FIXME */
+ }
+ ret = _send_vfio_user_msg(lm_ctx->conn_fd, hdr.msg_id, true,
+ 0, iovecs, nr_iovecs, NULL, 0);
if (unlikely(ret < 0)) {
lm_log(lm_ctx, LM_ERR, "failed to complete command: %s\n",
strerror(-ret));
}
- if (free_data) {
- free(data);
+ if (iovecs != NULL && iovecs != _iovecs) {
+ if (free_iovec_data) {
+ size_t i;
+ for (i = 0; i < nr_iovecs; i++) {
+ free(iovecs[i].iov_base);
+ }
+ }
+ free(iovecs);
}
return ret;
@@ -2190,8 +2399,9 @@ lm_irq_message(lm_ctx_t *lm_ctx, uint32_t subindex)
irq_info.subindex = subindex;
ret = send_recv_vfio_user_msg(lm_ctx->conn_fd, msg_id,
- VFIO_USER_VM_INTERRUPT, &irq_info,
- sizeof(irq_info), NULL, 0, NULL, NULL, 0);
+ VFIO_USER_VM_INTERRUPT,
+ &irq_info, sizeof irq_info,
+ NULL, 0, NULL, NULL, 0);
if (ret < 0) {
errno = -ret;
return -1;
@@ -2291,6 +2501,7 @@ pci_config_setup(lm_ctx_t *lm_ctx, const lm_dev_info_t *dev_info)
{
lm_reg_info_t *cfg_reg;
const lm_reg_info_t zero_reg = { 0 };
+ lm_reg_info_t *migr_reg;
int i;
assert(lm_ctx != NULL);
@@ -2347,6 +2558,16 @@ pci_config_setup(lm_ctx_t *lm_ctx, const lm_dev_info_t *dev_info)
lm_ctx->pci_config_space->hdr.cap = PCI_STD_HEADER_SIZEOF;
}
+ /*
+ * Check the migration region.
+ */
+ migr_reg = &lm_ctx->pci_info.reg_info[LM_DEV_MIGRATION_REG_IDX];
+ if (migr_reg->size > 0) {
+ if (migr_reg->size < sizeof(struct vfio_device_migration_info)) {
+ return -EINVAL;
+ }
+ }
+
return 0;
err:
@@ -2529,13 +2750,15 @@ lm_get_region_info(lm_ctx_t *lm_ctx)
inline int
lm_addr_to_sg(lm_ctx_t *lm_ctx, dma_addr_t dma_addr,
- uint32_t len, dma_sg_t *sg, int max_sg)
+ uint32_t len, dma_sg_t *sg, int max_sg, int prot)
{
+ assert(lm_ctx != NULL);
+
if (unlikely(lm_ctx->unmap_dma == NULL)) {
errno = EINVAL;
return -1;
}
- return dma_addr_to_sg(lm_ctx->dma, dma_addr, len, sg, max_sg);
+ return dma_addr_to_sg(lm_ctx->dma, dma_addr, len, sg, max_sg, prot);
}
inline int
@@ -2581,62 +2804,54 @@ lm_ctx_get_cap(lm_ctx_t *lm_ctx, uint8_t id)
}
int
-lm_dma_read(lm_ctx_t *lm_ctx, dma_addr_t addr, size_t count, void *data)
+lm_dma_read(lm_ctx_t *lm_ctx, dma_sg_t *sg, void *data)
{
struct vfio_user_dma_region_access *dma_recv;
- struct vfio_user_dma_region_access dma_send = {
- .addr = addr,
- .count = count
- };
- int recv_size = sizeof(*dma_recv) + count;
+ struct vfio_user_dma_region_access dma_send;
+ int recv_size;
int msg_id = 1, ret;
- if (!dma_controller_region_valid(lm_ctx->dma, addr, count)) {
- lm_log(lm_ctx, LM_ERR, "DMA region addr %#lx count %llu doest not "
- "exists", addr, count);
- return -ENOENT;
- }
+ assert(lm_ctx != NULL);
+ assert(sg != NULL);
+
+ recv_size = sizeof(*dma_recv) + sg->length;
dma_recv = calloc(recv_size, 1);
if (dma_recv == NULL) {
return -ENOMEM;
}
- dma_recv->addr = addr;
- dma_recv->count = count;
+ dma_send.addr = sg->dma_addr;
+ dma_send.count = sg->length;
ret = send_recv_vfio_user_msg(lm_ctx->conn_fd, msg_id, VFIO_USER_DMA_READ,
- &dma_send, sizeof(dma_send), NULL, 0, NULL,
+ &dma_send, sizeof dma_send, NULL, 0, NULL,
dma_recv, recv_size);
- memcpy(data, dma_recv->data, count);
+ memcpy(data, dma_recv->data, sg->length); /* FIXME no need for memcpy */
free(dma_recv);
return ret;
}
int
-lm_dma_write(lm_ctx_t *lm_ctx, dma_addr_t addr, size_t count, void *data)
+lm_dma_write(lm_ctx_t *lm_ctx, dma_sg_t *sg, void *data)
{
struct vfio_user_dma_region_access *dma_send, dma_recv;
- int send_size = sizeof(*dma_send) + count;
+ int send_size = sizeof(*dma_send) + sg->length;
int msg_id = 1, ret;
- if (!dma_controller_region_valid(lm_ctx->dma, addr, count)) {
- lm_log(lm_ctx, LM_ERR, "DMA region addr %#lx count %llu does not "
- "exists", addr, count);
- return -ENOENT;
- }
+ assert(lm_ctx != NULL);
+ assert(sg != NULL);
dma_send = calloc(send_size, 1);
if (dma_send == NULL) {
return -ENOMEM;
}
- dma_send->addr = addr;
- dma_send->count = count;
- memcpy(dma_send->data, data, count);
-
+ dma_send->addr = sg->dma_addr;
+ dma_send->count = sg->length;
+ memcpy(dma_send->data, data, sg->length); /* FIXME no need to copy! */
ret = send_recv_vfio_user_msg(lm_ctx->conn_fd, msg_id, VFIO_USER_DMA_WRITE,
- dma_send, send_size, NULL, 0, NULL, &dma_recv,
- sizeof(dma_recv));
+ dma_send, send_size,
+ NULL, 0, NULL, &dma_recv, sizeof(dma_recv));
free(dma_send);
return ret;