um: virtio: Implement VHOST_USER_PROTOCOL_F_REPLY_ACK
authorJohannes Berg <johannes.berg@intel.com>
Wed, 11 Sep 2019 12:51:22 +0000 (14:51 +0200)
committerRichard Weinberger <richard@nod.at>
Sun, 15 Sep 2019 19:37:16 +0000 (21:37 +0200)
Implement the VHOST_USER_PROTOCOL_F_REPLY_ACK extension for both
slave requests (previous patch) where we have to reply and our
own requests where it helps understand if the slave failed.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
arch/um/drivers/vhost_user.h
arch/um/drivers/virtio_uml.c

index 8aee991..45ff5ea 100644 (file)
@@ -6,9 +6,11 @@
 
 /* Message flags */
 #define VHOST_USER_FLAG_REPLY          BIT(2)
+#define VHOST_USER_FLAG_NEED_REPLY     BIT(3)
 /* Feature bits */
 #define VHOST_USER_F_PROTOCOL_FEATURES 30
 /* Protocol feature bits */
+#define VHOST_USER_PROTOCOL_F_REPLY_ACK                3
 #define VHOST_USER_PROTOCOL_F_SLAVE_REQ                5
 #define VHOST_USER_PROTOCOL_F_CONFIG           9
 /* Vring state index masks */
@@ -20,7 +22,8 @@
 /* Supported transport features */
 #define VHOST_USER_SUPPORTED_F         BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES)
 /* Supported protocol features */
