}
+/**
+ * qemuBlockReopenSliceExpand:
+ * @vm: domain object
+ * @src: storage source to reopen
+ *
+ * Reopen @src image to remove its storage slice. Note that this currently
+ * works only for 'raw' disks.
+ *
+ * Note: This changes transforms the definition such that the 'raw' driver
+ * becomes the 'format' layer rather than the 'slice' layer, to be able
+ * to free the slice definition.
+ */
+int
+qemuBlockReopenSliceExpand(virDomainObj *vm,
+ virStorageSource *src)
+{
+ g_autoptr(virJSONValue) reopenoptions = virJSONValueNewArray();
+ g_autoptr(virJSONValue) srcprops = NULL;
+ int rc;
+
+ /* If we are lacking the object here, qemu might have opened an image with
+ * a node name unknown to us */
+ /* Note: This is currently dead code, as only 'raw' images are supported */
+ if (!src->backingStore) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("can't reopen image with unknown presence of backing store"));
+ return -1;
+ }
+
+ /* If there is an explicit storage slice 'raw' driver layer we need to modify that */
+ if (qemuBlockStorageSourceGetSliceNodename(src)) {
+ /* we need to know whether the slice layer is the "effective" layer */
+ bool isEffective = !qemuBlockStorageSourceGetSliceNodename(src);
+
+ if (!(srcprops = qemuBlockStorageSourceGetBlockdevStorageSliceProps(src, isEffective, true)))
+ return -1;
+ } else {
+ if (!(srcprops = qemuBlockStorageSourceGetFormatProps(src, src->backingStore)))
+ return -1;
+ }
+
+ if (virJSONValueArrayAppend(reopenoptions, &srcprops) < 0)
+ return -1;
+
+ if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_NONE) < 0)
+ return -1;
+
+ rc = qemuMonitorBlockdevReopen(qemuDomainGetMonitor(vm), &reopenoptions);
+
+ qemuDomainObjExitMonitor(vm);
+ if (rc < 0)
+ return -1;
+
+ /* transform the 'slice' raw driver into a 'format' driver so that we don't
+ * have to add extra code */
+ if (qemuBlockStorageSourceGetSliceNodename(src))
+ qemuBlockStorageSourceSetFormatNodename(src, g_strdup(qemuBlockStorageSourceGetSliceNodename(src)));
+
+ /* get rid of the slice */
+ g_clear_pointer(&src->sliceStorage, virStorageSourceSliceFree);
+
+ return 0;
+}
+
+
/**
* qemuBlockStorageSourceNeedSliceLayer:
* @src: source to inspect
g_autofree char *device = NULL;
const char *nodename = NULL;
virDomainDiskDef *disk = NULL;
+ virDomainDiskDef *persistDisk = NULL;
virCheckFlags(VIR_DOMAIN_BLOCK_RESIZE_BYTES |
VIR_DOMAIN_BLOCK_RESIZE_CAPACITY, -1);
goto endjob;
}
- if (flags & VIR_DOMAIN_BLOCK_RESIZE_CAPACITY) {
+ /* The physical capacity is needed both when automatic sizing is requested
+ * and when a slice is used on top of a block device.
+ */
+ if (virStorageSourceIsBlockLocal(disk->src) &&
+ ((flags & VIR_DOMAIN_BLOCK_RESIZE_CAPACITY) ||
+ disk->src->sliceStorage)) {
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
+ if (qemuDomainStorageUpdatePhysical(cfg, vm, disk->src) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("failed to update capacity of '%1$s'"), disk->src->path);
+ goto endjob;
+ }
+
+ }
+
+ if (flags & VIR_DOMAIN_BLOCK_RESIZE_CAPACITY) {
if (!qemuBlockStorageSourceIsRaw(disk->src) ||
!virStorageSourceIsBlockLocal(disk->src)) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
goto endjob;
}
- if (qemuDomainStorageUpdatePhysical(cfg, vm, disk->src) < 0) {
- virReportError(VIR_ERR_OPERATION_FAILED,
- _("failed to update capacity of '%1$s'"), disk->src->path);
- goto endjob;
- }
-
size = disk->src->physical;
}
disk->src->format == VIR_STORAGE_FILE_QED)
size = VIR_ROUND_UP(size, 512);
+ if (disk->src->sliceStorage) {
+ if (qemuDiskBusIsSD(disk->bus)) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("resize of a 'sd' disk with storage slice is not supported"));
+ goto endjob;
+ }
+
+ /* If the storage slice has a non-zero 'offset' it's usually some weird
+ * configuration that we'd rather not touch */
+ if (disk->src->sliceStorage->offset > 0) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("resize of a disk with storage slice with non-zero 'offset' is not supported"));
+ goto endjob;
+ }
+
+ /* Removing the slice for non-raw will require introducing an attribute that
+ * the slice was fully expanded so that the XML can keep the 'slice' element.
+ * For raw images we simply remove the slice definition as there is no
+ * extra layer. */
+ if (!qemuBlockStorageSourceIsRaw(disk->src)) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("resize of a disk with storage slice is supported only for 'raw' images"));
+ goto endjob;
+ }
+
+ /* trying to resize a block device to a size not equal to the actual
+ * size of the block device will cause qemu to fail */
+ if (virStorageSourceIsBlockLocal(disk->src) &&
+ disk->src->physical != size) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+ _("block device backed disk must be resized to its actual size '%1$llu'"),
+ disk->src->physical);
+ goto endjob;
+ }
+
+ if (vm->newDef &&
+ (persistDisk = virDomainDiskByTarget(vm->newDef, disk->dst))) {
+ if (!virStorageSourceIsSameLocation(disk->src, persistDisk->src) ||
+ !persistDisk->src->sliceStorage)
+ persistDisk = NULL;
+ }
+
+ /* remove the slice completely, we then instruct qemu to resize */
+ if (qemuBlockReopenSliceExpand(vm, disk->src) < 0)
+ goto endjob;
+
+ qemuDomainSaveStatus(vm);
+ }
+
if (!qemuDiskBusIsSD(disk->bus)) {
nodename = qemuBlockStorageSourceGetEffectiveNodename(disk->src);
} else {
}
qemuDomainObjExitMonitor(vm);
+ if (persistDisk) {
+ g_clear_pointer(&persistDisk->src->sliceStorage, virStorageSourceSliceFree);
+ qemuDomainSaveConfig(vm);
+ }
+
ret = 0;
endjob: