aboutsummaryrefslogtreecommitdiff
path: root/lib/tran.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tran.c')
-rw-r--r--lib/tran.c95
1 files changed, 85 insertions, 10 deletions
diff --git a/lib/tran.c b/lib/tran.c
index 3c8b25a..46f5874 100644
--- a/lib/tran.c
+++ b/lib/tran.c
@@ -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;