Implement support for raw device mappings (RDMs) in QEMU's VMDK block
driver. Handle both virtual and physical compatibility modes. This
allows us to import RDM disks from ESXi through our FUSE layer without
any additional changes inside Proxmox VE.
According to the vSphere docs, an RDM provides a mechanism for a VM to
have direct access to a LUN on physical storage [rdm]. What it really
does is simply pass through a block device to a VM. The VMDK for an
RDM will reference a so-called "pointer file" in its extent
description, with the type `VMFSRDM`, which is created alongide the
VMDK file. This pointer file then points to the block device, kind of
like a symlink.
- Physical compatibility mode
In the "Disk DescriptorFile" section:
`createType="vmfsPassthroughRawDeviceMap"`
This is the simplest of the two modes; the block device is simply
passed through to the VM. RDMs in this compat mode are excluded from
snapshots.
On ESXi, such RDMs can be created with:
vmkfstools --createrdmpassthru \
/vmfs/devices/disks/... [PATH TO NEW VMDK]
- Virtual compatibility mode
In the "Disk DescriptorFile" section:
`createType="vmfsRawDeviceMap"`
This mode allows RDMs to be included in snapshots. When making a
snapshot, the new delta is written to a SESPARSE file on the
datastore instead, and the data on the passthrough disk will remain
unmodified from that point onwards.
On ESXi, such RDMs can be created with:
vmkfstools --createrdm \
/vmfs/devices/disks/... [PATH TO NEW VMDK]
In order to support both of these modes, make the VMDK block driver
aware of the `vmfsPassthroughRawDeviceMap` and `vmfsRawDeviceMap`
createTypes. Then, handle the `VMFSRDM` extent type in the extents
parser.
The `VMFSRDM` extent type works similar to the regular `VMFS` and
`FLAT` types. The entire extent's description consists of four fields;
the referenced file points to a raw block device and thus stores no
metadata. Therefore, this file can be treated just like a `FLAT`
extent and read directly.
[rdm]:
https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/7-0/vsphere-storage/raw-device-mapping-in-vsphere.html
Signed-off-by: Max R. Carrara <[email protected]>
---
Hello!
Some context: Our FUSE layer in Proxmox VE allows us to mount the VMware
filesystem on ESXi directly on our side, which makes migrating VMs from
ESXi to PVE possible using our import tools. However, we currently
cannot import RDM disks, since QEMU's VMDK driver does not support them.
I was advised to upstream this here directly instead of patching our
downstream version. So, I'd appreciate any feedback! :)
How I tested this:
1. Create a physical and virtual RDM each on ESXi
2. Assign the RDMs to a plain Debian VM
3. Boot up the VM and write over the disks with /dev/urandom from inside
the VM
4. Run a `sha256sum` for each disk inside the VM and note down the
checksums
5. Configure the ESXi host on Proxmox VE
6. Import the Debian VM with the two RDM disks
7. Boot up the VM once the import has been completed (if doing a
non-live import) and run the checksums again
Alternatively, it is also possible to use our [tooling] directly; I can
provide the steps for that, if desired. That makes it possible to
convert images directly, run `qemu-img compare` w/ the mounted disks,
etc.
FWIW, I did that as well and got matching images.
Thanks a lot! :)
Best regards,
Max R. Carrara
[tooling]:
https://git.proxmox.com/?p=pve-esxi-import-tools.git;a=tree;h=refs/heads/master;hb=refs/heads/master
block/vmdk.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/block/vmdk.c b/block/vmdk.c
index
cd8b4ec7c88187188e79801bd71dd9da42223db0..22f67e351bee2582023653709fa8c4859081b4c4
100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1174,6 +1174,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState
*bs, QDict *options,
* RW [size in sectors] VMFS "file-name.vmdk"
* RW [size in sectors] VMFSSPARSE "file-name.vmdk"
* RW [size in sectors] SESPARSE "file-name.vmdk"
+ * RW [size in sectors] VMFSRDM "file-name.vmdk"
*/
flat_offset = -1;
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
@@ -1184,7 +1185,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState
*bs, QDict *options,
if (matches != 5 || flat_offset < 0) {
goto invalid;
}
- } else if (!strcmp(type, "VMFS")) {
+ } else if (!strcmp(type, "VMFS") || !strcmp(type, "VMFSRDM")) {
if (matches == 4) {
flat_offset = 0;
} else {
@@ -1197,7 +1198,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState
*bs, QDict *options,
if (sectors <= 0 ||
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") &&
- strcmp(type, "SESPARSE")) ||
+ strcmp(type, "SESPARSE") && strcmp(type, "VMFSRDM")) ||
(strcmp(access, "RW"))) {
continue;
}
@@ -1224,7 +1225,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState
*bs, QDict *options,
assert(ret < 32);
extent_role = BDRV_CHILD_DATA;
- if (strcmp(type, "FLAT") != 0 && strcmp(type, "VMFS") != 0) {
+ if (strcmp(type, "FLAT") != 0 && strcmp(type, "VMFS") != 0 &&
strcmp(type, "VMFSRDM")) {
/* non-flat extents have metadata */
extent_role |= BDRV_CHILD_METADATA;
}
@@ -1242,7 +1243,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState
*bs, QDict *options,
}
/* save to extents array */
- if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS")) {
+ if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS") || !strcmp(type,
"VMFSRDM")) {
/* FLAT extent */
ret = vmdk_add_extent(bs, extent_file, true, sectors,
@@ -1334,7 +1335,9 @@ vmdk_open_desc_file(BlockDriverState *bs, int flags, char
*buf, QDict *options,
strcmp(ct, "vmfsSparse") &&
strcmp(ct, "seSparse") &&
strcmp(ct, "twoGbMaxExtentSparse") &&
- strcmp(ct, "twoGbMaxExtentFlat")) {
+ strcmp(ct, "twoGbMaxExtentFlat") &&
+ strcmp(ct, "vmfsRawDeviceMap") &&
+ strcmp(ct, "vmfsPassthroughRawDeviceMap")) {
error_setg(errp, "Unsupported image type '%s'", ct);
ret = -ENOTSUP;
goto exit;
---
base-commit: c3d48b7d1e89604920e5b81b91140c2ad39a1943
change-id: 20260922-2026-09-support-rdm-import-from-esxi-v1-7f601c111381
--