return alloc_vring_queue(dev, vring_idx);
 }
 
+static void
+vhost_user_lock_all_queue_pairs(struct virtio_net *dev)
+{
+       unsigned int i = 0;
+       unsigned int vq_num = 0;
+
+       while (vq_num < dev->nr_vring) {
+               struct vhost_virtqueue *vq = dev->virtqueue[i];
+
+               if (vq) {
+                       rte_spinlock_lock(&vq->access_lock);
+                       vq_num++;
+               }
+               i++;
+       }
+}
+
+static void
+vhost_user_unlock_all_queue_pairs(struct virtio_net *dev)
+{
+       unsigned int i = 0;
+       unsigned int vq_num = 0;
+
+       while (vq_num < dev->nr_vring) {
+               struct vhost_virtqueue *vq = dev->virtqueue[i];
+
+               if (vq) {
+                       rte_spinlock_unlock(&vq->access_lock);
+                       vq_num++;
+               }
+               i++;
+       }
+}
+
 int
 vhost_user_msg_handler(int vid, int fd)
 {
        struct virtio_net *dev;
        struct VhostUserMsg msg;
        int ret;
+       int unlock_required = 0;
 
        dev = get_device(vid);
        if (dev == NULL)
                return -1;
        }
 
+       /*
+        * Note: we don't lock all queues on VHOST_USER_GET_VRING_BASE,
+        * since it is sent when virtio stops and device is destroyed.
+        * destroy_device waits for queues to be inactive, so it is safe.
+        * Otherwise taking the access_lock would cause a dead lock.
+        */
+       switch (msg.request.master) {
+       case VHOST_USER_SET_FEATURES:
+       case VHOST_USER_SET_PROTOCOL_FEATURES:
+       case VHOST_USER_SET_OWNER:
+       case VHOST_USER_RESET_OWNER:
+       case VHOST_USER_SET_MEM_TABLE:
+       case VHOST_USER_SET_LOG_BASE:
+       case VHOST_USER_SET_LOG_FD:
+       case VHOST_USER_SET_VRING_NUM:
+       case VHOST_USER_SET_VRING_ADDR:
+       case VHOST_USER_SET_VRING_BASE:
+       case VHOST_USER_SET_VRING_KICK:
+       case VHOST_USER_SET_VRING_CALL:
+       case VHOST_USER_SET_VRING_ERR:
+       case VHOST_USER_SET_VRING_ENABLE:
+       case VHOST_USER_SEND_RARP:
+       case VHOST_USER_NET_SET_MTU:
+       case VHOST_USER_SET_SLAVE_REQ_FD:
+               vhost_user_lock_all_queue_pairs(dev);
+               unlock_required = 1;
+               break;
+       default:
+               break;
+
+       }
+
        switch (msg.request.master) {
        case VHOST_USER_GET_FEATURES:
                msg.payload.u64 = vhost_user_get_features(dev);
 
        }
 
+       if (unlock_required)
+               vhost_user_unlock_all_queue_pairs(dev);
+
        if (msg.flags & VHOST_USER_NEED_REPLY) {
                msg.payload.u64 = !!ret;
                msg.size = sizeof(msg.payload.u64);
 
 #include <rte_udp.h>
 #include <rte_sctp.h>
 #include <rte_arp.h>
+#include <rte_spinlock.h>
 
 #include "iotlb.h"
 #include "vhost.h"
        }
 
        vq = dev->virtqueue[queue_id];
+
+       rte_spinlock_lock(&vq->access_lock);
+
        if (unlikely(vq->enabled == 0))
-               return 0;
+               goto out_access_unlock;
 
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
                vhost_user_iotlb_rd_lock(vq);
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
                vhost_user_iotlb_rd_unlock(vq);
 
+out_access_unlock:
+       rte_spinlock_unlock(&vq->access_lock);
+
        return count;
 }
 
        }
 
        vq = dev->virtqueue[queue_id];
+
+       rte_spinlock_lock(&vq->access_lock);
+
        if (unlikely(vq->enabled == 0))
-               return 0;
+               goto out_access_unlock;
 
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
                vhost_user_iotlb_rd_lock(vq);
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
                vhost_user_iotlb_rd_unlock(vq);
 
+out_access_unlock:
+       rte_spinlock_unlock(&vq->access_lock);
+
        return pkt_idx;
 }
 
        }
 
        vq = dev->virtqueue[queue_id];
-       if (unlikely(vq->enabled == 0))
+
+       if (unlikely(rte_spinlock_trylock(&vq->access_lock) == 0))
                return 0;
 
+       if (unlikely(vq->enabled == 0))
+               goto out_access_unlock;
+
        vq->batch_copy_nb_elems = 0;
 
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
        if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
                vhost_user_iotlb_rd_unlock(vq);
 
+out_access_unlock:
+       rte_spinlock_unlock(&vq->access_lock);
+
        if (unlikely(rarp_mbuf != NULL)) {
                /*
                 * Inject it to the head of "pkts" array, so that switch's mac