return 0;
}
+static int
+flush_bdev_mapping(struct xenbus_device *dev, struct xen_vbd *vbd)
+{
+ int err;
+
+ err = filemap_write_and_wait(vbd->bdev->bd_inode->i_mapping);
+
+ if (!err)
+ invalidate_inode_pages2(vbd->bdev->bd_inode->i_mapping);
+
+ return err;
+}
+
static void xen_update_blkif_status(struct xen_blkif *blkif)
{
int err;
return;
}
- err = filemap_write_and_wait(blkif->vbd.bdev->bd_inode->i_mapping);
+ err = flush_bdev_mapping(blkif->be->dev, &blkif->vbd);
if (err) {
xenbus_dev_error(blkif->be->dev, err, "block flush");
return;
}
- invalidate_inode_pages2(blkif->vbd.bdev->bd_inode->i_mapping);
for (i = 0; i < blkif->nr_rings; i++) {
ring = &blkif->rings[i];
}
static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
- struct blkif_params *p)
+ struct blkif_params *p, struct xen_vbd *vbd)
{
- struct xen_vbd *vbd;
struct block_device *bdev;
struct request_queue *q;
- vbd = &blkif->vbd;
vbd->handle = handle;
vbd->readonly = p->readonly;
vbd->type = 0;
return 0;
}
+static int
+validate_swap_params(struct backend_info *be, struct blkif_params *p)
+{
+ int err = -EINVAL;
+
+ if (be->params.major != p->major ||
+ be->params.minor != p->minor) {
+ if (be->params.readonly == p->readonly &&
+ be->params.cdrom == p->cdrom) {
+ err = 0;
+ }
+ }
+
+ pr_warn("changing physical device (from %x:%x to %x:%x)%s.\n",
+ be->params.major, be->params.minor,
+ p->major, p->minor,
+ err ? " not supported":"");
+ return err;
+}
+
/*
* Callback received when the hotplug scripts have placed the physical-device
* node. Read it and the mode node, and create a vbd. If the frontend is
= container_of(watch, struct backend_info, backend_watch);
struct xenbus_device *dev = be->dev;
unsigned long handle;
- struct blkif_params p;
+ struct blkif_params oldp, newp;
+ struct xen_vbd vbd;
+ bool swap = false;
pr_debug("%s %p %d\n", __func__, dev, dev->otherend_id);
- err = xenbus_scan_be_params(&p, dev, &handle);
+ err = xenbus_scan_be_params(&newp, dev, &handle);
if (err)
return;
if (be->params.major | be->params.minor) {
- err = -EINVAL;
- if (be->params.major != p.major || be->params.minor != p.minor)
- pr_warn("changing physical device (from %x:%x to %x:%x) not supported.\n",
- be->params.major, be->params.minor,
- p.major, p.minor);
- goto out;
+ err = validate_swap_params(be, &newp);
+ if (err)
+ goto out;
+ swap = true;
}
- err = xen_vbd_create(be->blkif, handle, &p);
+ err = xen_vbd_create(be->blkif, handle, &newp, &vbd);
if (err) {
xenbus_dev_fatal(dev, err, "creating vbd structure");
goto out;
}
- be->params = p;
+ /*
+ * Save the current params: any errors after this
+ * point and we will have to restore them.
+ */
+ oldp = be->params;
+ be->params = newp;
- err = xenvbd_sysfs_addif(dev);
- if (err) {
+ if (swap) {
+ /* Flush, invalidate extant mappings */
+ err = flush_bdev_mapping(dev, &be->blkif->vbd);
+ if (err) {
+ /*
+ * In case of flush error, free the new vbd
+ * and continue with the old one.
+ */
+ xen_vbd_free(&vbd);
+ xenbus_dev_error(dev, err, "bdev flush error");
+ goto flush_sysfs_out;
+ }
+
+ /* Free old vbd and switch over to the new one */
xen_vbd_free(&be->blkif->vbd);
- xenbus_dev_fatal(dev, err, "creating sysfs entries");
- goto sysfs_out;
- }
+ be->blkif->vbd = vbd;
+ } else {
+ be->blkif->vbd = vbd;
- /* We're potentially connected now */
- xen_update_blkif_status(be->blkif);
+ /* We have a new vbd, add sysfs entries */
+ err = xenvbd_sysfs_addif(dev);
+ if (err) {
+ xen_vbd_free(&be->blkif->vbd);
+ xenbus_dev_fatal(dev, err, "creating sysfs entries");
+ goto flush_sysfs_out;
+ }
+
+ /* We're potentially connected now */
+ xen_update_blkif_status(be->blkif);
+ }
-sysfs_out:
+flush_sysfs_out:
/*
- * In case of failure reset the params.
+ * In case of failure we continue with the unchanged
+ * vbd (which in case of the !swap case might be NULL.)
+ * Switch over to the saved params.
*/
if (err)
- memset(&be->params, 0, sizeof(be->params));
+ be->params = oldp;
out:
/*
* err = 0: emit new vbd, err = 0 to the xenbus
*
* Volume Swapping:
*
- * blkback plugs in the backing volume based on changes to the "physical-device"
- * xenstore key.
+ * blkback plugs or swaps in the backing volume based on changes to the
+ * "physical-device" xenstore key.
*
- * Once the backing volume is plugged in, blkback writes to the
- * "oracle/active-physical-device" with the now active volume, and
- * "oracle/physical-device-status" with the error code. The error
- * code follows backend OS convention. On Linux, defined in
+ * Once the backing volume is plugged or swapped in, blkback writes to the
+ * "oracle/active-physical-device" with the now active volume (or the old device
+ * on failed swap), and "oracle/physical-device-status" with the error code.
+ * The error code follows backend OS convention. On Linux, defined in
* /usr/include/asm/errno*h.
*
- * For instance, the toolstack plugging in a guest disk would write:
+ * For instance, a toolstack plugging in or hot-swapping a guest disk would
+ * write:
*
* /local/domain/0/backend/vbd/<domid>/51712/physical-device = "1a:2"
*
- * And backend would validate and plug in the new physical device and its
- * status (e.g. in case of errors):
+ * This would be validated by the backend which would plug or swap in the new
+ * physical device and update the device and its error status:
*
* /local/domain/0/backend/vbd/<domid>/51712/oracle/active-physical-device = "1a:2"
* /local/domain/0/backend/vbd/<domid>/51712/oracle/physical-device-status = "0"