aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libvfio-user.c374
-rw-r--r--lib/private.h3
2 files changed, 200 insertions, 177 deletions
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c
index 44641c2..0a61f13 100644
--- a/lib/libvfio-user.c
+++ b/lib/libvfio-user.c
@@ -1062,6 +1062,118 @@ free_msg(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
errno = saved_errno;
}
+static int
+do_reply(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int reply_errno)
+{
+ int ret;
+
+ assert(vfu_ctx != NULL);
+ assert(msg != NULL);
+
+ if (msg->hdr.flags.no_reply) {
+ /*
+ * A failed client request is not a failure of handle_request() itself.
+ */
+ return 0;
+ }
+
+ ret = vfu_ctx->tran->reply(vfu_ctx, msg, reply_errno);
+
+ if (ret < 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "failed to reply: %m");
+
+ if (errno == ECONNRESET || errno == ENOMSG) {
+ ret = vfu_reset_ctx(vfu_ctx, errno);
+ if (ret < 0) {
+ if (errno != EBUSY) {
+ vfu_log(vfu_ctx, LOG_WARNING, "failed to reset context: %m");
+ }
+ return ret;
+ }
+ errno = ENOTCONN;
+ }
+ }
+
+ return ret;
+}
+
+static int
+handle_request(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+{
+ int ret = 0;
+
+ assert(vfu_ctx != NULL);
+ assert(msg != NULL);
+
+ msg->processed_cmd = true;
+
+ switch (msg->hdr.cmd) {
+ case VFIO_USER_DMA_MAP:
+ if (vfu_ctx->dma != NULL) {
+ ret = handle_dma_map(vfu_ctx, msg, msg->in_data);
+ }
+ break;
+
+ case VFIO_USER_DMA_UNMAP:
+ if (vfu_ctx->dma != NULL) {
+ ret = handle_dma_unmap(vfu_ctx, msg, msg->in_data);
+ }
+ break;
+
+ case VFIO_USER_DEVICE_GET_INFO:
+ ret = handle_device_get_info(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_DEVICE_GET_REGION_INFO:
+ ret = handle_device_get_region_info(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_DEVICE_GET_REGION_IO_FDS:
+ ret = handle_device_get_region_io_fds(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_DEVICE_GET_IRQ_INFO:
+ ret = handle_device_get_irq_info(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_DEVICE_SET_IRQS:
+ ret = handle_device_set_irqs(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_REGION_READ:
+ case VFIO_USER_REGION_WRITE:
+ ret = handle_region_access(vfu_ctx, msg);
+ break;
+
+ case VFIO_USER_DEVICE_RESET:
+ vfu_log(vfu_ctx, LOG_INFO, "device reset by client");
+ ret = handle_device_reset(vfu_ctx, VFU_RESET_DEVICE);
+ break;
+
+ case VFIO_USER_DIRTY_PAGES:
+ // FIXME: don't allow migration calls if migration == NULL
+ if (vfu_ctx->dma != NULL) {
+ ret = handle_dirty_pages(vfu_ctx, msg);
+ } else {
+ ret = 0;
+ }
+ break;
+
+ default:
+ msg->processed_cmd = false;
+ vfu_log(vfu_ctx, LOG_ERR, "bad command %d", msg->hdr.cmd);
+ ret = ERROR_INT(EINVAL);
+ break;
+ }
+
+ if (ret < 0) {
+ vfu_log(vfu_ctx, LOG_ERR, "msg%#hx: cmd %d failed: %m",
+ msg->hdr.msg_id, msg->hdr.cmd);
+ }
+
+ return do_reply(vfu_ctx, msg, ret == 0 ? 0 : errno);
+}
+
/*
* Note that we avoid any malloc() before we see data, as this is used for
* polling by SPDK.
@@ -1215,180 +1327,127 @@ command_needs_quiesce(vfu_ctx_t *vfu_ctx, const vfu_msg_t *msg)
return false;
}
-int
-exec_command(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+/*
+ * Acquire a request from the vfio-user socket. Returns 0 on success, or -1 with
+ * errno set as follows:
+ *
+ * EAGAIN/EWOULDBLOCK: no request was ready to read from the socket
+ * ENOMSG: a message was read and replied to, no further handling is needed.
+ * E*: other errors that should be returned to the caller
+ */
+static int
+get_request(vfu_ctx_t *vfu_ctx, vfu_msg_t **msgp)
{
- int ret = 0;
-
- msg->processed_cmd = true;
-
- switch (msg->hdr.cmd) {
- case VFIO_USER_DMA_MAP:
- if (vfu_ctx->dma != NULL) {
- ret = handle_dma_map(vfu_ctx, msg, msg->in_data);
- }
- break;
-
- case VFIO_USER_DMA_UNMAP:
- if (vfu_ctx->dma != NULL) {
- ret = handle_dma_unmap(vfu_ctx, msg, msg->in_data);
- }
- break;
+ vfu_msg_t *msg = NULL;
+ int ret;
- case VFIO_USER_DEVICE_GET_INFO:
- ret = handle_device_get_info(vfu_ctx, msg);
- break;
+ assert(vfu_ctx != NULL);
- case VFIO_USER_DEVICE_GET_REGION_INFO:
- ret = handle_device_get_region_info(vfu_ctx, msg);
- break;
+ *msgp = NULL;
- case VFIO_USER_DEVICE_GET_REGION_IO_FDS:
- ret = handle_device_get_region_io_fds(vfu_ctx, msg);
- break;
+ ret = get_request_header(vfu_ctx, &msg);
- case VFIO_USER_DEVICE_GET_IRQ_INFO:
- ret = handle_device_get_irq_info(vfu_ctx, msg);
- break;
+ if (ret < 0) {
+ return ret;
+ }
- case VFIO_USER_DEVICE_SET_IRQS:
- ret = handle_device_set_irqs(vfu_ctx, msg);
- break;
+ if (!is_valid_header(vfu_ctx, msg)) {
+ ret = ERROR_INT(EINVAL);
+ goto err;
+ }
- case VFIO_USER_REGION_READ:
- case VFIO_USER_REGION_WRITE:
- ret = handle_region_access(vfu_ctx, msg);
- break;
+ msg->in_size = msg->hdr.msg_size - sizeof(msg->hdr);
- case VFIO_USER_DEVICE_RESET:
- vfu_log(vfu_ctx, LOG_INFO, "device reset by client");
- ret = handle_device_reset(vfu_ctx, VFU_RESET_DEVICE);
- break;
+ if (msg->in_size > 0) {
+ ret = vfu_ctx->tran->recv_body(vfu_ctx, msg);
- case VFIO_USER_DIRTY_PAGES:
- // FIXME: don't allow migration calls if migration == NULL
- if (vfu_ctx->dma != NULL) {
- ret = handle_dirty_pages(vfu_ctx, msg);
- } else {
- ret = 0;
+ if (ret < 0) {
+ goto err;
}
- break;
+ }
- default:
- msg->processed_cmd = false;
- vfu_log(vfu_ctx, LOG_ERR, "bad command %d", msg->hdr.cmd);
+ if (!should_exec_command(vfu_ctx, msg->hdr.cmd)) {
ret = ERROR_INT(EINVAL);
- break;
+ goto err;
}
- return ret;
-}
+ if (command_needs_quiesce(vfu_ctx, msg)) {
+ vfu_log(vfu_ctx, LOG_DEBUG, "quiescing device");
+ ret = vfu_ctx->quiesce(vfu_ctx);
+ if (ret < 0) {
+ if (errno != EBUSY) {
+ vfu_log(vfu_ctx, LOG_DEBUG, "device failed to quiesce: %m");
+ goto err;
+ }
-static int
-do_reply(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int reply_errno)
-{
- assert(vfu_ctx != NULL);
- assert(msg != NULL);
+ vfu_log(vfu_ctx, LOG_DEBUG, "device will quiesce asynchronously");
+ vfu_ctx->pending.state = VFU_CTX_PENDING_MSG;
+ vfu_ctx->pending.msg = msg;
+ /* NB the message is freed in vfu_device_quiesced */
+ return ret;
+ }
- int ret = vfu_ctx->tran->reply(vfu_ctx, msg, reply_errno);
+ vfu_log(vfu_ctx, LOG_DEBUG, "device quiesced immediately");
+ }
- if (ret < 0) {
- vfu_log(vfu_ctx, LOG_ERR, "failed to reply: %m");
+ *msgp = msg;
+ return 0;
- if (errno == ECONNRESET || errno == ENOMSG) {
- ret = vfu_reset_ctx(vfu_ctx, errno);
- if (ret < 0) {
- if (errno != EBUSY) {
- vfu_log(vfu_ctx, LOG_WARNING, "failed to reset context: %m");
- }
- return ret;
- }
- errno = ENOTCONN;
- }
+err:
+ ret = do_reply(vfu_ctx, msg, ret == 0 ? 0 : errno);
+ free_msg(vfu_ctx, msg);
+ if (ret != 0) {
+ return ret;
}
- return ret;
+ /* We handled the message already. */
+ return ERROR_INT(ENOMSG);
}
-/*
- * Handle requests over the vfio-user socket. This can return immediately if we
- * are non-blocking, and there is no request from the client ready to read from
- * the socket. Otherwise, we synchronously process the request in place, and
- * possibly reply.
- */
-static int
-process_request(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
+EXPORT int
+vfu_run_ctx(vfu_ctx_t *vfu_ctx)
{
- int ret;
+ int reqs_processed = 0;
+ bool blocking;
+ int err;
assert(vfu_ctx != NULL);
- if (msg == NULL) {
- ret = get_request_header(vfu_ctx, &msg);
-
- if (ret < 0) {
- return ret;
- }
-
- if (!is_valid_header(vfu_ctx, msg)) {
- ret = ERROR_INT(EINVAL);
- goto out;
- }
+ if (!vfu_ctx->realized) {
+ vfu_log(vfu_ctx, LOG_DEBUG, "device not realized");
+ return ERROR_INT(EINVAL);
+ }
- msg->in_size = msg->hdr.msg_size - sizeof(msg->hdr);
+ blocking = !(vfu_ctx->flags & LIBVFIO_USER_FLAG_ATTACH_NB);
- if (msg->in_size > 0) {
- ret = vfu_ctx->tran->recv_body(vfu_ctx, msg);
+ do {
+ vfu_msg_t *msg;
- if (ret < 0) {
- goto out;
- }
+ if (vfu_ctx->pending.state != VFU_CTX_PENDING_NONE) {
+ return ERROR_INT(EBUSY);
}
- if (!should_exec_command(vfu_ctx, msg->hdr.cmd)) {
- ret = ERROR_INT(EINVAL);
- goto out;
- }
+ err = get_request(vfu_ctx, &msg);
- if (command_needs_quiesce(vfu_ctx, msg)) {
- vfu_log(vfu_ctx, LOG_DEBUG, "quiescing device");
- ret = vfu_ctx->quiesce(vfu_ctx);
- if (ret < 0) {
- if (errno == EBUSY) {
- vfu_log(vfu_ctx, LOG_DEBUG, "device will quiesce asynchronously");
- vfu_ctx->pending.state = VFU_CTX_PENDING_MSG;
- vfu_ctx->pending.msg = msg;
- /* NB the message is freed in vfu_device_quiesced */
- return ret;
- } else {
- vfu_log(vfu_ctx, LOG_DEBUG, "device failed to quiesce: %m");
- }
- goto out;
- } else {
- vfu_log(vfu_ctx, LOG_DEBUG, "device quiesced immediately");
+ if (err == 0) {
+ err = handle_request(vfu_ctx, msg);
+ free_msg(vfu_ctx, msg);
+ reqs_processed++;
+ } else {
+ /*
+ * If there was no request to read, or we already handled the
+ * (error) reply, that's not a failure of vfu_run_ctx() itself.
+ */
+ switch (errno) {
+ case ENOMSG:
+ case EAGAIN:
+ err = 0;
+ break;
}
}
- }
- errno = 0;
-
- ret = exec_command(vfu_ctx, msg);
-
- if (ret < 0 && errno != EBUSY) {
- vfu_log(vfu_ctx, LOG_ERR, "msg%#hx: cmd %d failed: %m", msg->hdr.msg_id,
- msg->hdr.cmd);
- }
+ } while (err == 0 && blocking);
-out:
- if (msg->hdr.flags.no_reply) {
- /*
- * A failed client request is not a failure of process_request() itself.
- */
- ret = 0;
- } else {
- ret = do_reply(vfu_ctx, msg, ret == 0 ? 0 : errno);
- }
- free_msg(vfu_ctx, msg);
- return ret;
+ return err == 0 ? reqs_processed : err;
}
EXPORT int
@@ -1469,40 +1528,6 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx)
return 0;
}
-EXPORT int
-vfu_run_ctx(vfu_ctx_t *vfu_ctx)
-{
- int reqs_processed = 0;
- bool blocking;
- int err;
-
- assert(vfu_ctx != NULL);
-
- if (!vfu_ctx->realized) {
- vfu_log(vfu_ctx, LOG_DEBUG, "device not realized");
- return ERROR_INT(EINVAL);
- }
-
- blocking = !(vfu_ctx->flags & LIBVFIO_USER_FLAG_ATTACH_NB);
-
- do {
- if (vfu_ctx->pending.state != VFU_CTX_PENDING_NONE) {
- return ERROR_INT(EBUSY);
- }
- err = process_request(vfu_ctx, NULL);
-
- if (err == 0) {
- reqs_processed++;
- } else {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- err = 0;
- }
- }
- } while (err == 0 && blocking);
-
- return err == 0 ? reqs_processed : err;
-}
-
static void
free_sparse_mmap_areas(vfu_ctx_t *vfu_ctx)
{
@@ -2109,7 +2134,8 @@ vfu_device_quiesced(vfu_ctx_t *vfu_ctx, int quiesce_errno)
if (quiesce_errno == 0) {
switch (vfu_ctx->pending.state) {
case VFU_CTX_PENDING_MSG:
- ret = process_request(vfu_ctx, vfu_ctx->pending.msg);
+ ret = handle_request(vfu_ctx, vfu_ctx->pending.msg);
+ free_msg(vfu_ctx, vfu_ctx->pending.msg);
break;
case VFU_CTX_PENDING_CTX_RESET:
vfu_reset_ctx_quiesced(vfu_ctx);
diff --git a/lib/private.h b/lib/private.h
index 2e9a769..ae70a4f 100644
--- a/lib/private.h
+++ b/lib/private.h
@@ -228,9 +228,6 @@ int
consume_fd(int *fds, size_t nr_fds, size_t index);
int
-exec_command(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg);
-
-int
handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg,
struct vfio_user_dma_map *dma_map);