]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: VDC threads in guest domain do not resume after primary domain reboot
authorJag Raman <jag.raman@oracle.com>
Tue, 7 Mar 2017 21:41:31 +0000 (16:41 -0500)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 6 Apr 2017 07:13:45 +0000 (00:13 -0700)
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 <jag.raman@oracle.com>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Reviewed-by: Bijan Mottahedeh <bijan.mottahedeh@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
drivers/block/sunvdc.c

index b43ee6f613c6689d185a5b1ff25f45159d98747f..5898a19030e00307b0ddaafb3cf30dcf805e6182 100644 (file)
@@ -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 = &comp;
-
        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);