From: Juergen Gross Date: Wed, 19 Dec 2018 00:31:01 +0000 (+0800) Subject: xen/blkback: fix disconnect while I/Os in flight X-Git-Tag: v4.1.12-124.31.3~358 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=2c976e363616a7340b7f18f446676813bd766e06;p=users%2Fjedix%2Flinux-maple.git xen/blkback: fix disconnect while I/Os in flight Today disconnecting xen-blkback is broken in case there are still I/Os in flight: xen_blkif_disconnect() will bail out early without releasing all resources in the hope it will be called again when the last request has terminated. This, however, won't happen as xen_blkif_free() won't be called on termination of the last running request: xen_blkif_put() won't decrement the blkif refcnt to 0 as xen_blkif_disconnect() didn't finish before thus some xen_blkif_put() calls in xen_blkif_disconnect() didn't happen. To solve this deadlock xen_blkif_disconnect() and xen_blkif_alloc_rings() shouldn't use xen_blkif_put() and xen_blkif_get() but use some other way to do their accounting of resources. This at once fixes another error in xen_blkif_disconnect(): when it returned early with -EBUSY for another ring than 0 it would call xen_blkif_put() again for already handled rings on a subsequent call. This will lead to inconsistencies in the refcnt handling. Cc: stable@vger.kernel.org Signed-off-by: Juergen Gross Tested-by: Steven Haigh Acked-by: Roger Pau Monné Signed-off-by: Konrad Rzeszutek Wilk Orabug: 28744234 (cherry picked from commit 46464411307746e6297a034a9983a22c9dfc5a0c) Signed-off-by: Brian Maly Conflicts: drivers/block/xen-blkback/xenbus.c The objective of this patch backport is not for the deadlock issue, as there is no xen_blkif_put() called in xen_blkif_disconnect() due to conflicts. xen_blkif_disconnect() may be entered twice during VM destroy. When there is in-flight I/O for any rings, to enter xen_blkif_disconnect() for the second the time would trigger the "WARN_ON(i != (XEN_BLKIF_REQS_PER_PAGE * blkif->nr_ring_pages));". The 'active' would guarantee the ring would be skipped if it is already cleaned up when xen_blkif_disconnect() is entered the second time. Signed-off-by: Dongli Zhang Reviewed-by: Joe Jin Signed-off-by: Brian Maly --- diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 242808a24be0..7a7eac63a3d1 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -264,6 +264,7 @@ struct xen_blkif_ring { wait_queue_head_t wq; atomic_t inflight; + bool active; /* One thread per blkif ring. */ struct task_struct *xenblkd; unsigned int waiting_reqs; diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 16cf1381508e..a43fd7db4a33 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -163,6 +163,7 @@ static int xen_blkif_alloc_rings(struct xen_blkif *blkif) init_waitqueue_head(&ring->shutdown_wq); ring->blkif = blkif; ring->st_print = jiffies; + ring->active = true; } return 0; @@ -254,6 +255,9 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) struct xen_blkif_ring *ring = &blkif->rings[r]; unsigned int i = 0; + if (!ring->active) + continue; + if (ring->xenblkd) { kthread_stop(ring->xenblkd); wake_up(&ring->shutdown_wq); @@ -296,6 +300,7 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) BUG_ON(ring->free_pages_num != 0); BUG_ON(ring->persistent_gnt_c != 0); WARN_ON(i != (XEN_BLKIF_REQS_PER_PAGE * blkif->nr_ring_pages)); + ring->active = false; } if (busy) return -EBUSY;