diff mbox

[dpdk-dev,v5] net/virtio-user: add support for server mode

Message ID 20180404171753.43422-1-zhiyong.yang@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Maxime Coquelin
Headers show

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation fail apply issues

Commit Message

Yang, Zhiyong April 4, 2018, 5:17 p.m. UTC
In a container environment if the vhost-user backend restarts, there's
no way for it to reconnect to virtio-user. To address this, support for
server mode is added. In this mode the socket file is created by virtio-
user, which the backend then connects to. This means that if the backend
restarts, it can reconnect to virtio-user and continue communications.

With current implementation, LSC is enabled at virtio-user side to
support to accept the coming connection.

Release note is updated in this patch.

Signed-off-by: Zhiyong Yang <zhiyong.yang@intel.com>
---

Changes in V5:
1. Support server mode virtio-user startup in non-blocking mode.
2. rebase on top of dpdk-next-virtio.

Changes in V4:
1. Don't create new pthread any more and use librte_eal interrupt thread.
2. virtio-user doesn't work in blocking mode any more for the first connection.
Client mode vhost-user startups firstly, then server mode virtio-user creates
socket file and startups. Keep consistency with usage of client mode
virtio-user. 

Changes in V3:
1. use EAL epoll mechanism instead of vhost events. Cancel to export vhost
event APIs.
2. rebase the code on top of dpdk-next-virtio

Changes in V2:
1. split two patches 1/5 and 2/5 from v1 patchset to fix some existing issues
which is not strongly related to support for server mode
2. move fdset related functions to librte_eal from librte_vhost exposed as
new APIs.
3. release note is added in the patch 5/5.
4. squash data structure change patch into 4/5 according to Maxime's suggestion.

 doc/guides/rel_notes/release_18_05.rst           |  6 ++
 drivers/net/virtio/virtio_user/vhost_user.c      | 47 ++++++++++--
 drivers/net/virtio/virtio_user/virtio_user_dev.c | 67 ++++++++++------
 drivers/net/virtio/virtio_user/virtio_user_dev.h |  3 +
 drivers/net/virtio/virtio_user_ethdev.c          | 98 +++++++++++++++++++++---
 5 files changed, 180 insertions(+), 41 deletions(-)

Comments

