diff mbox

[v2,4/4] migration: Fail migration blocker for --only-migratble

Message ID 1481882024-10016-5-git-send-email-ashijeetacharya@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ashijeet Acharya Dec. 16, 2016, 9:53 a.m. UTC
migrate_add_blocker should rightly fail if the '--only-migratable'
option was specified and the device in use should not be able to
perform the action which results in an unmigratable VM.

Make migrate_add_blocker return -EACCES in this case.

Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>
---
 block/qcow.c                |  7 +++++--
 block/vdi.c                 |  7 +++++--
 block/vhdx.c                |  8 +++++---
 block/vmdk.c                |  7 +++++--
 block/vpc.c                 |  7 +++++--
 block/vvfat.c               |  8 +++++---
 hw/9pfs/9p.c                |  9 +++++++--
 hw/display/virtio-gpu.c     |  9 +++++++--
 hw/intc/arm_gic_kvm.c       |  7 +++++--
 hw/intc/arm_gicv3_its_kvm.c |  5 ++++-
 hw/intc/arm_gicv3_kvm.c     |  5 ++++-
 hw/misc/ivshmem.c           | 10 ++++++++--
 hw/scsi/vhost-scsi.c        | 10 ++++++----
 hw/virtio/vhost.c           |  8 +++++---
 migration/migration.c       |  7 +++++++
 target-i386/kvm.c           |  7 +++++--
 16 files changed, 88 insertions(+), 33 deletions(-)

Comments

Greg Kurz Dec. 16, 2016, 11:14 a.m. UTC | #1
On Fri, 16 Dec 2016 15:23:44 +0530
Ashijeet Acharya <ashijeetacharya@gmail.com> wrote:

