]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/um/drivers/virtio_uml.c
Merge tag 'for-linus-5.5b-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / arch / um / drivers / virtio_uml.c
index fc8c52cff5aa897bea8d319a96936120a4fd2dbe..023ced2250ea7ef57c274440c752aefb9b6bd67d 100644 (file)
@@ -4,12 +4,12 @@
  *
  * Copyright(c) 2019 Intel Corporation
  *
- * This module allows virtio devices to be used over a vhost-user socket.
+ * This driver allows virtio devices to be used over a vhost-user socket.
  *
  * Guest devices can be instantiated by kernel module or command line
  * parameters. One device will be created for each parameter. Syntax:
  *
- *             [virtio_uml.]device=<socket>:<virtio_id>[:<platform_id>]
+ *             virtio_uml.device=<socket>:<virtio_id>[:<platform_id>]
  * where:
  *             <socket>        := vhost-user socket path to connect
  *             <virtio_id>     := virtio device id (as in virtio_ids.h)
 #define to_virtio_uml_device(_vdev) \
        container_of(_vdev, struct virtio_uml_device, vdev)
 
+struct virtio_uml_platform_data {
+       u32 virtio_device_id;
+       const char *socket_path;
+       struct work_struct conn_broken_wk;
+       struct platform_device *pdev;
+};
+
 struct virtio_uml_device {
        struct virtio_device vdev;
        struct platform_device *pdev;
@@ -50,6 +57,7 @@ struct virtio_uml_device {
        u64 features;
        u64 protocol_features;
        u8 status;
+       u8 registered:1;
 };
 
 struct virtio_uml_vq_info {
@@ -83,7 +91,7 @@ static int full_sendmsg_fds(int fd, const void *buf, unsigned int len,
        return 0;
 }
 
-static int full_read(int fd, void *buf, int len)
+static int full_read(int fd, void *buf, int len, bool abortable)
 {
        int rc;
 
@@ -93,7 +101,7 @@ static int full_read(int fd, void *buf, int len)
                        buf += rc;
                        len -= rc;
                }
-       } while (len && (rc > 0 || rc == -EINTR));
+       } while (len && (rc > 0 || rc == -EINTR || (!abortable && rc == -EAGAIN)));
 
        if (rc < 0)
                return rc;
@@ -104,28 +112,37 @@ static int full_read(int fd, void *buf, int len)
 
 static int vhost_user_recv_header(int fd, struct vhost_user_msg *msg)
 {
-       return full_read(fd, msg, sizeof(msg->header));
+       return full_read(fd, msg, sizeof(msg->header), true);
 }
 
-static int vhost_user_recv(int fd, struct vhost_user_msg *msg,
+static int vhost_user_recv(struct virtio_uml_device *vu_dev,
+                          int fd, struct vhost_user_msg *msg,
                           size_t max_payload_size)
 {
        size_t size;
        int rc = vhost_user_recv_header(fd, msg);
 
+       if (rc == -ECONNRESET && vu_dev->registered) {
+               struct virtio_uml_platform_data *pdata;
+
+               pdata = vu_dev->pdev->dev.platform_data;
+
+               virtio_break_device(&vu_dev->vdev);
+               schedule_work(&pdata->conn_broken_wk);
+       }
        if (rc)
                return rc;
        size = msg->header.size;
        if (size > max_payload_size)
                return -EPROTO;
-       return full_read(fd, &msg->payload, size);
+       return full_read(fd, &msg->payload, size, false);
 }
 
 static int vhost_user_recv_resp(struct virtio_uml_device *vu_dev,
                                struct vhost_user_msg *msg,
                                size_t max_payload_size)
 {
-       int rc = vhost_user_recv(vu_dev->sock, msg, max_payload_size);
+       int rc = vhost_user_recv(vu_dev, vu_dev->sock, msg, max_payload_size);
 
        if (rc)
                return rc;
@@ -155,7 +172,7 @@ static int vhost_user_recv_req(struct virtio_uml_device *vu_dev,
                               struct vhost_user_msg *msg,
                               size_t max_payload_size)
 {
-       int rc = vhost_user_recv(vu_dev->req_fd, msg, max_payload_size);
+       int rc = vhost_user_recv(vu_dev, vu_dev->req_fd, msg, max_payload_size);
 
        if (rc)
                return rc;
@@ -963,11 +980,6 @@ static void virtio_uml_release_dev(struct device *d)
 
 /* Platform device */
 
-struct virtio_uml_platform_data {
-       u32 virtio_device_id;
-       const char *socket_path;
-};
-
 static int virtio_uml_probe(struct platform_device *pdev)
 {
        struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
@@ -1005,6 +1017,7 @@ static int virtio_uml_probe(struct platform_device *pdev)
        rc = register_virtio_device(&vu_dev->vdev);
        if (rc)
                put_device(&vu_dev->vdev.dev);
+       vu_dev->registered = 1;
        return rc;
 
 error_init:
@@ -1034,13 +1047,31 @@ static struct device vu_cmdline_parent = {
 static bool vu_cmdline_parent_registered;
 static int vu_cmdline_id;
 
+static int vu_unregister_cmdline_device(struct device *dev, void *data)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
+
+       kfree(pdata->socket_path);
+       platform_device_unregister(pdev);
+       return 0;
+}
+
+static void vu_conn_broken(struct work_struct *wk)
+{
+       struct virtio_uml_platform_data *pdata;
+
+       pdata = container_of(wk, struct virtio_uml_platform_data, conn_broken_wk);
+       vu_unregister_cmdline_device(&pdata->pdev->dev, NULL);
+}
+
 static int vu_cmdline_set(const char *device, const struct kernel_param *kp)
 {
        const char *ids = strchr(device, ':');
        unsigned int virtio_device_id;
        int processed, consumed, err;
        char *socket_path;
-       struct virtio_uml_platform_data pdata;
+       struct virtio_uml_platform_data pdata, *ppdata;
        struct platform_device *pdev;
 
        if (!ids || ids == device)
@@ -1079,6 +1110,11 @@ static int vu_cmdline_set(const char *device, const struct kernel_param *kp)
        err = PTR_ERR_OR_ZERO(pdev);
        if (err)
                goto free;
+
+       ppdata = pdev->dev.platform_data;
+       ppdata->pdev = pdev;
+       INIT_WORK(&ppdata->conn_broken_wk, vu_conn_broken);
+
        return 0;
 
 free:
@@ -1121,16 +1157,6 @@ __uml_help(vu_cmdline_param_ops,
 );
 
 
-static int vu_unregister_cmdline_device(struct device *dev, void *data)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
-
-       kfree(pdata->socket_path);
-       platform_device_unregister(pdev);
-       return 0;
-}
-
 static void vu_unregister_cmdline_devices(void)
 {
        if (vu_cmdline_parent_registered) {