diff options
Diffstat (limited to 'arch/um/drivers/virtio_uml.c')
| -rw-r--r-- | arch/um/drivers/virtio_uml.c | 76 | 
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) {  |