> migrate_add_blocker should rightly fail if the '--only-migratable'
> option was specified and the device in use should not be able to
> perform the action which results in an unmigratable VM.
> 
> Make migrate_add_blocker return -EACCES in this case.
> 
> Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>
> ---
>  block/qcow.c                |  7 +++++--
>  block/vdi.c                 |  7 +++++--
>  block/vhdx.c                |  8 +++++---
>  block/vmdk.c                |  7 +++++--
>  block/vpc.c                 |  7 +++++--
>  block/vvfat.c               |  8 +++++---
>  hw/9pfs/9p.c                |  9 +++++++--
>  hw/display/virtio-gpu.c     |  9 +++++++--
>  hw/intc/arm_gic_kvm.c       |  7 +++++--
>  hw/intc/arm_gicv3_its_kvm.c |  5 ++++-
>  hw/intc/arm_gicv3_kvm.c     |  5 ++++-
>  hw/misc/ivshmem.c           | 10 ++++++++--
>  hw/scsi/vhost-scsi.c        | 10 ++++++----
>  hw/virtio/vhost.c           |  8 +++++---
>  migration/migration.c       |  7 +++++++
>  target-i386/kvm.c           |  7 +++++--
>  16 files changed, 88 insertions(+), 33 deletions(-)
> 
> diff --git a/block/qcow.c b/block/qcow.c
> index 11526a1..7e5003ac 100644
> --- a/block/qcow.c
> +++ b/block/qcow.c
> @@ -253,8 +253,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with qcow format as "
> +                              "it does not support live migration");
> +        }
>          goto fail;
>      }
>  
> diff --git a/block/vdi.c b/block/vdi.c
> index 2b56f52..df45df4 100644
> --- a/block/vdi.c
> +++ b/block/vdi.c
> @@ -472,8 +472,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with vdi format as "
> +                              "it does not support live migration");
> +        }
>          goto fail_free_bmap;
>      }
>  
> diff --git a/block/vhdx.c b/block/vhdx.c
> index d81941b..118134e 100644
> --- a/block/vhdx.c
> +++ b/block/vhdx.c
> @@ -996,11 +996,13 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with vhdx format as "
> +                              "it does not support live migration");
> +        }
>          goto fail;
>      }
> -
>      if (flags & BDRV_O_RDWR) {
>          ret = vhdx_update_headers(bs, s, false, NULL);
>          if (ret < 0) {
> diff --git a/block/vmdk.c b/block/vmdk.c
> index 4a45a83..cd6a641 100644
> --- a/block/vmdk.c
> +++ b/block/vmdk.c
> @@ -977,8 +977,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with vmdk format as "
> +                              "it does not support live migration");
> +        }
>          goto fail;
>      }
>  
> diff --git a/block/vpc.c b/block/vpc.c
> index 299a8c8..c935962 100644
> --- a/block/vpc.c
> +++ b/block/vpc.c
> @@ -427,8 +427,11 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with vpc format as "
> +                              "it does not support live migration");
> +        }
>          goto fail;
>      }
>  
> diff --git a/block/vvfat.c b/block/vvfat.c
> index 3de3320..f9a5f9b 100644
> --- a/block/vvfat.c
> +++ b/block/vvfat.c
> @@ -1192,8 +1192,11 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
>                     "does not support live migration",
>                     bdrv_get_device_or_node_name(bs));
>          ret = migrate_add_blocker(s->migration_blocker, errp);
> -        if (ret < 0) {
> -            error_free(s->migration_blocker);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Cannot use a node with vvfat format "
> +                                  "as it does not support live migration");
> +            }
>              goto fail;
>          }
>      }
> @@ -1202,7 +1205,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
>          init_mbr(s, cyls, heads, secs);
>      }
>  
> -    //    assert(is_consistent(s));
>      qemu_co_mutex_init(&s->lock);
>  
>      ret = 0;
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index e72ef43..01dd67f 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -1020,12 +1020,17 @@ static void coroutine_fn v9fs_attach(void *opaque)
>       */
>      if (!s->migration_blocker) {
>          int ret;
> +        Error *local_err;
>          s->root_fid = fid;
>          error_setg(&s->migration_blocker,
>                     "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
>                     s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
> -        ret = migrate_add_blocker(s->migration_blocker, NULL);
> -        if (ret < 0) {
> +        ret = migrate_add_blocker(s->migration_blocker, &local_err);
> +        if (ret) {

Why s/ret < 0/ret/ ? The migrate_add_blocker() function in this v2 only
returns 0 or a negative errno...

> +            if (ret == -EACCES) {
> +                error_append_hint(&local_err, "Failed to mount VirtFS as it "
> +                                  "does not support live migration");

And then local_err is leaked...

BTW, I'm not even sure if this useful since this is a guest originated action.

> +            }

This errno will be ultimately be returned to the mount(2) caller in the guest.

When reading the manpage, it looks like EBUSY definitely makes more sense than
EACCES:

       EACCES A component of a path was not searchable.  (See also  path_reso‐
              lution(7).)   Or,  mounting a read-only filesystem was attempted
              without giving the MS_RDONLY flag.  Or, the block device  source
              is located on a filesystem mounted with the MS_NODEV option.

       EBUSY  source  is  already  mounted.   Or, it cannot be remounted read-
              only, because it still holds files open  for  writing.   Or,  it
              cannot  be mounted on target because target is still busy (it is
              the working directory of some thread, the mount point of another
              device, has open files, etc.).

>              err = ret;
>              clunk_fid(s, fid);
>              goto out;
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> index 6e60b8c..1b02f4b 100644
> --- a/hw/display/virtio-gpu.c
> +++ b/hw/display/virtio-gpu.c
> @@ -1102,6 +1102,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>      VirtIOGPU *g = VIRTIO_GPU(qdev);
>      bool have_virgl;
>      int i;
> +    int ret;
>  
>      if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
>          error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
> @@ -1120,8 +1121,12 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>  
>      if (virtio_gpu_virgl_enabled(g->conf)) {
>          error_setg(&g->migration_blocker, "virgl is not yet migratable");
> -        if (migrate_add_blocker(g->migration_blocker, errp) < 0) {
> -            error_free(g->migration_blocker);
> +        ret = migrate_add_blocker(g->migration_blocker, errp);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Cannot use virgl as it does not "
> +                                  "support live migration yet");
> +            }
>              return;
>          }
>      }
> diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
> index 3614daa..b40ad5f 100644
> --- a/hw/intc/arm_gic_kvm.c
> +++ b/hw/intc/arm_gic_kvm.c
> @@ -514,8 +514,11 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>          error_setg(&s->migration_blocker, "This operating system kernel does "
>                                            "not support vGICv2 migration");
>          ret = migrate_add_blocker(s->migration_blocker, errp);
> -        if (ret < 0) {
> -            error_free(s->migration_blocker);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Failed to realize vGICv2 as its"
> +                                  " migrataion is not implemented yet");
> +            }
>              return;
>          }
>      }
> diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
> index 950a02f..3474642 100644
> --- a/hw/intc/arm_gicv3_its_kvm.c
> +++ b/hw/intc/arm_gicv3_its_kvm.c
> @@ -70,7 +70,10 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
>      error_setg(&s->migration_blocker, "vITS migration is not implemented");
>      ret = migrate_add_blocker(s->migration_blocker, errp);
>      if (ret < 0) {
> -        error_free(s->migration_blocker);
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Failed to realize vITS as its migration "
> +                              "is not implemented yet");
> +        }
>          return;
>      }
>  
> diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
> index 06f6f30..be7b97c 100644
> --- a/hw/intc/arm_gicv3_kvm.c
> +++ b/hw/intc/arm_gicv3_kvm.c
> @@ -118,7 +118,10 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
>      error_setg(&s->migration_blocker, "vGICv3 migration is not implemented");
>      ret = migrate_add_blocker(s->migration_blocker, errp);
>      if (ret < 0) {
> -        error_free(s->migration_blocker);
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Failed to realize vGICv3 as its migration"
> +                              "is not implemented yet");
> +        }
>          return;
>      }
>  
> diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
> index 585cc5b..58f27b1 100644
> --- a/hw/misc/ivshmem.c
> +++ b/hw/misc/ivshmem.c
> @@ -837,6 +837,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
>  {
>      IVShmemState *s = IVSHMEM_COMMON(dev);
>      Error *err = NULL;
> +    int ret;
>      uint8_t *pci_conf;
>      uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
>          PCI_BASE_ADDRESS_MEM_PREFETCH;
> @@ -910,8 +911,13 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
>      if (!ivshmem_is_master(s)) {
>          error_setg(&s->migration_blocker,
>                     "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
> -        if (migrate_add_blocker(s->migration_blocker, errp) < 0) {
> -            error_free(s->migration_blocker);
> +        ret = migrate_add_blocker(s->migration_blocker, errp);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Cannot use the 'peer mode' feature "
> +                                  "in device 'ivshmem' as it does not "
> +                                  "support live migration");
> +            }
>              return;
>          }
>      }
> diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
> index ff503d0..0c571f8 100644
> --- a/hw/scsi/vhost-scsi.c
> +++ b/hw/scsi/vhost-scsi.c
> @@ -244,8 +244,12 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
>      error_setg(&s->migration_blocker,
>                 "vhost-scsi does not support migration");
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        goto free_blocker;
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use vhost-scsi as it does not "
> +                              "support live migration");
> +        }
> +        goto close_fd;
>      }
>  
>      s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
> @@ -272,8 +276,6 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
>   free_vqs:
>      migrate_del_blocker(s->migration_blocker);
>      g_free(s->dev.vqs);
> - free_blocker:
> -    error_free(s->migration_blocker);
>   close_fd:
>      close(vhostfd);
>      return;
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 416fada..1c4a4f9 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -1159,9 +1159,11 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
>      if (hdev->migration_blocker != NULL) {
>          Error *local_err;
>          r = migrate_add_blocker(hdev->migration_blocker, &local_err);
> -        if (r < 0) {
> -            error_report_err(local_err);
> -            error_free(hdev->migration_blocker);
> +        if (r) {
> +            if (r == -EACCES) {
> +                error_append_hint(&local_err, "Cannot use vhost drivers as "
> +                                  "it does not support live migration");
> +            }
>              goto fail_busyloop;
>          }
>      }
> diff --git a/migration/migration.c b/migration/migration.c
> index 713e012..ab34328 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -1115,9 +1115,16 @@ int migrate_add_blocker(Error *reason, Error **errp)
>  {
>      if (migration_is_idle(NULL)) {
>          migration_blockers = g_slist_prepend(migration_blockers, reason);
> +        error_free(reason);
>          return 0;
>      }
>  
> +    if (only_migratable) {
> +        error_setg(errp, "Failed to add migration blocker: --only-migratable "
> +                   "was specified");
> +        return -EACCES;
> +    }
> +
>      error_setg(errp, "Cannot block a migration already in-progress");
>      return -EBUSY;
>  }
> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> index 6031a1c..05c8365 100644
> --- a/target-i386/kvm.c
> +++ b/target-i386/kvm.c
> @@ -962,7 +962,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
>                     "State blocked by non-migratable CPU device"
>                     " (invtsc flag)");
>          r = migrate_add_blocker(invtsc_mig_blocker, NULL);
> -        if (r < 0) {
> +        if (r) {
> +            if (r == -EACCES) {
> +                error_report("kvm: non-migratable CPU device but"
> +                        " --only-migratable was specified");
> +            }
>              error_report("kvm: migrate_add_blocker failed");
>              goto efail;
>          }
> @@ -1009,7 +1013,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
>   fail:
>      migrate_del_blocker(invtsc_mig_blocker);
>   efail:
> -    error_free(invtsc_mig_blocker);
>      return r;
>  }
>
Peter Maydell Dec. 16, 2016, 11:33 a.m. UTC | #2
On 16 December 2016 at 09:53, Ashijeet Acharya
<ashijeetacharya@gmail.com> wrote:
> migrate_add_blocker should rightly fail if the '--only-migratable'
> option was specified and the device in use should not be able to
> perform the action which results in an unmigratable VM.
>
> Make migrate_add_blocker return -EACCES in this case.
>
> Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>