Tiwei Bie April 5, 2018, 8:29 a.m. UTC | #1
On Thu, Apr 05, 2018 at 01:17:53AM +0800, zhiyong.yang@intel.com wrote:
[...]
> +static int
> +virtio_user_start_server(struct virtio_user_dev *dev, struct sockaddr_un *un)
> +{
> +	int ret;
> +	int flag;
> +	int fd = dev->listenfd;
> +
> +	ret = bind(fd, (struct sockaddr *)un, sizeof(*un));
> +	if (ret < 0) {
> +		PMD_DRV_LOG(ERR, "failed to bind to %s: %s; remove it and try again\n",
> +			    dev->path, strerror(errno));
> +		goto err;
> +	}
> +	ret = listen(fd, MAX_VIRTIO_USER_BACKLOG);
> +	if (ret < 0)
> +		goto err;
> +
> +	flag = fcntl(fd, F_GETFL);
> +	fcntl(fd, F_SETFL, flag | O_NONBLOCK);
> +	dev->vhostfd = -1;
> +
> +	return 0;
> +err:
> +	close(dev->listenfd);

The dev->listenfd isn't created in this function, maybe it's
better to avoid closing this file in this function.

> +	return -1;
> +}
> +
>  /**
>   * Set up environment to talk with a vhost user backend.
>   *
> @@ -390,6 +418,7 @@ vhost_user_setup(struct virtio_user_dev *dev)
>  {
>  	int fd;
>  	int flag;
> +	int ret = 0;
>  	struct sockaddr_un un;
>  
>  	fd = socket(AF_UNIX, SOCK_STREAM, 0);
> @@ -405,14 +434,20 @@ vhost_user_setup(struct virtio_user_dev *dev)
>  	memset(&un, 0, sizeof(un));
>  	un.sun_family = AF_UNIX;
>  	snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
> -	if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
> -		PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
> -		close(fd);
> -		return -1;
> +
> +	if (dev->is_server) {
> +		dev->listenfd = fd;
> +		ret = virtio_user_start_server(dev, &un);
> +	} else {

Maybe it's better to keep the style consistent. How
about something like this:

	if (dev->is_server) {
		if (virtio_user_start_server(fd, &un) < 0) {
			PMD_DRV_LOG(ERR, some messages...);
			close(fd);
			return -1;
		}
		dev->listenfd = fd;
		dev->vhostfd = -1;
	} else {

> +		dev->vhostfd = fd;
> +		if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
> +			PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
> +			close(fd);
> +			return -1;
> +		}
>  	}
>  
> -	dev->vhostfd = fd;
> -	return 0;
> +	return ret;
>  }
>  
>  static int
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> index f90fee9e5..45e324679 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> @@ -254,7 +254,8 @@ virtio_user_fill_intr_handle(struct virtio_user_dev *dev)
>  	eth_dev->intr_handle->fd = -1;
>  	if (dev->vhostfd >= 0)
>  		eth_dev->intr_handle->fd = dev->vhostfd;
> -

Maybe it's better to keep this empty line (keep it before the return 0).

> +	else if (dev->is_server)
> +		eth_dev->intr_handle->fd = dev->listenfd;
>  	return 0;
>  }
>  
> @@ -267,24 +268,29 @@ virtio_user_dev_setup(struct virtio_user_dev *dev)
>  	dev->vhostfds = NULL;
>  	dev->tapfds = NULL;
>  
> -	if (is_vhost_user_by_type(dev->path)) {
> -		dev->ops = &ops_user;
> +	if (dev->is_server) {
> +		dev->ops = &ops_user;/* server mode only supports vhost user */
>  	} else {
> -		dev->ops = &ops_kernel;
> -
> -		dev->vhostfds = malloc(dev->max_queue_pairs * sizeof(int));
> -		dev->tapfds = malloc(dev->max_queue_pairs * sizeof(int));
> -		if (!dev->vhostfds || !dev->tapfds) {
> -			PMD_INIT_LOG(ERR, "Failed to malloc");
> -			return -1;
> -		}
> -
> -		for (q = 0; q < dev->max_queue_pairs; ++q) {
> -			dev->vhostfds[q] = -1;
> -			dev->tapfds[q] = -1;
> +		if (is_vhost_user_by_type(dev->path)) {
> +			dev->ops = &ops_user;
> +		} else {
> +			dev->ops = &ops_kernel;
> +
> +			dev->vhostfds = malloc(dev->max_queue_pairs *
> +					       sizeof(int));
> +			dev->tapfds = malloc(dev->max_queue_pairs *
> +					     sizeof(int));
> +			if (!dev->vhostfds || !dev->tapfds) {
> +				PMD_INIT_LOG(ERR, "Failed to malloc");
> +				return -1;
> +			}
> +
> +			for (q = 0; q < dev->max_queue_pairs; ++q) {
> +				dev->vhostfds[q] = -1;
> +				dev->tapfds[q] = -1;
> +			}
>  		}
>  	}
> -

There is no need to remove this empty line.

>  	if (dev->ops->setup(dev) < 0)
>  		return -1;
>  
> @@ -337,16 +343,21 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
>  		return -1;
>  	}
>  
> -	if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
> -		PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
> -		return -1;
> -	}
> +	if (dev->vhostfd >= 0) {
> +		if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
> +			PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
> +			return -1;
> +		}
>  
> -	if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
> -			    &dev->device_features) < 0) {
> -		PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
> -		return -1;
> +		if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
> +				&dev->device_features) < 0) {
> +			PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
> +			return -1;
> +		}
> +	} else {
> +		dev->device_features = VIRTIO_USER_SUPPORTED_FEATURES;

If the backend doesn't support e.g. VIRTIO_RING_F_INDIRECT_DESC.
Will it cause any problem?

>  	}
> +
>  	if (dev->mac_specified)
>  		dev->device_features |= (1ull << VIRTIO_NET_F_MAC);
>  
> @@ -388,6 +399,11 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
>  
>  	close(dev->vhostfd);
>  
> +	if (dev->is_server && dev->listenfd >= 0) {
> +		close(dev->listenfd);
> +		dev->listenfd = -1;
> +	}
> +
>  	if (dev->vhostfds) {
>  		for (i = 0; i < dev->max_queue_pairs; ++i)
>  			close(dev->vhostfds[i]);
> @@ -396,6 +412,9 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
>  	}
>  
>  	free(dev->ifname);
> +
> +	if (dev->is_server)
> +		unlink(dev->path);
>  }
[...]
>  
>  static int
>  get_string_arg(const char *key __rte_unused,
> @@ -378,10 +438,12 @@ virtio_user_pmd_probe(struct rte_vdev_device *dev)
>  	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
>  	uint64_t cq = VIRTIO_USER_DEF_CQ_EN;
>  	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
> +	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
>  	char *path = NULL;
>  	char *ifname = NULL;
>  	char *mac_addr = NULL;
>  	int ret = -1;
> +	struct virtio_user_dev *vu_dev = NULL;

Maybe it's better to move the definition of vu_dev after
eth_dev. And there isn't no need to initialize it.

Thanks

>  
>  	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_args);
>  	if (!kvlist) {
> @@ -445,6 +507,15 @@ virtio_user_pmd_probe(struct rte_vdev_device *dev)
>  		}
>  	}
>  
> +	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_SERVER_MODE) == 1) {
> +		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_SERVER_MODE,
> +				       &get_integer_arg, &server_mode) < 0) {
> +			PMD_INIT_LOG(ERR, "error to parse %s",
> +				     VIRTIO_USER_ARG_SERVER_MODE);
> +			goto end;
> +		}
> +	}
> +
>  	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_CQ_NUM) == 1) {
>  		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_CQ_NUM,
>  				       &get_integer_arg, &cq) < 0) {
> @@ -476,6 +547,11 @@ virtio_user_pmd_probe(struct rte_vdev_device *dev)
>  		}
>  
>  		hw = eth_dev->data->dev_private;
> +		vu_dev = virtio_user_get_dev(hw);
> +		if (server_mode == 1)
> +			vu_dev->is_server = true;
> +		else
> +			vu_dev->is_server = false;
>  		if (virtio_user_dev_init(hw->virtio_user_dev, path, queues, cq,
>  				 queue_size, mac_addr, &ifname) < 0) {
>  			PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");
> -- 
> 2.14.3
>
Yang, Zhiyong April 5, 2018, 9:19 a.m. UTC | #2
Tiwei,

Thanks  a lot for your review and comments.

Reply inline.

> -----Original Message-----

> From: Bie, Tiwei

> Sent: Thursday, April 5, 2018 4:29 PM

> To: Yang, Zhiyong <zhiyong.yang@intel.com>

> Cc: dev@dpdk.org; maxime.coquelin@redhat.com; thomas@monjalon.net;

> Tan, Jianfeng <jianfeng.tan@intel.com>; Wang, Zhihong

> <zhihong.wang@intel.com>; Wang, Dong1 <dong1.wang@intel.com>

> Subject: Re: [PATCH v5] net/virtio-user: add support for server mode

> 

> On Thu, Apr 05, 2018 at 01:17:53AM +0800, zhiyong.yang@intel.com wrote:

> [...]

> > +static int

> > +virtio_user_start_server(struct virtio_user_dev *dev, struct

> > +sockaddr_un *un) {

> > +	int ret;

> > +	int flag;

> > +	int fd = dev->listenfd;

> > +

> > +	ret = bind(fd, (struct sockaddr *)un, sizeof(*un));

> > +	if (ret < 0) {

> > +		PMD_DRV_LOG(ERR, "failed to bind to %s: %s; remove it and

> try again\n",

> > +			    dev->path, strerror(errno));

> > +		goto err;

> > +	}

> > +	ret = listen(fd, MAX_VIRTIO_USER_BACKLOG);

> > +	if (ret < 0)

> > +		goto err;

> > +

> > +	flag = fcntl(fd, F_GETFL);

> > +	fcntl(fd, F_SETFL, flag | O_NONBLOCK);

> > +	dev->vhostfd = -1;

> > +

> > +	return 0;

> > +err:

> > +	close(dev->listenfd);

> 

> The dev->listenfd isn't created in this function, maybe it's better to avoid

> closing this file in this function.

> 


Ok.

> > +	return -1;

> > +}

> > +

> >  /**

> >   * Set up environment to talk with a vhost user backend.

> >   *

> > @@ -390,6 +418,7 @@ vhost_user_setup(struct virtio_user_dev *dev)  {

> >  	int fd;

> >  	int flag;

> > +	int ret = 0;

> >  	struct sockaddr_un un;

> >

> >  	fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -405,14 +434,20 @@

> > vhost_user_setup(struct virtio_user_dev *dev)

> >  	memset(&un, 0, sizeof(un));

> >  	un.sun_family = AF_UNIX;

> >  	snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);

> > -	if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {

> > -		PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));

> > -		close(fd);

> > -		return -1;

> > +

> > +	if (dev->is_server) {

> > +		dev->listenfd = fd;

> > +		ret = virtio_user_start_server(dev, &un);

> > +	} else {

> 

> Maybe it's better to keep the style consistent. How about something like this:

> 

> 	if (dev->is_server) {

> 		if (virtio_user_start_server(fd, &un) < 0) {

> 			PMD_DRV_LOG(ERR, some messages...);

> 			close(fd);

> 			return -1;

> 		}

> 		dev->listenfd = fd;

> 		dev->vhostfd = -1;

> 	} else {

> 


Ok. it looks better.

So, the following code changes also.

> > +		if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {

> > +			PMD_DRV_LOG(ERR, "connect error, %s",

> strerror(errno));

> > +			close(fd);

> > +			return -1;

> > +		}

> > +		dev->vhostfd = fd;


Keep consistency.

> >  	}

> >

> > -	dev->vhostfd = fd;

> > -	return 0;

> > +	return ret;

> >  }

> >

> >  static int

> > diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c

> > b/drivers/net/virtio/virtio_user/virtio_user_dev.c

> > index f90fee9e5..45e324679 100644

> > --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c

> > +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c

> > @@ -254,7 +254,8 @@ virtio_user_fill_intr_handle(struct virtio_user_dev

> *dev)

> >  	eth_dev->intr_handle->fd = -1;

> >  	if (dev->vhostfd >= 0)

> >  		eth_dev->intr_handle->fd = dev->vhostfd;

> > -

> 

> Maybe it's better to keep this empty line (keep it before the return 0).

>


Ok.
 
> > +	else if (dev->is_server)

> > +		eth_dev->intr_handle->fd = dev->listenfd;

> >  	return 0;

> >  }

> >

> > @@ -267,24 +268,29 @@ virtio_user_dev_setup(struct virtio_user_dev

> *dev)

> >  	dev->vhostfds = NULL;

> >  	dev->tapfds = NULL;

> >

> > -	if (is_vhost_user_by_type(dev->path)) {

> > -		dev->ops = &ops_user;

> > +	if (dev->is_server) {

> > +		dev->ops = &ops_user;/* server mode only supports vhost

> user */

> >  	} else {

> > -		dev->ops = &ops_kernel;

> > -

> > -		dev->vhostfds = malloc(dev->max_queue_pairs *

> sizeof(int));

> > -		dev->tapfds = malloc(dev->max_queue_pairs * sizeof(int));

> > -		if (!dev->vhostfds || !dev->tapfds) {

> > -			PMD_INIT_LOG(ERR, "Failed to malloc");

> > -			return -1;

> > -		}

> > -

> > -		for (q = 0; q < dev->max_queue_pairs; ++q) {

> > -			dev->vhostfds[q] = -1;

> > -			dev->tapfds[q] = -1;

> > +		if (is_vhost_user_by_type(dev->path)) {

> > +			dev->ops = &ops_user;

> > +		} else {

> > +			dev->ops = &ops_kernel;

> > +

> > +			dev->vhostfds = malloc(dev->max_queue_pairs *

> > +					       sizeof(int));

> > +			dev->tapfds = malloc(dev->max_queue_pairs *

> > +					     sizeof(int));

> > +			if (!dev->vhostfds || !dev->tapfds) {

> > +				PMD_INIT_LOG(ERR, "Failed to malloc");

> > +				return -1;

> > +			}

> > +

> > +			for (q = 0; q < dev->max_queue_pairs; ++q) {

> > +				dev->vhostfds[q] = -1;

> > +				dev->tapfds[q] = -1;

> > +			}

> >  		}

> >  	}

> > -

> 

> There is no need to remove this empty line.

> 

Ok
> >  	if (dev->ops->setup(dev) < 0)

> >  		return -1;

> >

> > @@ -337,16 +343,21 @@ virtio_user_dev_init(struct virtio_user_dev *dev,

> char *path, int queues,

> >  		return -1;

> >  	}

> >

> > -	if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL)