-#define VHOST_USER_SUPPORTED_PROTOCOL_F        (BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \
+#define VHOST_USER_SUPPORTED_PROTOCOL_F        (BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \
+                                        BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \
                                         BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG))
 
 enum vhost_user_request {
index ea4d9c1..fc8c52c 100644 (file)
@@ -160,30 +160,64 @@ static int vhost_user_recv_req(struct virtio_uml_device *vu_dev,
        if (rc)
                return rc;
 
-       if (msg->header.flags != VHOST_USER_VERSION)
+       if ((msg->header.flags & ~VHOST_USER_FLAG_NEED_REPLY) !=
+                       VHOST_USER_VERSION)
                return -EPROTO;
 
        return 0;
 }
 
 static int vhost_user_send(struct virtio_uml_device *vu_dev,
-                          struct vhost_user_msg *msg,
+                          bool need_response, struct vhost_user_msg *msg,
                           int *fds, size_t num_fds)
 {
        size_t size = sizeof(msg->header) + msg->header.size;
+       bool request_ack;
+       int rc;
 
        msg->header.flags |= VHOST_USER_VERSION;
-       return full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
+
+       /*
+        * The need_response flag indicates that we already need a response,
+        * e.g. to read the features. In these cases, don't request an ACK as
+        * it is meaningless. Also request an ACK only if supported.
+        */
+       request_ack = !need_response;
+       if (!(vu_dev->protocol_features &
+                       BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK)))
+               request_ack = false;
+
+       if (request_ack)
+               msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY;
+
+       rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
+       if (rc < 0)
+               return rc;
+
+       if (request_ack) {
+               uint64_t status;
+
+               rc = vhost_user_recv_u64(vu_dev, &status);
+               if (rc)
+                       return rc;
+
+               if (status) {
+                       vu_err(vu_dev, "slave reports error: %llu\n", status);
+                       return -EIO;
+               }
+       }
+
+       return 0;
 }
 
 static int vhost_user_send_no_payload(struct virtio_uml_device *vu_dev,
-                                     u32 request)
+                                     bool need_response, u32 request)
 {
        struct vhost_user_msg msg = {
                .header.request = request,
        };
 
-       return vhost_user_send(vu_dev, &msg, NULL, 0);
+       return vhost_user_send(vu_dev, need_response, &msg, NULL, 0);
 }
 
 static int vhost_user_send_no_payload_fd(struct virtio_uml_device *vu_dev,
@@ -193,7 +227,7 @@ static int vhost_user_send_no_payload_fd(struct virtio_uml_device *vu_dev,
                .header.request = request,
        };
 
-       return vhost_user_send(vu_dev, &msg, &fd, 1);
+       return vhost_user_send(vu_dev, false, &msg, &fd, 1);
 }
 
 static int vhost_user_send_u64(struct virtio_uml_device *vu_dev,
@@ -205,18 +239,19 @@ static int vhost_user_send_u64(struct virtio_uml_device *vu_dev,
                .payload.integer = value,
        };
 
-       return vhost_user_send(vu_dev, &msg, NULL, 0);
+       return vhost_user_send(vu_dev, false, &msg, NULL, 0);
 }
 
 static int vhost_user_set_owner(struct virtio_uml_device *vu_dev)
 {
-       return vhost_user_send_no_payload(vu_dev, VHOST_USER_SET_OWNER);
+       return vhost_user_send_no_payload(vu_dev, false, VHOST_USER_SET_OWNER);
 }
 
 static int vhost_user_get_features(struct virtio_uml_device *vu_dev,
                                   u64 *features)
 {
-       int rc = vhost_user_send_no_payload(vu_dev, VHOST_USER_GET_FEATURES);
+       int rc = vhost_user_send_no_payload(vu_dev, true,
+                                           VHOST_USER_GET_FEATURES);
 
        if (rc)
                return rc;
@@ -232,7 +267,7 @@ static int vhost_user_set_features(struct virtio_uml_device *vu_dev,
 static int vhost_user_get_protocol_features(struct virtio_uml_device *vu_dev,
                                            u64 *protocol_features)
 {
-       int rc = vhost_user_send_no_payload(vu_dev,
+       int rc = vhost_user_send_no_payload(vu_dev, true,
                        VHOST_USER_GET_PROTOCOL_FEATURES);
 
        if (rc)
@@ -247,9 +282,32 @@ static int vhost_user_set_protocol_features(struct virtio_uml_device *vu_dev,
                                   protocol_features);
 }
 
+static void vhost_user_reply(struct virtio_uml_device *vu_dev,
+                            struct vhost_user_msg *msg, int response)
+{
+       struct vhost_user_msg reply = {
+               .payload.integer = response,
+       };
+       size_t size = sizeof(reply.header) + sizeof(reply.payload.integer);
+       int rc;
+
+       reply.header = msg->header;
+       reply.header.flags &= ~VHOST_USER_FLAG_NEED_REPLY;
+       reply.header.flags |= VHOST_USER_FLAG_REPLY;
+       reply.header.size = sizeof(reply.payload.integer);
+
+       rc = full_sendmsg_fds(vu_dev->req_fd, &reply, size, NULL, 0);
+
+       if (rc)
+               vu_err(vu_dev,
+                      "sending reply to slave request failed: %d (size %zu)\n",
+                      rc, size);
+}
+
 static irqreturn_t vu_req_interrupt(int irq, void *data)
 {
        struct virtio_uml_device *vu_dev = data;
+       int response = 1;
        struct {
                struct vhost_user_msg msg;
                u8 extra_payload[512];
@@ -266,6 +324,7 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
        switch (msg.msg.header.request) {
        case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG:
                virtio_config_changed(&vu_dev->vdev);
+               response = 0;
                break;
        case VHOST_USER_SLAVE_IOTLB_MSG:
                /* not supported - VIRTIO_F_IOMMU_PLATFORM */
@@ -276,6 +335,9 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
                       msg.msg.header.request);
        }
 
+       if (msg.msg.header.flags & VHOST_USER_FLAG_NEED_REPLY)
+               vhost_user_reply(vu_dev, &msg.msg, response);
+
        return IRQ_HANDLED;
 }
 
@@ -365,7 +427,7 @@ static void vhost_user_get_config(struct virtio_uml_device *vu_dev,
        msg->payload.config.offset = 0;
        msg->payload.config.size = cfg_size;
 
-       rc = vhost_user_send(vu_dev, msg, NULL, 0);
+       rc = vhost_user_send(vu_dev, true, msg, NULL, 0);
        if (rc) {
                vu_err(vu_dev, "sending VHOST_USER_GET_CONFIG failed: %d\n",
                       rc);
@@ -416,7 +478,7 @@ static void vhost_user_set_config(struct virtio_uml_device *vu_dev,
        msg->payload.config.size = len;
        memcpy(msg->payload.config.payload, buf, len);
 
-       rc = vhost_user_send(vu_dev, msg, NULL, 0);
+       rc = vhost_user_send(vu_dev, false, msg, NULL, 0);
        if (rc)
                vu_err(vu_dev, "sending VHOST_USER_SET_CONFIG failed: %d\n",
                       rc);
@@ -506,7 +568,8 @@ static int vhost_user_set_mem_table(struct virtio_uml_device *vu_dev)
                        return rc;
        }
 
-       return vhost_user_send(vu_dev, &msg, fds, msg.payload.mem_regions.num);
+       return vhost_user_send(vu_dev, false, &msg, fds,
+                              msg.payload.mem_regions.num);
 }
 
 static int vhost_user_set_vring_state(struct virtio_uml_device *vu_dev,
@@ -519,7 +582,7 @@ static int vhost_user_set_vring_state(struct virtio_uml_device *vu_dev,
                .payload.vring_state.num = num,
        };
 
-       return vhost_user_send(vu_dev, &msg, NULL, 0);
+       return vhost_user_send(vu_dev, false, &msg, NULL, 0);
 }
 
 static int vhost_user_set_vring_num(struct virtio_uml_device *vu_dev,
@@ -550,7 +613,7 @@ static int vhost_user_set_vring_addr(struct virtio_uml_device *vu_dev,
                .payload.vring_addr.log = log,
        };
 
-       return vhost_user_send(vu_dev, &msg, NULL, 0);
+       return vhost_user_send(vu_dev, false, &msg, NULL, 0);
 }
 
 static int vhost_user_set_vring_fd(struct virtio_uml_device *vu_dev,
@@ -566,9 +629,9 @@ static int vhost_user_set_vring_fd(struct virtio_uml_device *vu_dev,
                return -EINVAL;
        if (fd < 0) {
                msg.payload.integer |= VHOST_USER_VRING_POLL_MASK;
-               return vhost_user_send(vu_dev, &msg, NULL, 0);
+               return vhost_user_send(vu_dev, false, &msg, NULL, 0);
        }
-       return vhost_user_send(vu_dev, &msg, &fd, 1);
+       return vhost_user_send(vu_dev, false, &msg, &fd, 1);
 }
 
 static int vhost_user_set_vring_call(struct virtio_uml_device *vu_dev,