aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorThanos Makatos <thanos.makatos@nutanix.com>2021-10-05 13:54:26 +0100
committerGitHub <noreply@github.com>2021-10-05 13:54:26 +0100
commit8d82bd5f20fac5d8b4dab510d2294e076a6dd93d (patch)
treecb287e9436e387d14ca0d81a2182dd10fca8bd86 /lib
parente7f0fc73cdab811948adf5227c04f722a7c3105b (diff)
downloadlibvfio-user-8d82bd5f20fac5d8b4dab510d2294e076a6dd93d.zip
libvfio-user-8d82bd5f20fac5d8b4dab510d2294e076a6dd93d.tar.gz
libvfio-user-8d82bd5f20fac5d8b4dab510d2294e076a6dd93d.tar.bz2
make migration state callback optionally asynchronous (#608)
Some devices need the migration state callback to be asynchronous. The simplest way to implement this is to require from the callback to return -1 and set errno to EBUSY, not process any other new messages (vfu_ctx_run returns -1 and sets errno to EBUSY), and provide a way to the user to complete migration (vfu_migr_done). Signed-off-by: Thanos Makatos <thanos.makatos@nutanix.com> Reviewed-by: John Levon <john.levon@nutanix.com> Reviewed-by: Swapnil Ingle <swapnil.ingle@nutanix.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/libvfio-user.c76
-rw-r--r--lib/migration.c5
-rw-r--r--lib/private.h2
3 files changed, 69 insertions, 14 deletions
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c
index 25b58e0..a56d34c 100644
--- a/lib/libvfio-user.c
+++ b/lib/libvfio-user.c
@@ -335,7 +335,18 @@ handle_region_access(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
ret = region_access(vfu_ctx, in_ra->region, buf, in_ra->count,
in_ra->offset, msg->hdr.cmd == VFIO_USER_REGION_WRITE);
-
+ if (ret == -1 && in_ra->region == VFU_PCI_DEV_MIGR_REGION_IDX
+ && errno == EBUSY && (vfu_ctx->flags & LIBVFIO_USER_FLAG_ATTACH_NB)) {
+ /*
+ * We don't support async behavior for the non-blocking mode simply
+ * because we don't have a use case yet, the only user of migration
+ * is SPDK and it operates in non-blocking mode. We don't know the
+ * implications of enabling this in blocking mode as we haven't looked
+ * at the details.
+ */
+ vfu_ctx->migr_trans_pending = true;
+ return 0;
+ }
if (ret != in_ra->count) {
vfu_log(vfu_ctx, LOG_ERR, "failed to %s %#lx-%#lx: %m",
msg->hdr.cmd == VFIO_USER_REGION_WRITE ? "write" : "read",
@@ -1209,6 +1220,29 @@ exec_command(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg)
return ret;
}
+static int
+do_reply(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg, int reply_errno)
+{
+ assert(vfu_ctx != NULL);
+ assert(msg != NULL);
+
+ int 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) {
+ vfu_reset_ctx(vfu_ctx, "reset");
+ errno = ENOTCONN;
+ } else if (errno == ENOMSG) {
+ vfu_reset_ctx(vfu_ctx, "closed");
+ errno = ENOTCONN;
+ }
+ }
+
+ return ret;
+}
+
/*
* 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
@@ -1257,25 +1291,19 @@ process_request(vfu_ctx_t *vfu_ctx)
}
out:
+ if (vfu_ctx->migr_trans_pending) {
+ assert(ret == 0);
+ vfu_ctx->migr_trans_msg = msg;
+ /* NB the message is freed in vfu_migr_done */
+ return 0;
+ }
if (msg->hdr.flags.no_reply) {
/*
* A failed client request is not a failure of process_request() itself.
*/
ret = 0;
} else {
- ret = vfu_ctx->tran->reply(vfu_ctx, msg, ret == 0 ? 0 : errno);
-
- if (ret < 0) {
- vfu_log(vfu_ctx, LOG_ERR, "failed to reply: %m");
-
- if (errno == ECONNRESET) {
- vfu_reset_ctx(vfu_ctx, "reset");
- errno = ENOTCONN;
- } else if (errno == ENOMSG) {
- vfu_reset_ctx(vfu_ctx, "closed");
- errno = ENOTCONN;
- }
- }
+ ret = do_reply(vfu_ctx, msg, ret == 0 ? 0 : errno);
}
free_msg(vfu_ctx, msg);
@@ -1376,6 +1404,9 @@ vfu_run_ctx(vfu_ctx_t *vfu_ctx)
blocking = !(vfu_ctx->flags & LIBVFIO_USER_FLAG_ATTACH_NB);
do {
+ if (vfu_ctx->migr_trans_pending) {
+ return ERROR_INT(EBUSY);
+ }
err = process_request(vfu_ctx);
if (err == 0) {
@@ -1928,12 +1959,14 @@ vfu_dma_transfer(vfu_ctx_t *vfu_ctx, enum vfio_user_command cmd,
EXPORT int
vfu_dma_read(vfu_ctx_t *vfu_ctx, dma_sg_t *sg, void *data)
{
+ assert(!vfu_ctx->migr_trans_pending);
return vfu_dma_transfer(vfu_ctx, VFIO_USER_DMA_READ, sg, data);
}
EXPORT int
vfu_dma_write(vfu_ctx_t *vfu_ctx, dma_sg_t *sg, void *data)
{
+ assert(!vfu_ctx->migr_trans_pending);
return vfu_dma_transfer(vfu_ctx, VFIO_USER_DMA_WRITE, sg, data);
}
@@ -1943,4 +1976,19 @@ vfu_sg_is_mappable(vfu_ctx_t *vfu_ctx, dma_sg_t *sg)
return dma_sg_is_mappable(vfu_ctx->dma, sg);
}
+EXPORT void
+vfu_migr_done(vfu_ctx_t *vfu_ctx, int reply_errno)
+{
+ assert(vfu_ctx != NULL);
+ assert(vfu_ctx->migr_trans_pending);
+
+ if (!vfu_ctx->migr_trans_msg->hdr.flags.no_reply) {
+ do_reply(vfu_ctx, vfu_ctx->migr_trans_msg, reply_errno);
+ }
+ free_msg(vfu_ctx, vfu_ctx->migr_trans_msg);
+ vfu_ctx->migr_trans_msg = NULL;
+
+ vfu_ctx->migr_trans_pending = false;
+}
+
/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lib/migration.c b/lib/migration.c
index c1fe0b5..c8b97fa 100644
--- a/lib/migration.c
+++ b/lib/migration.c
@@ -423,6 +423,11 @@ MOCK_DEFINE(migration_region_access_registers)(vfu_ctx_t *vfu_ctx, char *buf,
"migration: transition from state %s to state %s",
migr_states[old_device_state].name,
migr_states[*device_state].name);
+ } else if (errno == EBUSY) {
+ vfu_log(vfu_ctx, LOG_DEBUG,
+ "migration: transition from state %s to state %s deferred",
+ migr_states[old_device_state].name,
+ migr_states[*device_state].name);
} else {
vfu_log(vfu_ctx, LOG_ERR,
"migration: failed to transition from state %s to state %s",
diff --git a/lib/private.h b/lib/private.h
index 93a354b..05d2fa4 100644
--- a/lib/private.h
+++ b/lib/private.h
@@ -169,6 +169,8 @@ struct vfu_ctx {
size_t client_max_data_xfer_size;
struct migration *migration;
+ bool migr_trans_pending;
+ vfu_msg_t *migr_trans_msg;
uint32_t irq_count[VFU_DEV_NUM_IRQS];
vfu_irqs_t *irqs;