Skip to content

Instantly share code, notes, and snippets.

@vroad
Last active May 16, 2022 08:05
Show Gist options
  • Save vroad/14dc3ce5830df3228d1e3e0d9c73f5ac to your computer and use it in GitHub Desktop.
Save vroad/14dc3ce5830df3228d1e3e0d9c73f5ac to your computer and use it in GitHub Desktop.
Single partition passthrough with mdadm hack

WARNING!! You may lose all of your data stored in the partition if you don't follow these steps correctly. Backup important data and use at your own risk. I strongly recommend you to start with an empty partition, or a loopback device backed by an empty image file.

This script will allow you to pass existing NTFS partition to windows running on KVM, which is also readable by bare-metal windows. You can't simply pass existing NTFS partition to the VM since guest OS see the partion as a whole disk device with a partiton table. The trick used here is sandwiching the partition with 2 loopback devices using software RAID (MD) device built with mdadm --build --level=linear. First device stores main partition table in the beginning of disk, and second one stores backup table in the end of disk. This idea is originally posted by a redditor ws-ilazki, I just converted it to the script: https://www.reddit.com/r/VFIO/comments/j443ad/pass_through_a_partition/g7hn38z/

Trim from guest OS seems to be not possible. If you want to run trim, just stop the VM and run from host.

At least on my environment, "${md_device_name}"-gpt-a.img has to be 1MB for the partition to be read from correct offset, probably because the partion alignment is 1MB. "${md_device_name}"-gpt-a.img can be smaller but should be at least 512KB, the chunk size used in mdadm --build. Using a image size smaller than this seems to cause the software RAID device to skip loopback devices without warning, which in turn corrupts your data in the partition.

Steps to create new MD device (Replace <MD_DEVICE_NAME> and <PARTITION_UUID> with the value you want):

  1. If you haven't done so, turn off fast startup in both bare-metal windows and VM windows.
  2. Run ./init-software-raid.sh <MD_DEVICE_NAME> to create empty disk images which will be used for loopback devices.
  3. Add start-software-raid.sh and relevant configuration to your NixOS configuration to automatically build MD deivce at startup.
  4. Open gparted
  5. Create a gpt partition table in the MD device.
  6. Create an unformatted partition that spans whole MD disk. The free space preceeding must be 0, the size must match with original partition, and flags must be msftdata. Don't choose NTFS here, that will re-format your partition.
  7. Apply all operations in gparted. If you did everything right NTFS partition shows up after applying.
  8. You can optionally run ntfsfix -n to test that the partition is not corrupted.
  9. Edit your VM configuration to add new MD device to the VM.
{ pkgs, ... }:
{
systemd.services.single-partition-passthrough = {
wantedBy = [ "multi-user.target" ];
description = "software RAID for single partition passthrough";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
restartIfChanged = false;
path = with pkgs; [ util-linux mdadm ];
script = builtins.readFile ./start-software-raid.sh;
scriptArgs = "<MD_DEvICE_NAME> /dev/disk/by-partuuid/<PARTITION_UUID>";
};
}
#!/usr/bin/env bash
set -euo pipefail
md_device_name="$1"
mkdir -p /var/lib/libvirt/extra-images/
dd conv=excl if=/dev/zero of=/var/lib/libvirt/extra-images/"${md_device_name}"-gpt-a.img bs=1024K count=1
# dd conv=excl if=/dev/zero of=/var/lib/libvirt/extra-images/"${md_device_name}".img count=256 bs=1M
dd conv=excl if=/dev/zero of=/var/lib/libvirt/extra-images/"${md_device_name}"-gpt-b.img bs=512K count=1
#!/usr/bin/env bash
set -euo pipefail
md_device_name="$1"
partition_device="$2"
md_device_path="/dev/md/${md_device_name}"
gpt_a_device="$(losetup --nooverlap --show --find /var/lib/libvirt/extra-images/"${md_device_name}"-gpt-a.img)"
# partition_device="$(losetup --nooverlap --show --find /var/lib/libvirt/extra-images/"${md_device_name}".img)"
gpt_b_device="$(losetup --nooverlap --show --find /var/lib/libvirt/extra-images/"${md_device_name}"-gpt-b.img)"
echo "gpt_a_device: ${gpt_a_device}"
echo "partition_device: ${partition_device}"
echo "gpt_b_device: ${gpt_b_device}"
mdadm --build "${md_device_path}" --chunk=512 --level=linear --raid-devices=3 "${gpt_a_device}" "${partition_device}" "${gpt_b_device}"
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='none' io='native'/>
<source dev='/dev/md/<MD_DEVICE_NAME>'/>
<target dev='vdb' bus='scsi'/>
</disk>
<controller type='scsi' index='0' model='virtio-scsi'/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment