When the owner is killed, vhost_worker_killed() detaches the worker
from the virtqueues and runs only the work already queued. A request
that completes after that is added to blk_vq->llhead, but
vhost_vq_work_queue() returns false and queues nothing, so the request
is never forgotten. req_inflight[] stays elevated and vhost_blk_flush()
hangs uninterruptibly in release when the fd is closed. A request in
flight at the kill is enough.

Release such requests from a work item when the worker cannot be
queued, as vhost-scsi does in vhost_scsi_drop_cmds(). The bio
completion may run in interrupt context, where forget_request() and
the kvfree() of the request buffers are not allowed. The guest is not
answered, its memory is no longer reachable.

Once the completion publishes the request, the request can be
forgotten and vhost_blk_flush() can return while the completion is
still queueing work into blk, which release then frees. Queue under
rcu_read_lock() and wait for it with synchronize_rcu() in
vhost_blk_drop_backend(), then flush the drop work before freeing the
requests. Flushing there rather than in release also keeps a stale
drop work from releasing requests of the next owner after
VHOST_RESET_OWNER.

https://virtuozzo.atlassian.net/browse/VSTOR-141684

Fixes: 1629db092dd7 ("drivers/vhost: vhost-blk accelerator for virtio-blk 
guests")
Feature: vhost-blk: in-kernel accelerator for virtio-blk guests
Signed-off-by: Alexander Ivanov <[email protected]>
---
 drivers/vhost/blk.c | 36 +++++++++++++++++++++++++++++++++---
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/drivers/vhost/blk.c b/drivers/vhost/blk.c
index b95358cdf3bc..eea8cca59cac 100644
--- a/drivers/vhost/blk.c
+++ b/drivers/vhost/blk.c
@@ -91,6 +91,7 @@ struct vhost_blk_vq {
        struct iovec iov[UIO_MAXIOV];
        struct llist_head llhead;
        struct vhost_work work;
+       struct work_struct drop_work;
 };
 
 struct vhost_blk {
@@ -168,8 +169,13 @@ static void vhost_blk_req_done(struct bio *bio)
        }
 
        if (atomic_dec_and_test(&req->bio_nr)) {
-               llist_add(&req->llnode, &req->blk_vq->llhead);
-               vhost_vq_work_queue(&req->blk_vq->vq, &req->blk_vq->work);
+               struct vhost_blk_vq *blk_vq = req->blk_vq;
+
+               rcu_read_lock();
+               llist_add(&req->llnode, &blk_vq->llhead);
+               if (!vhost_vq_work_queue(&blk_vq->vq, &blk_vq->work))
+                       schedule_work(&blk_vq->drop_work);
+               rcu_read_unlock();
        }
 }
 
@@ -683,6 +689,24 @@ static void vhost_blk_handle_host_kick(struct vhost_work 
*work)
        mutex_unlock(&vq->mutex);
 }
 
+static void vhost_blk_drop_reqs(struct work_struct *work)
+{
+       struct vhost_blk_vq *blk_vq;
+       struct vhost_blk_req *req, *tmp;
+       struct llist_node *llnode;
+
+       blk_vq = container_of(work, struct vhost_blk_vq, drop_work);
+
+       mutex_lock(&blk_vq->vq.mutex);
+       llnode = llist_del_all(&blk_vq->llhead);
+       llist_for_each_entry_safe(req, tmp, llnode, llnode) {
+               vhost_blk_req_cleanup(req);
+               req->inflight = false;
+               forget_request(req);
+       }
+       mutex_unlock(&blk_vq->vq.mutex);
+}
+
 /* It is forbidden to call more than one vhost_blk_flush() simultaneously */
 static void vhost_blk_flush(struct vhost_blk *blk)
 {
@@ -730,6 +754,10 @@ static void vhost_blk_drop_backend(struct vhost_blk *blk)
 
        vhost_blk_flush(blk);
 
+       synchronize_rcu();
+       for (i = 0; i < VHOST_BLK_VQ_MAX; i++)
+               flush_work(&blk->vqs[i].drop_work);
+
        for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
                kvfree(blk->vqs[i].req);
                blk->vqs[i].req = NULL;
@@ -803,8 +831,10 @@ static int vhost_blk_open(struct inode *inode, struct file 
*file)
                       VHOST_DEV_WEIGHT, VHOST_DEV_PKT_WEIGHT, true, NULL);
        file->private_data = blk;
 
-       for (i = 0; i < VHOST_BLK_VQ_MAX; i++)
+       for (i = 0; i < VHOST_BLK_VQ_MAX; i++) {
                vhost_work_init(&blk->vqs[i].work, vhost_blk_handle_host_kick);
+               INIT_WORK(&blk->vqs[i].drop_work, vhost_blk_drop_reqs);
+       }
 
        return ret;
 out_blk:
-- 
2.53.0

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to