> < 0) {

> > -		PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));

> > -		return -1;

> > -	}

> > +	if (dev->vhostfd >= 0) {

> > +		if (dev->ops->send_request(dev,

> VHOST_USER_SET_OWNER, NULL) < 0) {

> > +			PMD_INIT_LOG(ERR, "set_owner fails: %s",

> strerror(errno));

> > +			return -1;

> > +		}

> >

> > -	if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,

> > -			    &dev->device_features) < 0) {

> > -		PMD_INIT_LOG(ERR, "get_features failed: %s",

> strerror(errno));

> > -		return -1;

> > +		if (dev->ops->send_request(dev,

> VHOST_USER_GET_FEATURES,

> > +				&dev->device_features) < 0) {

> > +			PMD_INIT_LOG(ERR, "get_features failed: %s",

> strerror(errno));

> > +			return -1;

> > +		}

> > +	} else {

> > +		dev->device_features =

> VIRTIO_USER_SUPPORTED_FEATURES;

> 

> If the backend doesn't support e.g. VIRTIO_RING_F_INDIRECT_DESC.

> Will it cause any problem?

> 

Let me try it  and see what will be happening.

> >  	}

> > +

> >  	if (dev->mac_specified)

> >  		dev->device_features |= (1ull << VIRTIO_NET_F_MAC);

