diff options
Diffstat (limited to 'lib/tran.c')
-rw-r--r-- | lib/tran.c | 95 |
1 files changed, 85 insertions, 10 deletions
@@ -36,6 +36,7 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <sys/socket.h> #include <json.h> @@ -52,9 +53,13 @@ * { * "capabilities": { * "max_msg_fds": 32, - * "max_data_xfer_size": 1048576 + * "max_data_xfer_size": 1048576, * "migration": { * "pgsize": 4096 + * }, + * "twin_socket": { + * "supported": true, + * "fd_index": 0 * } * } * } @@ -64,7 +69,8 @@ */ int tran_parse_version_json(const char *json_str, int *client_max_fdsp, - size_t *client_max_data_xfer_sizep, size_t *pgsizep) + size_t *client_max_data_xfer_sizep, size_t *pgsizep, + bool *twin_socket_supportedp) { struct json_object *jo_caps = NULL; struct json_object *jo_top = NULL; @@ -130,6 +136,27 @@ tran_parse_version_json(const char *json_str, int *client_max_fdsp, } } + if (json_object_object_get_ex(jo_caps, "twin_socket", &jo)) { + struct json_object *jo2 = NULL; + + if (json_object_get_type(jo) != json_type_object) { + goto out; + } + + if (json_object_object_get_ex(jo, "supported", &jo2)) { + if (json_object_get_type(jo2) != json_type_boolean) { + goto out; + } + + errno = 0; + *twin_socket_supportedp = json_object_get_boolean(jo2); + + if (errno != 0) { + goto out; + } + } + } + ret = 0; out: @@ -143,7 +170,7 @@ out: static int recv_version(vfu_ctx_t *vfu_ctx, uint16_t *msg_idp, - struct vfio_user_version **versionp) + struct vfio_user_version **versionp, bool *twin_socket_supportedp) { struct vfio_user_version *cversion = NULL; vfu_msg_t msg = { { 0 } }; @@ -208,7 +235,7 @@ recv_version(vfu_ctx_t *vfu_ctx, uint16_t *msg_idp, ret = tran_parse_version_json(json_str, &vfu_ctx->client_max_fds, &vfu_ctx->client_max_data_xfer_size, - &pgsize); + &pgsize, twin_socket_supportedp); if (ret < 0) { /* No client-supplied strings in the log for release build. */ @@ -312,8 +339,9 @@ json_add_uint64(struct json_object *jso, const char *key, uint64_t value) * be freed by the caller. */ static char * -format_server_capabilities(vfu_ctx_t *vfu_ctx) +format_server_capabilities(vfu_ctx_t *vfu_ctx, int twin_socket_fd_index) { + struct json_object *jo_twin_socket = NULL; struct json_object *jo_migration = NULL; struct json_object *jo_caps = NULL; struct json_object *jo_top = NULL; @@ -347,6 +375,25 @@ format_server_capabilities(vfu_ctx_t *vfu_ctx) } } + if (twin_socket_fd_index >= 0) { + struct json_object *jo_supported = NULL; + + if ((jo_twin_socket = json_object_new_object()) == NULL) { + goto out; + } + + if ((jo_supported = json_object_new_boolean(true)) == NULL || + json_add(jo_twin_socket, "supported", &jo_supported) < 0 || + json_add_uint64(jo_twin_socket, "fd_index", + twin_socket_fd_index) < 0) { + goto out; + } + + if (json_add(jo_caps, "twin_socket", &jo_twin_socket) < 0) { + goto out; + } + } + if ((jo_top = json_object_new_object()) == NULL || json_add(jo_top, "capabilities", &jo_caps) < 0) { goto out; @@ -355,6 +402,7 @@ format_server_capabilities(vfu_ctx_t *vfu_ctx) caps_str = strdup(json_object_to_json_string(jo_top)); out: + json_object_put(jo_twin_socket); json_object_put(jo_migration); json_object_put(jo_caps); json_object_put(jo_top); @@ -363,15 +411,17 @@ out: static int send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id, - struct vfio_user_version *cversion) + struct vfio_user_version *cversion, int client_cmd_socket_fd) { + int twin_socket_fd_index = client_cmd_socket_fd >= 0 ? 0 : -1; struct vfio_user_version sversion = { 0 }; struct iovec iovecs[2] = { { 0 } }; vfu_msg_t msg = { { 0 } }; char *server_caps = NULL; int ret; - if ((server_caps = format_server_capabilities(vfu_ctx)) == NULL) { + server_caps = format_server_capabilities(vfu_ctx, twin_socket_fd_index); + if (server_caps == NULL) { errno = ENOMEM; return -1; } @@ -391,6 +441,11 @@ send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id, msg.hdr.msg_id = msg_id; msg.out_iovecs = iovecs; msg.nr_out_iovecs = 2; + if (client_cmd_socket_fd >= 0) { + msg.out.fds = &client_cmd_socket_fd; + msg.out.nr_fds = 1; + assert(msg.out.fds[twin_socket_fd_index] == client_cmd_socket_fd); + } ret = vfu_ctx->tran->reply(vfu_ctx, &msg, 0); free(server_caps); @@ -398,25 +453,45 @@ send_version(vfu_ctx_t *vfu_ctx, uint16_t msg_id, } int -tran_negotiate(vfu_ctx_t *vfu_ctx) +tran_negotiate(vfu_ctx_t *vfu_ctx, int *client_cmd_socket_fdp) { struct vfio_user_version *client_version = NULL; + int client_cmd_socket_fds[2] = { -1, -1 }; + bool twin_socket_supported = false; uint16_t msg_id = 0x0bad; int ret; - ret = recv_version(vfu_ctx, &msg_id, &client_version); + ret = recv_version(vfu_ctx, &msg_id, &client_version, + &twin_socket_supported); if (ret < 0) { vfu_log(vfu_ctx, LOG_ERR, "failed to recv version: %m"); return ret; } - ret = send_version(vfu_ctx, msg_id, client_version); + if (twin_socket_supported && client_cmd_socket_fdp != NULL && + vfu_ctx->client_max_fds > 0) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, client_cmd_socket_fds) == -1) { + vfu_log(vfu_ctx, LOG_ERR, "failed to create cmd socket: %m"); + return -1; + } + } + + ret = send_version(vfu_ctx, msg_id, client_version, + client_cmd_socket_fds[0]); free(client_version); + /* + * The remote end of the client command socket pair is no longer needed. + * The local end is kept only if passed to the caller on successful return. + */ + close_safely(&client_cmd_socket_fds[0]); if (ret < 0) { vfu_log(vfu_ctx, LOG_ERR, "failed to send version: %m"); + close_safely(&client_cmd_socket_fds[1]); + } else if (client_cmd_socket_fdp != NULL) { + *client_cmd_socket_fdp = client_cmd_socket_fds[1]; } return ret; |