Skip to content

Commit 13a4950

Browse files
committed
linstor: add QemuImg 3-param constructor for skip-zero copy optimization
Add QemuImg(timeout, skipZeroIfSupported, noCache) constructor that detects --target-is-zero support in qemu-img and uses it during convert to skip writing zeroes on pre-zeroed block devices. This significantly improves copy performance on thin-provisioned LINSTOR volumes. Update LinstorStorageAdaptor.copyPhysicalDisk and LinstorRevertBackupSnapshotCommandWrapper to use the new constructor with zeroedDevice detection via LinstorUtil.resourceSupportZeroBlocks.
1 parent 0142594 commit 13a4950

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ public class QemuImg {
3838
public final static String FILE_FORMAT = "file_format";
3939
public final static String IMAGE = "image";
4040
public final static String VIRTUAL_SIZE = "virtual_size";
41+
public static final String TARGET_ZERO_FLAG = "--target-is-zero";
4142

4243
/* The qemu-img binary. We expect this to be in $PATH */
4344
public String _qemuImgPath = "qemu-img";
4445
private String cloudQemuImgPath = "cloud-qemu-img";
4546
private int timeout;
47+
private boolean skipZero = false;
48+
private boolean noCache = false;
4649

4750
private String getQemuImgPathScript = String.format("which %s >& /dev/null; " +
4851
"if [ $? -gt 0 ]; then echo \"%s\"; else echo \"%s\"; fi",
@@ -97,6 +100,38 @@ public QemuImg(final int timeout) {
97100
this.timeout = timeout;
98101
}
99102

103+
/**
104+
* Create a QemuImg object that supports skipping target zeroes.
105+
* We detect this support via qemu-img help since support can
106+
* be backported rather than found in a specific version.
107+
*
108+
* @param timeout script timeout, default 0
109+
* @param skipZeroIfSupported Don't write zeroes to target device during convert, if supported by qemu-img
110+
* @param noCache Ensure we flush writes to target disk (useful for block device targets)
111+
*/
112+
public QemuImg(final int timeout, final boolean skipZeroIfSupported, final boolean noCache) {
113+
if (skipZeroIfSupported) {
114+
final Script s = new Script(_qemuImgPath, timeout);
115+
s.add("--help");
116+
117+
final OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
118+
final String result = s.execute(parser);
119+
120+
// Older Qemu returns output in result due to --help reporting error status
121+
if (result != null) {
122+
if (result.contains(TARGET_ZERO_FLAG)) {
123+
this.skipZero = true;
124+
}
125+
} else {
126+
if (parser.getLines().contains(TARGET_ZERO_FLAG)) {
127+
this.skipZero = true;
128+
}
129+
}
130+
}
131+
this.timeout = timeout;
132+
this.noCache = noCache;
133+
}
134+
100135
public void setTimeout(final int timeout) {
101136
this.timeout = timeout;
102137
}
@@ -254,6 +289,16 @@ public void convert(final QemuImgFile srcFile, final QemuImgFile destFile,
254289
}
255290

256291
script.add("convert");
292+
293+
if (skipZero) {
294+
script.add("-n");
295+
script.add(TARGET_ZERO_FLAG);
296+
script.add("-W");
297+
// with target-is-zero we skip zeros in 1M chunks for compatibility
298+
script.add("-S");
299+
script.add("1M");
300+
}
301+
257302
Long version = LibvirtConnection.getConnection().getVersion();
258303
if (version >= 2010000) {
259304
script.add("-U");
@@ -281,6 +326,11 @@ public void convert(final QemuImgFile srcFile, final QemuImgFile destFile,
281326

282327
addSnapshotToConvertCommand(srcFile.getFormat().toString(), snapshotName, forceSourceFormat, script, version);
283328

329+
if (noCache) {
330+
script.add("-t");
331+
script.add("none");
332+
}
333+
284334
script.add(srcFile.getFileName());
285335
script.add(destFile.getFileName());
286336

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private void convertQCow2ToRAW(
6060
blkDiscardScript.add(dstPath);
6161
blkDiscardScript.execute();
6262
}
63-
final QemuImg qemu = new QemuImg(waitMilliSeconds);
63+
final QemuImg qemu = new QemuImg(waitMilliSeconds, zeroedDevice, true);
6464
final QemuImgFile dstFile = new QemuImgFile(dstPath, QemuImg.PhysicalDiskFormat.RAW);
6565
qemu.convert(srcQemuFile, dstFile);
6666
}

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,6 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
619619
s_logger.debug(String.format("Linstor.copyPhysicalDisk: %s -> %s", disk.getPath(), name));
620620
final QemuImg.PhysicalDiskFormat sourceFormat = disk.getFormat();
621621
final String sourcePath = disk.getPath();
622-
final QemuImg qemu = new QemuImg(timeout);
623622

624623
final QemuImgFile srcFile = new QemuImgFile(sourcePath, sourceFormat);
625624

@@ -634,7 +633,9 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
634633
destFile.setFormat(dstDisk.getFormat());
635634
destFile.setSize(disk.getVirtualSize());
636635

636+
boolean zeroedDevice = LinstorUtil.resourceSupportZeroBlocks(destPools, getLinstorRscName(name));
637637
try {
638+
final QemuImg qemu = new QemuImg(timeout, zeroedDevice, true);
638639
qemu.convert(srcFile, destFile);
639640
} catch (QemuImgException | LibvirtException e) {
640641
s_logger.error(e);

0 commit comments

Comments
 (0)