> >

> > @@ -388,6 +399,11 @@ virtio_user_dev_uninit(struct virtio_user_dev

> > *dev)

> >

> >  	close(dev->vhostfd);

> >

> > +	if (dev->is_server && dev->listenfd >= 0) {

> > +		close(dev->listenfd);

> > +		dev->listenfd = -1;

> > +	}

> > +

> >  	if (dev->vhostfds) {

> >  		for (i = 0; i < dev->max_queue_pairs; ++i)

> >  			close(dev->vhostfds[i]);

> > @@ -396,6 +412,9 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)

> >  	}

> >

> >  	free(dev->ifname);

> > +

> > +	if (dev->is_server)

> > +		unlink(dev->path);

> >  }

> [...]

> >

> >  static int

> >  get_string_arg(const char *key __rte_unused, @@ -378,10 +438,12 @@

> > virtio_user_pmd_probe(struct rte_vdev_device *dev)

> >  	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;

> >  	uint64_t cq = VIRTIO_USER_DEF_CQ_EN;

> >  	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;

> > +	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;

> >  	char *path = NULL;

> >  	char *ifname = NULL;

> >  	char *mac_addr = NULL;

> >  	int ret = -1;

> > +	struct virtio_user_dev *vu_dev = NULL;

> 

> Maybe it's better to move the definition of vu_dev after eth_dev. And there

> isn't no need to initialize it.

> 


Ok.

thanks
Zhiyong
Yang, Zhiyong April 5, 2018, 9:21 a.m. UTC | #3
Ping Maxime, Jianfeng

Do you have any comments about the patch?

Thanks
Zhiyong

> -----Original Message-----
> From: Yang, Zhiyong
> Sent: Thursday, April 5, 2018 1:18 AM
> To: dev@dpdk.org
> Cc: maxime.coquelin@redhat.com; thomas@monjalon.net; Tan, Jianfeng
> <jianfeng.tan@intel.com>; Wang, Zhihong <zhihong.wang@intel.com>; Bie,
> Tiwei <tiwei.bie@intel.com>; Wang, Dong1 <dong1.wang@intel.com>; Yang,
> Zhiyong <zhiyong.yang@intel.com>
> Subject: [PATCH v5] net/virtio-user: add support for server mode
> 
> In a container environment if the vhost-user backend restarts, there's no
> way for it to reconnect to virtio-user. To address this, support for server
> mode is added. In this mode the socket file is created by virtio- user, which
> the backend then connects to. This means that if the backend restarts, it can
> reconnect to virtio-user and continue communications.
> 
> With current implementation, LSC is enabled at virtio-user side to support to
> accept the coming connection.
> 
> Release note is updated in this patch.
> 
> Signed-off-by: Zhiyong Yang <zhiyong.yang@intel.com>
> ---
Yang, Zhiyong April 6, 2018, 7:22 a.m. UTC | #4
> -----Original Message-----

> From: Bie, Tiwei

> Sent: Thursday, April 5, 2018 4:29 PM

> To: Yang, Zhiyong <zhiyong.yang@intel.com>

> Cc: dev@dpdk.org; maxime.coquelin@redhat.com; thomas@monjalon.net;

> Tan, Jianfeng <jianfeng.tan@intel.com>; Wang, Zhihong

> <zhihong.wang@intel.com>; Wang, Dong1 <dong1.wang@intel.com>

> Subject: Re: [PATCH v5] net/virtio-user: add support for server mode

> 

> On Thu, Apr 05, 2018 at 01:17:53AM +0800, zhiyong.yang@intel.com wrote:


<snip>

> > @@ -337,16 +343,21 @@ virtio_user_dev_init(struct virtio_user_dev *dev,

> char *path, int queues,

> >  		return -1;

> >  	}

> >

> > -	if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL)

