[v4,4/4] vhost: fix async unregister deadlock

Message ID 20201013014546.2896162-5-patrick.fu@intel.com (mailing list archive)
State Accepted, archived
Delegated to: Maxime Coquelin
Headers
Series optimize async data path |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/iol-broadcom-Performance success Performance Testing PASS
ci/Intel-compilation success Compilation OK
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/travis-robot success Travis build: passed
ci/iol-mellanox-Performance success Performance Testing PASS

Commit Message

Patrick Fu Oct. 13, 2020, 1:45 a.m. UTC
  When async unregister function is invoked in certain vhost event
callbacks (e.g. vring state change), deadlock may occur due to
recursive spinlock acquire. This patch uses trylock() primitive in
the unregister API to avoid deadlock.

Fixes: 78639d54563a ("vhost: introduce async enqueue registration API")

Signed-off-by: Patrick Fu <patrick.fu@intel.com>
---
 lib/librte_vhost/vhost.c      | 9 +++++++--
 lib/librte_vhost/vhost_user.c | 4 ++--
 2 files changed, 9 insertions(+), 4 deletions(-)
  

Comments

Maxime Coquelin Oct. 14, 2020, 9:34 a.m. UTC | #1
On 10/13/20 3:45 AM, Patrick Fu wrote:
> When async unregister function is invoked in certain vhost event
> callbacks (e.g. vring state change), deadlock may occur due to
> recursive spinlock acquire. This patch uses trylock() primitive in
> the unregister API to avoid deadlock.
> 
> Fixes: 78639d54563a ("vhost: introduce async enqueue registration API")
> 
> Signed-off-by: Patrick Fu <patrick.fu@intel.com>
> ---
>  lib/librte_vhost/vhost.c      | 9 +++++++--
>  lib/librte_vhost/vhost_user.c | 4 ++--
>  2 files changed, 9 insertions(+), 4 deletions(-)
> 
> diff --git a/lib/librte_vhost/vhost.c b/lib/librte_vhost/vhost.c
> index 323565898..6068c38ec 100644
> --- a/lib/librte_vhost/vhost.c
> +++ b/lib/librte_vhost/vhost.c
> @@ -1633,10 +1633,15 @@ int rte_vhost_async_channel_unregister(int vid, uint16_t queue_id)
>  		return ret;
>  
>  	ret = 0;
> -	rte_spinlock_lock(&vq->access_lock);
>  
>  	if (!vq->async_registered)
> -		goto out;
> +		return ret;
> +
> +	if (!rte_spinlock_trylock(&vq->access_lock)) {
> +		VHOST_LOG_CONFIG(ERR, "Failed to unregister async channel. "
> +			"virt queue busy.\n");
> +		return -1;
> +	}
>  
>  	if (vq->async_pkts_inflight_n) {
>  		VHOST_LOG_CONFIG(ERR, "Failed to unregister async channel. "
> diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
> index 1656ec736..d20c8c57a 100644
> --- a/lib/librte_vhost/vhost_user.c
> +++ b/lib/librte_vhost/vhost_user.c
> @@ -1980,9 +1980,9 @@ vhost_user_set_vring_enable(struct virtio_net **pdev,
>  		"set queue enable: %d to qp idx: %d\n",
>  		enable, index);
>  
> -	if (!enable && dev->virtqueue[index]->async_registered) {
> +	if (enable && dev->virtqueue[index]->async_registered) {
>  		if (dev->virtqueue[index]->async_pkts_inflight_n) {
> -			VHOST_LOG_CONFIG(ERR, "failed to disable vring. "
> +			VHOST_LOG_CONFIG(ERR, "failed to enable vring. "
>  			"async inflight packets must be completed first\n");
>  			return RTE_VHOST_MSG_RESULT_ERR;
>  		}
> 

Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>

Thanks,
Maxime
  

Patch

diff --git a/lib/librte_vhost/vhost.c b/lib/librte_vhost/vhost.c
index 323565898..6068c38ec 100644
--- a/lib/librte_vhost/vhost.c
+++ b/lib/librte_vhost/vhost.c
@@ -1633,10 +1633,15 @@  int rte_vhost_async_channel_unregister(int vid, uint16_t queue_id)
 		return ret;
 
 	ret = 0;
-	rte_spinlock_lock(&vq->access_lock);
 
 	if (!vq->async_registered)
-		goto out;
+		return ret;
+
+	if (!rte_spinlock_trylock(&vq->access_lock)) {
+		VHOST_LOG_CONFIG(ERR, "Failed to unregister async channel. "
+			"virt queue busy.\n");
+		return -1;
+	}
 
 	if (vq->async_pkts_inflight_n) {
 		VHOST_LOG_CONFIG(ERR, "Failed to unregister async channel. "
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 1656ec736..d20c8c57a 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -1980,9 +1980,9 @@  vhost_user_set_vring_enable(struct virtio_net **pdev,
 		"set queue enable: %d to qp idx: %d\n",
 		enable, index);
 
-	if (!enable && dev->virtqueue[index]->async_registered) {
+	if (enable && dev->virtqueue[index]->async_registered) {
 		if (dev->virtqueue[index]->async_pkts_inflight_n) {
-			VHOST_LOG_CONFIG(ERR, "failed to disable vring. "
+			VHOST_LOG_CONFIG(ERR, "failed to enable vring. "
 			"async inflight packets must be completed first\n");
 			return RTE_VHOST_MSG_RESULT_ERR;
 		}