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;
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)
}
}
+/*
+ * 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)
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 *
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;
}
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);