> < 0) {

> > -		PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));

> > -		return -1;

> > -	}

> > +	if (dev->vhostfd >= 0) {

> > +		if (dev->ops->send_request(dev,

> VHOST_USER_SET_OWNER, NULL) < 0) {

> > +			PMD_INIT_LOG(ERR, "set_owner fails: %s",

> strerror(errno));

> > +			return -1;

> > +		}

> >

> > -	if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,

> > -			    &dev->device_features) < 0) {

> > -		PMD_INIT_LOG(ERR, "get_features failed: %s",

> strerror(errno));

> > -		return -1;

> > +		if (dev->ops->send_request(dev,

> VHOST_USER_GET_FEATURES,

> > +				&dev->device_features) < 0) {

> > +			PMD_INIT_LOG(ERR, "get_features failed: %s",

> strerror(errno));

> > +			return -1;

> > +		}

> > +	} else {

> > +		dev->device_features =

> VIRTIO_USER_SUPPORTED_FEATURES;

> 

> If the backend doesn't support e.g. VIRTIO_RING_F_INDIRECT_DESC.

> Will it cause any problem?

> 

vhost-user will compare virtio-user and vhost-user features, as you said that
if VIRTIO_RING_F_INDIRECT_DESC was not supported ,
vhost-user come across failure. vhost-user closes the connecting socket and later virtio-user will detect the
broken connection by LSC. 

thanks
zhiyong
diff mbox

Patch

diff --git a/doc/guides/rel_notes/release_18_05.rst b/doc/guides/rel_notes/release_18_05.rst
index 9cc77f893..f8897b2e9 100644
--- a/doc/guides/rel_notes/release_18_05.rst
+++ b/doc/guides/rel_notes/release_18_05.rst
@@ -58,6 +58,12 @@  New Features
   * Added support for NVGRE, VXLAN and GENEVE filters in flow API.
   * Added support for DROP action in flow API.
 
+* **Added support for virtio-user server mode.**
+  In a container environment if the vhost-user backend restarts, there's no way
+  for it to reconnect to virtio-user. To address this, support for server mode
+  is added. In this mode the socket file is created by virtio-user, which the
+  backend connects to. This means that if the backend restarts, it can reconnect
+  to virtio-user and continue communications.
 
 API Changes
 -----------
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 91c6449bb..f08dfeda9 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -378,6 +378,34 @@  vhost_user_sock(struct virtio_user_dev *dev,
 	return 0;
 }
 
+#define MAX_VIRTIO_USER_BACKLOG 1
+static int
+virtio_user_start_server(struct virtio_user_dev *dev, struct sockaddr_un *un)
+{
+	int ret;
+	int flag;
+	int fd = dev->listenfd;
+
+	ret = bind(fd, (struct sockaddr *)un, sizeof(*un));
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "failed to bind to %s: %s; remove it and try again\n",
+			    dev->path, strerror(errno));
+		goto err;
+	}
+	ret = listen(fd, MAX_VIRTIO_USER_BACKLOG);
+	if (ret < 0)
+		goto err;
+
+	flag = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flag | O_NONBLOCK);
+	dev->vhostfd = -1;
+
+	return 0;
+err:
+	close(dev->listenfd);
+	return -1;
+}
+
 /**
  * Set up environment to talk with a vhost user backend.
  *
@@ -390,6 +418,7 @@  vhost_user_setup(struct virtio_user_dev *dev)
 {
 	int fd;
 	int flag;
+	int ret = 0;
 	struct sockaddr_un un;
 
 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -405,14 +434,20 @@  vhost_user_setup(struct virtio_user_dev *dev)
 	memset(&un, 0, sizeof(un));
 	un.sun_family = AF_UNIX;
 	snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
-	if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
-		PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
-		close(fd);
-		return -1;
+
+	if (dev->is_server) {
+		dev->listenfd = fd;
+		ret = virtio_user_start_server(dev, &un);
+	} else {
+		dev->vhostfd = fd;
+		if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
+			PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
+			close(fd);
+			return -1;
+		}
 	}
 
-	dev->vhostfd = fd;
-	return 0;
+	return ret;
 }
 
 static int
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index f90fee9e5..45e324679 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -254,7 +254,8 @@  virtio_user_fill_intr_handle(struct virtio_user_dev *dev)
 	eth_dev->intr_handle->fd = -1;
 	if (dev->vhostfd >= 0)
 		eth_dev->intr_handle->fd = dev->vhostfd;
-
+	else if (dev->is_server)
+		eth_dev->intr_handle->fd = dev->listenfd;
 	return 0;
 }
 
@@ -267,24 +268,29 @@  virtio_user_dev_setup(struct virtio_user_dev *dev)
 	dev->vhostfds = NULL;
 	dev->tapfds = NULL;
 
-	if (is_vhost_user_by_type(dev->path)) {
-		dev->ops = &ops_user;
+	if (dev->is_server) {
+		dev->ops = &ops_user;/* server mode only supports vhost user */
 	} else {
-		dev->ops = &ops_kernel;
-
-		dev->vhostfds = malloc(dev->max_queue_pairs * sizeof(int));
-		dev->tapfds = malloc(dev->max_queue_pairs * sizeof(int));
-		if (!dev->vhostfds || !dev->tapfds) {
-			PMD_INIT_LOG(ERR, "Failed to malloc");
-			return -1;
-		}
-
-		for (q = 0; q < dev->max_queue_pairs; ++q) {
-			dev->vhostfds[q] = -1;
-			dev->tapfds[q] = -1;
+		if (is_vhost_user_by_type(dev->path)) {
+			dev->ops = &ops_user;
+		} else {
+			dev->ops = &ops_kernel;
+
+			dev->vhostfds = malloc(dev->max_queue_pairs *
+					       sizeof(int));
+			dev->tapfds = malloc(dev->max_queue_pairs *
+					     sizeof(int));
+			if (!dev->vhostfds || !dev->tapfds) {
+				PMD_INIT_LOG(ERR, "Failed to malloc");
+				return -1;
+			}
+
+			for (q = 0; q < dev->max_queue_pairs; ++q) {
+				dev->vhostfds[q] = -1;
+				dev->tapfds[q] = -1;
+			}
 		}
 	}
-
 	if (dev->ops->setup(dev) < 0)
 		return -1;
 
@@ -337,16 +343,21 @@  virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
 		return -1;
 	}
 