> diff --git a/block/qcow.c b/block/qcow.c
> index 11526a1..7e5003ac 100644
> --- a/block/qcow.c
> +++ b/block/qcow.c
> @@ -253,8 +253,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>                 "does not support live migration",
>                 bdrv_get_device_or_node_name(bs));
>      ret = migrate_add_blocker(s->migration_blocker, errp);
> -    if (ret < 0) {
> -        error_free(s->migration_blocker);
> +    if (ret) {
> +        if (ret == -EACCES) {
> +            error_append_hint(errp, "Cannot use a node with qcow format as "
> +                              "it does not support live migration");
> +        }
>          goto fail;
>      }

> --- a/hw/display/virtio-gpu.c
> +++ b/hw/display/virtio-gpu.c
> @@ -1102,6 +1102,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>      VirtIOGPU *g = VIRTIO_GPU(qdev);
>      bool have_virgl;
>      int i;
> +    int ret;
>
>      if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
>          error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
> @@ -1120,8 +1121,12 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>
>      if (virtio_gpu_virgl_enabled(g->conf)) {
>          error_setg(&g->migration_blocker, "virgl is not yet migratable");
> -        if (migrate_add_blocker(g->migration_blocker, errp) < 0) {
> -            error_free(g->migration_blocker);
> +        ret = migrate_add_blocker(g->migration_blocker, errp);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Cannot use virgl as it does not "
> +                                  "support live migration yet");
> +            }
>              return;
>          }
>      }
> diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
> index 3614daa..b40ad5f 100644
> --- a/hw/intc/arm_gic_kvm.c
> +++ b/hw/intc/arm_gic_kvm.c
> @@ -514,8 +514,11 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>          error_setg(&s->migration_blocker, "This operating system kernel does "
>                                            "not support vGICv2 migration");
>          ret = migrate_add_blocker(s->migration_blocker, errp);
> -        if (ret < 0) {
> -            error_free(s->migration_blocker);
> +        if (ret) {
> +            if (ret == -EACCES) {
> +                error_append_hint(errp, "Failed to realize vGICv2 as its"
> +                                  " migrataion is not implemented yet");
> +            }
>              return;
>          }
>      }

So if you look at these diff hunks you can see that we effectively
end up duplicating the "why doesn't this device let you migrate"
user message -- once in the error_setg(&s->migration_blocker, ...)
and once with the error_append_hint() call.

Is there some way we can get migrate_add_blocker() to automatically
incorporate the message when it's failing for the only-migratable case?
(It's already being passed the errp.)
That would avoid this duplication of the messages, and should also
mean we don't need to modify the callers at all.

If the error messages come out looking a bit strangely worded we
could edit the text in the callers so it works in both situations
when it will be used.

thanks
-- PMM
Ashijeet Acharya Jan. 4, 2017, 10:25 a.m. UTC | #3
On Fri, Dec 16, 2016 at 4:44 PM, Greg Kurz <groug@kaod.org> wrote:
> On Fri, 16 Dec 2016 15:23:44 +0530
> Ashijeet Acharya <ashijeetacharya@gmail.com> wrote:
>
>> migrate_add_blocker should rightly fail if the '--only-migratable'
>> option was specified and the device in use should not be able to
>> perform the action which results in an unmigratable VM.
>>
>> Make migrate_add_blocker return -EACCES in this case.
>>
>> Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>
>> ---
>>  block/qcow.c                |  7 +++++--
>>  block/vdi.c                 |  7 +++++--
>>  block/vhdx.c                |  8 +++++---
>>  block/vmdk.c                |  7 +++++--
>>  block/vpc.c                 |  7 +++++--
>>  block/vvfat.c               |  8 +++++---
>>  hw/9pfs/9p.c                |  9 +++++++--
>>  hw/display/virtio-gpu.c     |  9 +++++++--
>>  hw/intc/arm_gic_kvm.c       |  7 +++++--
>>  hw/intc/arm_gicv3_its_kvm.c |  5 ++++-
>>  hw/intc/arm_gicv3_kvm.c     |  5 ++++-
>>  hw/misc/ivshmem.c           | 10 ++++++++--
>>  hw/scsi/vhost-scsi.c        | 10 ++++++----
>>  hw/virtio/vhost.c           |  8 +++++---
>>  migration/migration.c       |  7 +++++++
>>  target-i386/kvm.c           |  7 +++++--
>>  16 files changed, 88 insertions(+), 33 deletions(-)
>>
>> diff --git a/block/qcow.c b/block/qcow.c
>> index 11526a1..7e5003ac 100644
>> --- a/block/qcow.c
>> +++ b/block/qcow.c
>> @@ -253,8 +253,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with qcow format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail;
>>      }
>>
>> diff --git a/block/vdi.c b/block/vdi.c
>> index 2b56f52..df45df4 100644
>> --- a/block/vdi.c
>> +++ b/block/vdi.c
>> @@ -472,8 +472,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with vdi format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail_free_bmap;
>>      }
>>
>> diff --git a/block/vhdx.c b/block/vhdx.c
>> index d81941b..118134e 100644
>> --- a/block/vhdx.c
>> +++ b/block/vhdx.c
>> @@ -996,11 +996,13 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with vhdx format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail;
>>      }
>> -
>>      if (flags & BDRV_O_RDWR) {
>>          ret = vhdx_update_headers(bs, s, false, NULL);
>>          if (ret < 0) {
>> diff --git a/block/vmdk.c b/block/vmdk.c
>> index 4a45a83..cd6a641 100644
>> --- a/block/vmdk.c
>> +++ b/block/vmdk.c
>> @@ -977,8 +977,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with vmdk format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail;
>>      }
>>
>> diff --git a/block/vpc.c b/block/vpc.c
>> index 299a8c8..c935962 100644
>> --- a/block/vpc.c
>> +++ b/block/vpc.c
>> @@ -427,8 +427,11 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with vpc format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail;
>>      }
>>
>> diff --git a/block/vvfat.c b/block/vvfat.c
>> index 3de3320..f9a5f9b 100644
>> --- a/block/vvfat.c
>> +++ b/block/vvfat.c
>> @@ -1192,8 +1192,11 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
>>                     "does not support live migration",
>>                     bdrv_get_device_or_node_name(bs));
>>          ret = migrate_add_blocker(s->migration_blocker, errp);
>> -        if (ret < 0) {
>> -            error_free(s->migration_blocker);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Cannot use a node with vvfat format "
>> +                                  "as it does not support live migration");
>> +            }
>>              goto fail;
>>          }
>>      }
>> @@ -1202,7 +1205,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
>>          init_mbr(s, cyls, heads, secs);
>>      }
>>
>> -    //    assert(is_consistent(s));
>>      qemu_co_mutex_init(&s->lock);
>>
>>      ret = 0;
>> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
>> index e72ef43..01dd67f 100644
>> --- a/hw/9pfs/9p.c
>> +++ b/hw/9pfs/9p.c
>> @@ -1020,12 +1020,17 @@ static void coroutine_fn v9fs_attach(void *opaque)
>>       */
>>      if (!s->migration_blocker) {
>>          int ret;
>> +        Error *local_err;
>>          s->root_fid = fid;
>>          error_setg(&s->migration_blocker,
>>                     "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
>>                     s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
>> -        ret = migrate_add_blocker(s->migration_blocker, NULL);
>> -        if (ret < 0) {
>> +        ret = migrate_add_blocker(s->migration_blocker, &local_err);
>> +        if (ret) {
>
> Why s/ret < 0/ret/ ? The migrate_add_blocker() function in this v2 only
> returns 0 or a negative errno...

No reason, maybe I forgot to change it back because I got no logical
error after compiling but I have fixed this at all occurrences.

>
>> +            if (ret == -EACCES) {
>> +                error_append_hint(&local_err, "Failed to mount VirtFS as it "
>> +                                  "does not support live migration");
>
> And then local_err is leaked...

Yes, I have freed it with error_free().

>
> BTW, I'm not even sure if this useful since this is a guest originated action.
>
>> +            }
>
> This errno will be ultimately be returned to the mount(2) caller in the guest.
>
> When reading the manpage, it looks like EBUSY definitely makes more sense than
> EACCES:
>
>        EACCES A component of a path was not searchable.  (See also  path_reso‐
>               lution(7).)   Or,  mounting a read-only filesystem was attempted
>               without giving the MS_RDONLY flag.  Or, the block device  source
>               is located on a filesystem mounted with the MS_NODEV option.
>
>        EBUSY  source  is  already  mounted.   Or, it cannot be remounted read-
>               only, because it still holds files open  for  writing.   Or,  it
>               cannot  be mounted on target because target is still busy (it is
>               the working directory of some thread, the mount point of another
>               device, has open files, etc.).
>

Reading the manpages definitely make your argument stronger but I am
not sure if I will be able to differentiate between what caused
the migrate_add_blocker to fail if I get -EBUSY for both the cases.

Ashijeet
>>              err = ret;
>>              clunk_fid(s, fid);
>>              goto out;
>> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
>> index 6e60b8c..1b02f4b 100644
>> --- a/hw/display/virtio-gpu.c
>> +++ b/hw/display/virtio-gpu.c
>> @@ -1102,6 +1102,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>>      VirtIOGPU *g = VIRTIO_GPU(qdev);
>>      bool have_virgl;
>>      int i;
>> +    int ret;
>>
>>      if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
>>          error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
>> @@ -1120,8 +1121,12 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>>
>>      if (virtio_gpu_virgl_enabled(g->conf)) {
>>          error_setg(&g->migration_blocker, "virgl is not yet migratable");
>> -        if (migrate_add_blocker(g->migration_blocker, errp) < 0) {
>> -            error_free(g->migration_blocker);
>> +        ret = migrate_add_blocker(g->migration_blocker, errp);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Cannot use virgl as it does not "
>> +                                  "support live migration yet");
>> +            }
>>              return;
>>          }
>>      }
>> diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
>> index 3614daa..b40ad5f 100644
>> --- a/hw/intc/arm_gic_kvm.c
>> +++ b/hw/intc/arm_gic_kvm.c
>> @@ -514,8 +514,11 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>>          error_setg(&s->migration_blocker, "This operating system kernel does "
>>                                            "not support vGICv2 migration");
>>          ret = migrate_add_blocker(s->migration_blocker, errp);
>> -        if (ret < 0) {
>> -            error_free(s->migration_blocker);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Failed to realize vGICv2 as its"
>> +                                  " migrataion is not implemented yet");
>> +            }
>>              return;
>>          }
>>      }
>> diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
>> index 950a02f..3474642 100644
>> --- a/hw/intc/arm_gicv3_its_kvm.c
>> +++ b/hw/intc/arm_gicv3_its_kvm.c
>> @@ -70,7 +70,10 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
>>      error_setg(&s->migration_blocker, "vITS migration is not implemented");
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>>      if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Failed to realize vITS as its migration "
>> +                              "is not implemented yet");
>> +        }
>>          return;
>>      }
>>
>> diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
>> index 06f6f30..be7b97c 100644
>> --- a/hw/intc/arm_gicv3_kvm.c
>> +++ b/hw/intc/arm_gicv3_kvm.c
>> @@ -118,7 +118,10 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
>>      error_setg(&s->migration_blocker, "vGICv3 migration is not implemented");
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>>      if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Failed to realize vGICv3 as its migration"
>> +                              "is not implemented yet");
>> +        }
>>          return;
>>      }
>>
>> diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
>> index 585cc5b..58f27b1 100644
>> --- a/hw/misc/ivshmem.c
>> +++ b/hw/misc/ivshmem.c
>> @@ -837,6 +837,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
>>  {
>>      IVShmemState *s = IVSHMEM_COMMON(dev);
>>      Error *err = NULL;
>> +    int ret;
>>      uint8_t *pci_conf;
>>      uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
>>          PCI_BASE_ADDRESS_MEM_PREFETCH;
>> @@ -910,8 +911,13 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
>>      if (!ivshmem_is_master(s)) {
>>          error_setg(&s->migration_blocker,
>>                     "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
>> -        if (migrate_add_blocker(s->migration_blocker, errp) < 0) {
>> -            error_free(s->migration_blocker);
>> +        ret = migrate_add_blocker(s->migration_blocker, errp);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Cannot use the 'peer mode' feature "
>> +                                  "in device 'ivshmem' as it does not "
>> +                                  "support live migration");
>> +            }
>>              return;
>>          }
>>      }
>> diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
>> index ff503d0..0c571f8 100644
>> --- a/hw/scsi/vhost-scsi.c
>> +++ b/hw/scsi/vhost-scsi.c
>> @@ -244,8 +244,12 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
>>      error_setg(&s->migration_blocker,
>>                 "vhost-scsi does not support migration");
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        goto free_blocker;
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use vhost-scsi as it does not "
>> +                              "support live migration");
>> +        }
>> +        goto close_fd;
>>      }
>>
>>      s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
>> @@ -272,8 +276,6 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
>>   free_vqs:
>>      migrate_del_blocker(s->migration_blocker);
>>      g_free(s->dev.vqs);
>> - free_blocker:
>> -    error_free(s->migration_blocker);
>>   close_fd:
>>      close(vhostfd);
>>      return;
>> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
>> index 416fada..1c4a4f9 100644
>> --- a/hw/virtio/vhost.c
>> +++ b/hw/virtio/vhost.c
>> @@ -1159,9 +1159,11 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
>>      if (hdev->migration_blocker != NULL) {
>>          Error *local_err;
>>          r = migrate_add_blocker(hdev->migration_blocker, &local_err);
>> -        if (r < 0) {
>> -            error_report_err(local_err);
>> -            error_free(hdev->migration_blocker);
>> +        if (r) {
>> +            if (r == -EACCES) {
>> +                error_append_hint(&local_err, "Cannot use vhost drivers as "
>> +                                  "it does not support live migration");
>> +            }
>>              goto fail_busyloop;
>>          }
>>      }
>> diff --git a/migration/migration.c b/migration/migration.c
>> index 713e012..ab34328 100644
>> --- a/migration/migration.c
>> +++ b/migration/migration.c
>> @@ -1115,9 +1115,16 @@ int migrate_add_blocker(Error *reason, Error **errp)
>>  {
>>      if (migration_is_idle(NULL)) {
>>          migration_blockers = g_slist_prepend(migration_blockers, reason);
>> +        error_free(reason);
>>          return 0;
>>      }
>>
>> +    if (only_migratable) {
>> +        error_setg(errp, "Failed to add migration blocker: --only-migratable "
>> +                   "was specified");
>> +        return -EACCES;
>> +    }
>> +
>>      error_setg(errp, "Cannot block a migration already in-progress");
>>      return -EBUSY;
>>  }
>> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
>> index 6031a1c..05c8365 100644
>> --- a/target-i386/kvm.c
>> +++ b/target-i386/kvm.c
>> @@ -962,7 +962,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
>>                     "State blocked by non-migratable CPU device"
>>                     " (invtsc flag)");
>>          r = migrate_add_blocker(invtsc_mig_blocker, NULL);
>> -        if (r < 0) {
>> +        if (r) {
>> +            if (r == -EACCES) {
>> +                error_report("kvm: non-migratable CPU device but"
>> +                        " --only-migratable was specified");
>> +            }
>>              error_report("kvm: migrate_add_blocker failed");
>>              goto efail;
>>          }
>> @@ -1009,7 +1013,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
>>   fail:
>>      migrate_del_blocker(invtsc_mig_blocker);
>>   efail:
>> -    error_free(invtsc_mig_blocker);
>>      return r;
>>  }
>>
>
Ashijeet Acharya Jan. 4, 2017, 11:31 a.m. UTC | #4
On Fri, Dec 16, 2016 at 5:03 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 16 December 2016 at 09:53, Ashijeet Acharya
> <ashijeetacharya@gmail.com> wrote:
>> migrate_add_blocker should rightly fail if the '--only-migratable'
>> option was specified and the device in use should not be able to
>> perform the action which results in an unmigratable VM.
>>
>> Make migrate_add_blocker return -EACCES in this case.
>>
>> Signed-off-by: Ashijeet Acharya <ashijeetacharya@gmail.com>
>
>> diff --git a/block/qcow.c b/block/qcow.c
>> index 11526a1..7e5003ac 100644
>> --- a/block/qcow.c
>> +++ b/block/qcow.c
>> @@ -253,8 +253,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
>>                 "does not support live migration",
>>                 bdrv_get_device_or_node_name(bs));
>>      ret = migrate_add_blocker(s->migration_blocker, errp);
>> -    if (ret < 0) {
>> -        error_free(s->migration_blocker);
>> +    if (ret) {
>> +        if (ret == -EACCES) {
>> +            error_append_hint(errp, "Cannot use a node with qcow format as "
>> +                              "it does not support live migration");
>> +        }
>>          goto fail;
>>      }
>
>> --- a/hw/display/virtio-gpu.c
>> +++ b/hw/display/virtio-gpu.c
>> @@ -1102,6 +1102,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>>      VirtIOGPU *g = VIRTIO_GPU(qdev);
>>      bool have_virgl;
>>      int i;
>> +    int ret;
>>
>>      if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
>>          error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
>> @@ -1120,8 +1121,12 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
>>
>>      if (virtio_gpu_virgl_enabled(g->conf)) {
>>          error_setg(&g->migration_blocker, "virgl is not yet migratable");
>> -        if (migrate_add_blocker(g->migration_blocker, errp) < 0) {
>> -            error_free(g->migration_blocker);
>> +        ret = migrate_add_blocker(g->migration_blocker, errp);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Cannot use virgl as it does not "
>> +                                  "support live migration yet");
>> +            }
>>              return;
>>          }
>>      }
>> diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
>> index 3614daa..b40ad5f 100644
>> --- a/hw/intc/arm_gic_kvm.c
>> +++ b/hw/intc/arm_gic_kvm.c
>> @@ -514,8 +514,11 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
>>          error_setg(&s->migration_blocker, "This operating system kernel does "
>>                                            "not support vGICv2 migration");
>>          ret = migrate_add_blocker(s->migration_blocker, errp);
>> -        if (ret < 0) {
>> -            error_free(s->migration_blocker);
>> +        if (ret) {
>> +            if (ret == -EACCES) {
>> +                error_append_hint(errp, "Failed to realize vGICv2 as its"
>> +                                  " migrataion is not implemented yet");
>> +            }
>>              return;
>>          }
>>      }
>
> So if you look at these diff hunks you can see that we effectively
> end up duplicating the "why doesn't this device let you migrate"
> user message -- once in the error_setg(&s->migration_blocker, ...)
> and once with the error_append_hint() call.

Maybe changing the error message in error_setg(&s->migration_blocker, ...) to:
"Trying to add migration blocker but will fail if --only-migratable is
specified"
or something like that do the trick?

>
> Is there some way we can get migrate_add_blocker() to automatically
> incorporate the message when it's failing for the only-migratable case?
> (It's already being passed the errp.)

Sorry! I am not sure how I can use that but if you have something more
I am ready to work on it to fix this.

> That would avoid this duplication of the messages, and should also
> mean we don't need to modify the callers at all.

Right. I also thought so while writing this patch but wasn't able to
come up with a solution.

Ashijeet
>
> If the error messages come out looking a bit strangely worded we
> could edit the text in the callers so it works in both situations
> when it will be used.
>
> thanks
> -- PMM
diff mbox

Patch

diff --git a/block/qcow.c b/block/qcow.c
index 11526a1..7e5003ac 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -253,8 +253,11 @@  static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
                "does not support live migration",
                bdrv_get_device_or_node_name(bs));
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        error_free(s->migration_blocker);
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use a node with qcow format as "
+                              "it does not support live migration");
+        }
         goto fail;
     }
 
diff --git a/block/vdi.c b/block/vdi.c
index 2b56f52..df45df4 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -472,8 +472,11 @@  static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
                "does not support live migration",
                bdrv_get_device_or_node_name(bs));
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        error_free(s->migration_blocker);
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use a node with vdi format as "
+                              "it does not support live migration");
+        }
         goto fail_free_bmap;
     }
 
