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
