aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/virtio_uml.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/drivers/virtio_uml.c')
-rw-r--r--arch/um/drivers/virtio_uml.c76
1 files changed, 51 insertions, 25 deletions
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index fc8c52cff5aa..023ced2250ea 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -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)
@@ -42,6 +42,13 @@
#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) {