diff --git a/block/vhdx.c b/block/vhdx.c
index d81941b..118134e 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -996,11 +996,13 @@  static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
                "does not support live migration",
                bdrv_get_device_or_node_name(bs));
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        error_free(s->migration_blocker);
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use a node with vhdx format as "
+                              "it does not support live migration");
+        }
         goto fail;
     }
-
     if (flags & BDRV_O_RDWR) {
         ret = vhdx_update_headers(bs, s, false, NULL);
         if (ret < 0) {
diff --git a/block/vmdk.c b/block/vmdk.c
index 4a45a83..cd6a641 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -977,8 +977,11 @@  static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
                "does not support live migration",
                bdrv_get_device_or_node_name(bs));
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        error_free(s->migration_blocker);
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use a node with vmdk format as "
+                              "it does not support live migration");
+        }
         goto fail;
     }
 
diff --git a/block/vpc.c b/block/vpc.c
index 299a8c8..c935962 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -427,8 +427,11 @@  static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
                "does not support live migration",
                bdrv_get_device_or_node_name(bs));
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        error_free(s->migration_blocker);
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use a node with vpc format as "
+                              "it does not support live migration");
+        }
         goto fail;
     }
 
diff --git a/block/vvfat.c b/block/vvfat.c
index 3de3320..f9a5f9b 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1192,8 +1192,11 @@  static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
                    "does not support live migration",
                    bdrv_get_device_or_node_name(bs));
         ret = migrate_add_blocker(s->migration_blocker, errp);
-        if (ret < 0) {
-            error_free(s->migration_blocker);
+        if (ret) {
+            if (ret == -EACCES) {
+                error_append_hint(errp, "Cannot use a node with vvfat format "
+                                  "as it does not support live migration");
+            }
             goto fail;
         }
     }
@@ -1202,7 +1205,6 @@  static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
         init_mbr(s, cyls, heads, secs);
     }
 
-    //    assert(is_consistent(s));
     qemu_co_mutex_init(&s->lock);
 
     ret = 0;
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index e72ef43..01dd67f 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -1020,12 +1020,17 @@  static void coroutine_fn v9fs_attach(void *opaque)
      */
     if (!s->migration_blocker) {
         int ret;
+        Error *local_err;
         s->root_fid = fid;
         error_setg(&s->migration_blocker,
                    "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'",
                    s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag);
-        ret = migrate_add_blocker(s->migration_blocker, NULL);
-        if (ret < 0) {
+        ret = migrate_add_blocker(s->migration_blocker, &local_err);
+        if (ret) {
+            if (ret == -EACCES) {
+                error_append_hint(&local_err, "Failed to mount VirtFS as it "
+                                  "does not support live migration");
+            }
             err = ret;
             clunk_fid(s, fid);
             goto out;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 6e60b8c..1b02f4b 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1102,6 +1102,7 @@  static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
     VirtIOGPU *g = VIRTIO_GPU(qdev);
     bool have_virgl;
     int i;
+    int ret;
 
     if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
         error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
@@ -1120,8 +1121,12 @@  static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
 
     if (virtio_gpu_virgl_enabled(g->conf)) {
         error_setg(&g->migration_blocker, "virgl is not yet migratable");
-        if (migrate_add_blocker(g->migration_blocker, errp) < 0) {
-            error_free(g->migration_blocker);
+        ret = migrate_add_blocker(g->migration_blocker, errp);
+        if (ret) {
+            if (ret == -EACCES) {
+                error_append_hint(errp, "Cannot use virgl as it does not "
+                                  "support live migration yet");
+            }
             return;
         }
     }
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index 3614daa..b40ad5f 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -514,8 +514,11 @@  static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
         error_setg(&s->migration_blocker, "This operating system kernel does "
                                           "not support vGICv2 migration");
         ret = migrate_add_blocker(s->migration_blocker, errp);
-        if (ret < 0) {
-            error_free(s->migration_blocker);
+        if (ret) {
+            if (ret == -EACCES) {
+                error_append_hint(errp, "Failed to realize vGICv2 as its"
+                                  " migrataion is not implemented yet");
+            }
             return;
         }
     }
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index 950a02f..3474642 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -70,7 +70,10 @@  static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
     error_setg(&s->migration_blocker, "vITS migration is not implemented");
     ret = migrate_add_blocker(s->migration_blocker, errp);
     if (ret < 0) {
-        error_free(s->migration_blocker);
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Failed to realize vITS as its migration "
+                              "is not implemented yet");
+        }
         return;
     }
 
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 06f6f30..be7b97c 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -118,7 +118,10 @@  static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
     error_setg(&s->migration_blocker, "vGICv3 migration is not implemented");
     ret = migrate_add_blocker(s->migration_blocker, errp);
     if (ret < 0) {
-        error_free(s->migration_blocker);
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Failed to realize vGICv3 as its migration"
+                              "is not implemented yet");
+        }
         return;
     }
 
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index 585cc5b..58f27b1 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -837,6 +837,7 @@  static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
 {
     IVShmemState *s = IVSHMEM_COMMON(dev);
     Error *err = NULL;
+    int ret;
     uint8_t *pci_conf;
     uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
         PCI_BASE_ADDRESS_MEM_PREFETCH;
@@ -910,8 +911,13 @@  static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
     if (!ivshmem_is_master(s)) {
         error_setg(&s->migration_blocker,
                    "Migration is disabled when using feature 'peer mode' in device 'ivshmem'");
-        if (migrate_add_blocker(s->migration_blocker, errp) < 0) {
-            error_free(s->migration_blocker);
+        ret = migrate_add_blocker(s->migration_blocker, errp);
+        if (ret) {
+            if (ret == -EACCES) {
+                error_append_hint(errp, "Cannot use the 'peer mode' feature "
+                                  "in device 'ivshmem' as it does not "
+                                  "support live migration");
+            }
             return;
         }
     }
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index ff503d0..0c571f8 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -244,8 +244,12 @@  static void vhost_scsi_realize(DeviceState *dev, Error **errp)
     error_setg(&s->migration_blocker,
                "vhost-scsi does not support migration");
     ret = migrate_add_blocker(s->migration_blocker, errp);
-    if (ret < 0) {
-        goto free_blocker;
+    if (ret) {
+        if (ret == -EACCES) {
+            error_append_hint(errp, "Cannot use vhost-scsi as it does not "
+                              "support live migration");
+        }
+        goto close_fd;
     }
 
     s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
@@ -272,8 +276,6 @@  static void vhost_scsi_realize(DeviceState *dev, Error **errp)
  free_vqs:
     migrate_del_blocker(s->migration_blocker);
     g_free(s->dev.vqs);
- free_blocker:
-    error_free(s->migration_blocker);
  close_fd:
     close(vhostfd);
     return;
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 416fada..1c4a4f9 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1159,9 +1159,11 @@  int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
     if (hdev->migration_blocker != NULL) {
         Error *local_err;
         r = migrate_add_blocker(hdev->migration_blocker, &local_err);
-        if (r < 0) {
-            error_report_err(local_err);
-            error_free(hdev->migration_blocker);
+        if (r) {
+            if (r == -EACCES) {
+                error_append_hint(&local_err, "Cannot use vhost drivers as "
+                                  "it does not support live migration");
+            }
             goto fail_busyloop;
         }
     }
diff --git a/migration/migration.c b/migration/migration.c
index 713e012..ab34328 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1115,9 +1115,16 @@  int migrate_add_blocker(Error *reason, Error **errp)
 {
     if (migration_is_idle(NULL)) {
         migration_blockers = g_slist_prepend(migration_blockers, reason);
+        error_free(reason);
         return 0;
     }
 
+    if (only_migratable) {
+        error_setg(errp, "Failed to add migration blocker: --only-migratable "
+                   "was specified");
+        return -EACCES;
+    }
+
     error_setg(errp, "Cannot block a migration already in-progress");
     return -EBUSY;
 }
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 6031a1c..05c8365 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -962,7 +962,11 @@  int kvm_arch_init_vcpu(CPUState *cs)
                    "State blocked by non-migratable CPU device"
                    " (invtsc flag)");
         r = migrate_add_blocker(invtsc_mig_blocker, NULL);
-        if (r < 0) {
+        if (r) {
+            if (r == -EACCES) {
+                error_report("kvm: non-migratable CPU device but"
+                        " --only-migratable was specified");
+            }
             error_report("kvm: migrate_add_blocker failed");
             goto efail;
         }
@@ -1009,7 +1013,6 @@  int kvm_arch_init_vcpu(CPUState *cs)
  fail:
     migrate_del_blocker(invtsc_mig_blocker);
  efail:
-    error_free(invtsc_mig_blocker);
     return r;
 }