-	if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
-		PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
-		return -1;
-	}
+	if (dev->vhostfd >= 0) {
+		if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
+			PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
+			return -1;
+		}
 
-	if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
-			    &dev->device_features) < 0) {
-		PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
-		return -1;
+		if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
+				&dev->device_features) < 0) {
+			PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
+			return -1;
+		}
+	} else {
+		dev->device_features = VIRTIO_USER_SUPPORTED_FEATURES;
 	}
+
 	if (dev->mac_specified)
 		dev->device_features |= (1ull << VIRTIO_NET_F_MAC);
 
@@ -388,6 +399,11 @@  virtio_user_dev_uninit(struct virtio_user_dev *dev)
 
 	close(dev->vhostfd);
 
+	if (dev->is_server && dev->listenfd >= 0) {
+		close(dev->listenfd);
+		dev->listenfd = -1;
+	}
+
 	if (dev->vhostfds) {
 		for (i = 0; i < dev->max_queue_pairs; ++i)
 			close(dev->vhostfds[i]);
@@ -396,6 +412,9 @@  virtio_user_dev_uninit(struct virtio_user_dev *dev)
 	}
 
 	free(dev->ifname);
+
+	if (dev->is_server)
+		unlink(dev->path);
 }
 
 static uint8_t
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 5f8755771..ade727e46 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -6,6 +6,7 @@ 
 #define _VIRTIO_USER_DEV_H
 
 #include <limits.h>
+#include <stdbool.h>
 #include "../virtio_pci.h"
 #include "../virtio_ring.h"
 #include "vhost.h"
@@ -13,6 +14,8 @@ 
 struct virtio_user_dev {
 	/* for vhost_user backend */
 	int		vhostfd;
+	int		listenfd;   /* listening fd */
+	bool		is_server;  /* server or client mode */
 
 	/* for vhost_kernel backend */
 	char		*ifname;
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 263649006..c34749953 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -24,15 +24,72 @@ 
 #define virtio_user_get_dev(hw) \
 	((struct virtio_user_dev *)(hw)->virtio_user_dev)
 
+static void
+virtio_user_server_reconnect(struct virtio_user_dev *dev)
+{
+	int ret;
+	int flag;
+	int connectfd;
+	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
+
+	connectfd = accept(dev->listenfd, NULL, NULL);
+	if (connectfd < 0)
+		return;
+
+	dev->vhostfd = connectfd;
+	flag = fcntl(connectfd, F_GETFD);
+	fcntl(connectfd, F_SETFL, flag | O_NONBLOCK);
+
+	ret = virtio_user_start_device(dev);
+	if (ret < 0)
+		return;
+
+	if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
+		if (rte_intr_disable(eth_dev->intr_handle) < 0) {
+			PMD_DRV_LOG(ERR, "interrupt disable failed");
+			return;
+		}
+		rte_intr_callback_unregister(eth_dev->intr_handle,
+					     virtio_interrupt_handler,
+					     eth_dev);
+		eth_dev->intr_handle->fd = connectfd;
+		rte_intr_callback_register(eth_dev->intr_handle,
+					   virtio_interrupt_handler, eth_dev);
+
+		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
+			PMD_DRV_LOG(ERR, "interrupt enable failed");
+			return;
+		}
+	}
+	PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
+}
+
 static void
 virtio_user_delayed_handler(void *param)
 {
 	struct virtio_hw *hw = (struct virtio_hw *)param;
-	struct rte_eth_dev *dev = &rte_eth_devices[hw->port_id];
+	struct rte_eth_dev *eth_dev = &rte_eth_devices[hw->port_id];
+	struct virtio_user_dev *dev = virtio_user_get_dev(hw);
 
-	rte_intr_callback_unregister(dev->intr_handle,
-				     virtio_interrupt_handler,
-				     dev);
+	if (rte_intr_disable(eth_dev->intr_handle) < 0) {
+		PMD_DRV_LOG(ERR, "interrupt disable failed");
+		return;
+	}
+	rte_intr_callback_unregister(eth_dev->intr_handle,
+				     virtio_interrupt_handler, eth_dev);
+	if (dev->is_server) {
+		if (dev->vhostfd >= 0) {
+			close(dev->vhostfd);
+			dev->vhostfd = -1;
+		}
+		eth_dev->intr_handle->fd = dev->listenfd;
+		rte_intr_callback_register(eth_dev->intr_handle,
+					   virtio_interrupt_handler, eth_dev);
+		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
+			PMD_DRV_LOG(ERR, "interrupt enable failed");
+			return;
+		}
+	}
 }
 
 static void
@@ -67,12 +124,10 @@  virtio_user_read_dev_config(struct virtio_hw *hw, size_t offset,
 				dev->status &= (~VIRTIO_NET_S_LINK_UP);
 				PMD_DRV_LOG(ERR, "virtio-user port %u is down",
 					    hw->port_id);
-				/* Only client mode is available now. Once the
-				 * connection is broken, it can never be up
-				 * again. Besides, this function could be called
-				 * in the process of interrupt handling,
-				 * callback cannot be unregistered here, set an
-				 * alarm to do it.
+
+				/* This function could be called in the process
+				 * of interrupt handling, callback cannot be
+				 * unregistered here, set an alarm to do it.
 				 */
 				rte_eal_alarm_set(1,
 						  virtio_user_delayed_handler,
@@ -85,7 +140,9 @@  virtio_user_read_dev_config(struct virtio_hw *hw, size_t offset,
 				PMD_DRV_LOG(ERR, "error clearing O_NONBLOCK flag");
 				return;
 			}
-		}
+		} else if (dev->is_server)
+			virtio_user_server_reconnect(dev);
+
 		*(uint16_t *)dst = dev->status;
 	}
 
@@ -278,12 +335,15 @@  static const char *valid_args[] = {
 	VIRTIO_USER_ARG_QUEUE_SIZE,
 #define VIRTIO_USER_ARG_INTERFACE_NAME "iface"
 	VIRTIO_USER_ARG_INTERFACE_NAME,
+#define VIRTIO_USER_ARG_SERVER_MODE "server"
+	VIRTIO_USER_ARG_SERVER_MODE,
 	NULL
 };
 
 #define VIRTIO_USER_DEF_CQ_EN	0
 #define VIRTIO_USER_DEF_Q_NUM	1
 #define VIRTIO_USER_DEF_Q_SZ	256
+#define VIRTIO_USER_DEF_SERVER_MODE	0
 
 static int
 get_string_arg(const char *key __rte_unused,
@@ -378,10 +438,12 @@  virtio_user_pmd_probe(struct rte_vdev_device *dev)
 	uint64_t queues = VIRTIO_USER_DEF_Q_NUM;
 	uint64_t cq = VIRTIO_USER_DEF_CQ_EN;
 	uint64_t queue_size = VIRTIO_USER_DEF_Q_SZ;
+	uint64_t server_mode = VIRTIO_USER_DEF_SERVER_MODE;
 	char *path = NULL;
 	char *ifname = NULL;
 	char *mac_addr = NULL;
 	int ret = -1;
+	struct virtio_user_dev *vu_dev = NULL;
 
 	kvlist = rte_kvargs_parse(rte_vdev_device_args(dev), valid_args);
 	if (!kvlist) {
@@ -445,6 +507,15 @@  virtio_user_pmd_probe(struct rte_vdev_device *dev)
 		}
 	}
 
+	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_SERVER_MODE) == 1) {
+		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_SERVER_MODE,
+				       &get_integer_arg, &server_mode) < 0) {
+			PMD_INIT_LOG(ERR, "error to parse %s",
+				     VIRTIO_USER_ARG_SERVER_MODE);
+			goto end;
+		}
+	}
+
 	if (rte_kvargs_count(kvlist, VIRTIO_USER_ARG_CQ_NUM) == 1) {
 		if (rte_kvargs_process(kvlist, VIRTIO_USER_ARG_CQ_NUM,
 				       &get_integer_arg, &cq) < 0) {
@@ -476,6 +547,11 @@  virtio_user_pmd_probe(struct rte_vdev_device *dev)
 		}
 
 		hw = eth_dev->data->dev_private;
+		vu_dev = virtio_user_get_dev(hw);
+		if (server_mode == 1)
+			vu_dev->is_server = true;
+		else
+			vu_dev->is_server = false;
 		if (virtio_user_dev_init(hw->virtio_user_dev, path, queues, cq,
 				 queue_size, mac_addr, &ifname) < 0) {
 			PMD_INIT_LOG(ERR, "virtio_user_dev_init fails");