From 0281f5b41068bd0be45e799a61272dc646ac7b50 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 7 Mar 2017 16:41:31 -0500 Subject: [PATCH] sparc64: VDC threads in guest domain do not resume after primary domain reboot Prevents VDC threads from hanging while waiting for primary domain to come back up. Ensures that all waiting VDC threads are woken up when primary domain comes back up. Orabug: 25519961 Signed-off-by: Jagannathan Raman Reviewed-by: Alexandre Chartre Reviewed-by: Bijan Mottahedeh Signed-off-by: Allen Pais --- drivers/block/sunvdc.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index b43ee6f613c66..5898a19030e00 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -56,7 +56,8 @@ struct vdc_port { struct gendisk *disk; - struct vio_completion *cmp; + struct vio_completion *cmp; /* Generic request completion */ + struct vio_completion cmp_hs; /* Handshake completion */ u64 req_id; u64 seq; @@ -184,8 +185,7 @@ static void vdc_handshake_complete(struct vio_driver_state *vio) struct vdc_port *port = to_vdc_port(vio); del_timer(&port->ldc_reset_timer); - vdc_finish(vio->cmp, 0, WAITING_FOR_LINK_UP); - vio->cmp = NULL; + vdc_finish(&port->cmp_hs, 0, WAITING_FOR_LINK_UP); } static int vdc_handle_unknown(struct vdc_port *port, void *arg) @@ -915,18 +915,16 @@ static void vdc_free_tx_ring(struct vdc_port *port) } } +/* + * This function ensures that the communication channel between VDC & VDS + * is established. It assumes that the handshake completion variable is + * initialized before it is called. + */ static int vdc_port_up(struct vdc_port *port) { - struct vio_completion comp; - - init_completion(&comp.com); - comp.err = 0; - comp.waiting_for = WAITING_FOR_LINK_UP; - port->vio.cmp = ∁ - vio_port_up(&port->vio); - wait_for_completion(&comp.com); - return comp.err; + wait_for_completion(&port->cmp_hs.com); + return port->cmp_hs.err; } static int probe_disk(struct vdc_port *port) @@ -1116,6 +1114,10 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (err) goto err_out_free_port; + init_completion(&port->cmp_hs.com); + port->cmp_hs.err = 0; + port->cmp_hs.waiting_for = WAITING_FOR_LINK_UP; + port->vdisk_block_size = VDC_DEFAULT_BLK_SIZE; port->max_xfer_size = ((128 * 1024) / port->vdisk_block_size); port->ring_cookies = ((port->max_xfer_size * @@ -1263,7 +1265,7 @@ static void vdc_ldc_reset(struct vdc_port *port) if (port->flags & VDC_PORT_RESET) { spin_unlock_irqrestore(&vio->lock, flags); - wait_for_completion(&port->vio.cmp->com); + wait_for_completion(&port->cmp_hs.com); return; } @@ -1272,6 +1274,19 @@ static void vdc_ldc_reset(struct vdc_port *port) vio_link_state_change(vio, LDC_EVENT_RESET); port->flags |= VDC_PORT_RESET; + + /* + * To avoid potential race between multiple VDC threads + * trying to reset the LDC channel concurrently, the + * handshake completion variable is initialized while + * holding vio->lock. Otherwise there's a window where + * vdc_ldc_reset() can call wait_for_completion() but + * reinit_completion() hasn't been called yet. + */ + reinit_completion(&port->cmp_hs.com); + port->cmp_hs.err = 0; + port->cmp_hs.waiting_for = WAITING_FOR_LINK_UP; + spin_unlock_irqrestore(&vio->lock, flags); err = vdc_port_up(port); spin_lock_irqsave(&vio->lock, flags); -- 2.50.1