叁·分

不求甚解

用户工具

站点工具


tech-notes:gpu-pv-hyperv

Hyper-V 使用 GPU-PV

施工中

参考

https://www.reddit.com/r/HyperV/comments/vph5lw/windows_server_2022_gpup_virtualization_working/

https://github.com/jamesstringerparsec/Easy-GPU-PV/

https://github.com/jamesstringerparsec/Easy-GPU-PV/issues/65

https://superuser.com/questions/647638/create-folder-on-system32-driverstore-filerepository

https://www.tenforums.com/virtualization/195745-tutorial-passing-through-gpu-hyper-v-guest-vm.html

add-gpu-pv.ps1
Param (
[string]$VMName = "TEST",
[string]$GPUName = "AUTO",
[string]$Hostname = $ENV:Computername,
[decimal]$GPUResourceAllocationPercentage = 100
)
 
Function Add-VMGpuPartitionAdapterFiles {
param(
[string]$hostname = $ENV:COMPUTERNAME,
[string]$DriveLetter,
[string]$GPUName
)
 
If (!($DriveLetter -like "*:*")) {
    $DriveLetter = $Driveletter + ":"
    }
 
If ($GPUName -eq "AUTO") {
    $PartitionableGPUList = Get-WmiObject -Class "Msvm_PartitionableGpu" -ComputerName $env:COMPUTERNAME -Namespace "ROOT\virtualization\v2"
    $DevicePathName = $PartitionableGPUList.Name | Select-Object -First 1
    $GPU = Get-PnpDevice | Where-Object {($_.DeviceID -like "*$($DevicePathName.Substring(8,16))*") -and ($_.Status -eq "OK")} | Select-Object -First 1
    $GPUName = $GPU.Friendlyname
    $GPUServiceName = $GPU.Service 
    }
Else {
    $GPU = Get-PnpDevice | Where-Object {($_.Name -eq "$GPUName") -and ($_.Status -eq "OK")} | Select-Object -First 1
    $GPUServiceName = $GPU.Service
    }
# Get Third Party drivers used, that are not provided by Microsoft and presumably included in the OS
 
Write-Host "INFO   : Finding and copying driver files for $GPUName to VM. This could take a while..."
 
$Drivers = Get-WmiObject Win32_PNPSignedDriver | where {$_.DeviceName -eq "$GPUName"}
 
New-Item -ItemType Directory -Path "$DriveLetter\windows\system32\HostDriverStore" -Force | Out-Null
 
#copy directory associated with sys file 
$servicePath = (Get-WmiObject Win32_SystemDriver | Where-Object {$_.Name -eq "$GPUServiceName"}).Pathname
                $ServiceDriverDir = $servicepath.split('\')[0..5] -join('\')
                $ServicedriverDest = ("$driveletter" + "\" + $($servicepath.split('\')[1..5] -join('\'))).Replace("DriverStore","HostDriverStore")
                if (!(Test-Path $ServicedriverDest)) {
                Copy-item -path "$ServiceDriverDir" -Destination "$ServicedriverDest" -Recurse
                }
 
# Initialize the list of detected driver packages as an array
$DriverFolders = @()
foreach ($d in $drivers) {
 
    $DriverFiles = @()
    $ModifiedDeviceID = $d.DeviceID -replace "\\", "\\"
    $Antecedent = "\\" + $hostname + "\ROOT\cimv2:Win32_PNPSignedDriver.DeviceID=""$ModifiedDeviceID"""
    $DriverFiles += Get-WmiObject Win32_PNPSignedDriverCIMDataFile | where {$_.Antecedent -eq $Antecedent}
    $DriverName = $d.DeviceName
    $DriverID = $d.DeviceID
    if ($DriverName -like "NVIDIA*") {
        New-Item -ItemType Directory -Path "$driveletter\Windows\System32\drivers\Nvidia Corporation\" -Force | Out-Null
        }
    foreach ($i in $DriverFiles) {
            $path = $i.Dependent.Split("=")[1] -replace '\\\\', '\'
            $path2 = $path.Substring(1,$path.Length-2)
            $InfItem = Get-Item -Path $path2
            $Version = $InfItem.VersionInfo.FileVersion
            If ($path2 -like "c:\windows\system32\driverstore\*") {
                $DriverDir = $path2.split('\')[0..5] -join('\')
                $driverDest = ("$driveletter" + "\" + $($path2.split('\')[1..5] -join('\'))).Replace("driverstore","HostDriverStore")
                if (!(Test-Path $driverDest)) {
                Copy-item -path "$DriverDir" -Destination "$driverDest" -Recurse
                }
            }
            Else {
                $ParseDestination = $path2.Replace("c:", "$driveletter")
                $Destination = $ParseDestination.Substring(0, $ParseDestination.LastIndexOf('\'))
                if (!$(Test-Path -Path $Destination)) {
                    New-Item -ItemType Directory -Path $Destination -Force | Out-Null
                    }
                Copy-Item $path2 -Destination $Destination -Force
 
            }
 
    }
    }
 
}
 
$VM = Get-VM -VMName $VMName
$VHD = Get-VHD -VMId $VM.VMId
 
If ($VM.state -eq "Running") {
    [bool]$state_was_running = $true
    }
 
if ($VM.state -ne "Off"){
    "Attemping to shutdown VM..."
    Stop-VM -Name $VMName -Force
    } 
 
While ($VM.State -ne "Off") {
    Start-Sleep -s 3
    "Waiting for VM to shutdown - make sure there are no unsaved documents..."
    }
 
"Mounting Drive..."
$DriveLetter = (Mount-VHD -Path $VHD.Path -PassThru | Get-Disk | Get-Partition | Get-Volume | Where-Object {$_.DriveLetter} | ForEach-Object DriveLetter)
 
"Copying GPU Files - this could take a while..."
Add-VMGPUPartitionAdapterFiles -hostname $Hostname -DriveLetter $DriveLetter -GPUName $GPUName
 
"Dismounting Drive..."
Dismount-VHD -Path $VHD.Path
 
function Assign-VMGPUPartitionAdapter 
{  
    $PartitionableGPUList = Get-WmiObject -Class "Msvm_PartitionableGpu" -ComputerName $env:COMPUTERNAME -Namespace "ROOT\virtualization\v2" 
    if ($GPUName -eq "AUTO") {
        $DevicePathName = $PartitionableGPUList.Name[0]
        Add-VMGpuPartitionAdapter -VMName $VMName
        }
    else {
        $DeviceID = ((Get-WmiObject Win32_PNPSignedDriver | where {($_.Devicename -eq "$GPUNAME")}).hardwareid).split('\')[1]
        $DevicePathName = ($PartitionableGPUList | Where-Object name -like "*$deviceid*").Name
        Add-VMGpuPartitionAdapter -VMName $VMName -InstancePath $DevicePathName
        }
 
    [float]$devider = [math]::round($(100 / $GPUResourceAllocationPercentage),2)
 
    Set-VMGpuPartitionAdapter -VMName $VMName -MinPartitionVRAM ([math]::round($(1000000000 / $devider))) -MaxPartitionVRAM ([math]::round($(1000000000 / $devider))) -OptimalPartitionVRAM ([math]::round($(1000000000 / $devider)))
    Set-VMGPUPartitionAdapter -VMName $VMName -MinPartitionEncode ([math]::round($(18446744073709551615 / $devider))) -MaxPartitionEncode ([math]::round($(18446744073709551615 / $devider))) -OptimalPartitionEncode ([math]::round($(18446744073709551615 / $devider)))
    Set-VMGpuPartitionAdapter -VMName $VMName -MinPartitionDecode ([math]::round($(1000000000 / $devider))) -MaxPartitionDecode ([math]::round($(1000000000 / $devider))) -OptimalPartitionDecode ([math]::round($(1000000000 / $devider)))
    Set-VMGpuPartitionAdapter -VMName $VMName -MinPartitionCompute ([math]::round($(1000000000 / $devider))) -MaxPartitionCompute ([math]::round($(1000000000 / $devider))) -OptimalPartitionCompute ([math]::round($(1000000000 / $devider)))
 
}
 
"Assigning GPU-P Adapter"
Assign-VMGPUPartitionAdapter
 
If ($state_was_running){
    "Previous State was running so starting VM..."
    Start-VM $VMName
    }
 
"Done..."

Hyper-V Guide Install windows server 22 host, and update to latest build, install Hyper-V Role. (21H2, OS Build: 20348.803 or later / Cumulative update KB5014665)

We'll be using Jamesstringerparsec Easy-GPU-PV's guide with a few tweaks, so download and extract the package to a folder. GitHub - jamesstringerparsec/Easy-GPU-PV: A Project dedicated to making GPU Partitioning on Windows easier!

Run powershell ISE as an admin and set Execution policy to unrestricted Set-ExecutionPolicy -ExecutionPolicy Unrestricted -force

Once done, go to File > Open > and open CopyFilesToVM.ps1

Firstly, modify the script to add your server info. Take a look at the Github values to understand what each settings does and which should not be changed. The settings are made with Win10/11 in mind. Below are the settings that must be changed to work with WinServer:

Edition = to find out the number for your Windows edition, open a separate powershell window and run the following script after mounting your Server ISO to a drive letter: dism /get-wiminfo /wimfile:<Drive Letter>:\\sources\\install.wim (this can also be done with native POSH cmdlets, Black V using Get-WindowsImage -ImagePath F:\sources\install.win) Once you run the command there will be an index number for every windows version available. Choose your specific version and set that number as the Edition Value.

GPUName = Leave as auto if the GPU you are virtualising is the only one available. (Personally I'd manually name it saves issues later on, Black V)

Once this is done, run the script. It should go through the creating on the VM, but will fail when trying to launch it. In my case, the error was somewhere along the lines of:

GPU Partition (Instance ID 69E7E377-A066-41D6-A03B-E610AC30DAA6): Failed to Power on with Error

'.'.Insufficient system resources exist to complete the requested service

'WS22PLEX' failed to start. (Virtual machine ID CAED9896-6895-4541-AC43-B25856D3B385)

'WS22PLEX' GPU Partition (Instance ID 69E7E377-A066-41D6-A03B-E610AC30DAA6): Failed to Power on with Error

'Insufficient system resources exist to complete the requested service.' (0x800705AA). (Virtual machine ID CAED9896-6895-4541-AC43-B25856D3B385)

Could not allocate a GPU partition as no GPU devices are compliant with currently set group policy. See HyperV\RequireSecureDeviceAssignment and HyperV\RequireSupportedDeviceAssignment for more details.

To get around this, you need to create 2 keys in Reg edit.

HKLM:\SOFTWARE\Policies\Microsoft\Windows\HyperV“ -Name “RequireSecureDeviceAssignment” -Type DWORD -Value 0

HKLM:\SOFTWARE\Policies\Microsoft\Windows\HyperV” -Name “RequireSupportedDeviceAssignment” -Type DWORD -Value 0

I closed off MMC.exe from Task Manager before attempting to run the VM again. Try to launch the VM again by running the following command in Powershell: Start-VM Server123 (Replace “Server123” with your recently created guest server name).

Your server should now boot up with no error. I installed drivers in the guest and tested by running a 4K youtube video, and watched the resource spike up on the host machine's task manager.

Notes:

Make sure that you install GPU drivers on the host machine before doing any of the above.

Make sure to install GPU drivers on the guest VM when after creation and booting into it.

Normally, GPU resources are not visible in the guest VM.

Check Device manager to make sure the GPU shows up and is enabled. If it isn't, you need to extract the drivers from the host machine and re-assign them to the VM.

See the github link

Normal NTFS Permisions on folder C:\Windows\System32\DriverStore\FileRepository\ are System - Full Control, and Everyone - Read & Execute.

I you really want to copy into this directory you can do the following:

Right click the folder, click Properties Click the Security tab Click the Edit button. Now add your account and give it Full Control Click Ok Click Yes at the “Windows Security”-prompt At the “Error Applying Security”-prompt click Continue once and Cancel at the next Click Ok at the “Windows Security”-prompt You can now copy files/folder into this directory.

To get control over all the other directories you could “Get Ownership” of this folder but that's not recommended. (you already have read access of those)

Solutions for existing VM

Steps:

Test to see if your GPU can be partitioned at all. On the Host, open a Powershell prompt as administrator. Then run: Get-VMPartitionableGpu (win10) or Get-VMHostPartitionableGpu (win11)

Open up Device Manager on the guest VM, and check the Display Adapters. You will see that your GPU is NOT enabled. Then shut down the VM.

Go to this link: GitHub - jamesstringerparsec/Easy-GPU-PV: A Project dedicated to making GPU Partitioning on Windows easier! You can download the full repo if you want. But you only need these two files: Add-VMGpuPartitionAdapterFiles.psm1 Update-VMGpuPartitionDriver.ps1

From the admin powershell console, run this command: .\Update-VMGpuPartitionDriver.ps1 -VMName “Name of your VM” -GPUName “AUTO” Just edit that command with the name or your VM. GPU “AUTO” will automatically determine your GPU. These scripts will find all the driver files from your host machine, and copy the files to the VM. This can take some time.

With the VM still off, create a new .ps1 powershell file on the host, and paste in this code: Code:

$vm = "Name of your VM"
if (Get-VMGpuPartitionAdapter -VMName $vm -ErrorAction SilentlyContinue) {
   Remove-VMGpuPartitionAdapter -VMName $vm
}
Set-VM -GuestControlledCacheTypes $true -VMName $vm
Set-VM -LowMemoryMappedIoSpace 1Gb -VMName $vm
Set-VM -HighMemoryMappedIoSpace 32Gb -VMName $vm
Add-VMGpuPartitionAdapter -VMName $vm

This script will enable the GPU partitioning for your VM, and turn on some required settings.

Edit the first line and again put the name of your VM. Then run this script file in your powershell prompt by preceeding the filename with .\ just like you did with the previous script above.

Now we should have the drivers copied into the VM, and the GPU partitioning feature enabled. You can now turn on the VM, and go back to Device Manager and see if your GPU is now shown under Display Adapters

tech-notes/gpu-pv-hyperv.txt · 最后更改: 2024/01/27 11:02 由 Librarian