起因背景
我们在使用NVIDIA vGPU切分显卡资源给虚拟机后,这台虚拟机就会存在2个虚拟屏幕,一个是PVE的VNC窗口,一个是NVIDIA VGX虚拟屏幕,当我们在PVE的VNC窗口操作虚拟机的话,部分应用又无法调用到GPU资源,把VNC窗口设置“无”即NONE的话只能借助外部远程工具连接虚拟机,PVE的VNC窗口又不工作。
所以这里参考佛西:为vgpu设备添加mdev gpu类型的补丁给PVE9打上。让VNC窗口直接输出vGPU显卡画面。
支持类型
| 说明 | 版本 |
|---|---|
| 系统 | PVE 9.1.6 |
| 内核 | Linux pve9 6.17.13-2-pve |
| pve-qemu-kvm | 10.0.2-7 |
| 支持虚拟机类型 | SeaBIOS/OVMF |
| 正常工作版本 | vGPU11/16/17/18 |
| 不工作版本 | vGPU19 |
修改QemuServer.pm
可以使用nano命令来编辑QemuServer.pm文件nano /usr/share/perl5/PVE/QemuServer.pm
1)第一处,在这句结尾加入mdev参数
enum => [
- qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)
],修改完成后效果:
enum => [
+ qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware mdev)
],2)第二处,在这句结尾加入'mdev' => 'mdev',参数
my $vga_map = {
'cirrus' => 'cirrus-vga',
'std' => 'VGA',
'vmware' => 'vmware-svga',
'virtio' => 'virtio-vga',
'virtio-gl' => 'virtio-vga-gl',
};修改完成后效果:
my $vga_map = {
'cirrus' => 'cirrus-vga',
'std' => 'VGA',
'vmware' => 'vmware-svga',
'virtio' => 'virtio-vga',
'virtio-gl' => 'virtio-vga-gl',
+ 'mdev' => 'mdev',
};3)第三处,在这句中加入if ($vga->{type} ne 'mdev'){ if判断语句,别忘了后面还有个括号}参数
push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none') {
push @$devices, '-device',
print_vga_device($conf, $vga, $arch, $machine_version, undef, $qxlnum, $bridges);
push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type} eq 'virtio-gl'; # VIRGL修改完成后效果:
push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none') {
+ if ($vga->{type} ne 'mdev'){
push @$devices, '-device',
print_vga_device($conf, $vga, $arch, $machine_version, undef, $qxlnum, $bridges);
+ }
push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type} eq 'virtio-gl'; # VIRGL4)第四处,在这句结尾加入|mdev参数,让Mdev显示器支持spcie协议,如果要用SPICE协议云桌面办公的建议加上(可选)
my $spice_port;
assert_clipboard_config($vga);
- my $is_spice = $qxlnum || $vga->{type} =~ /^virtio/;修改完成后效果:
my $spice_port;
assert_clipboard_config($vga);
+ my $is_spice = $qxlnum || $vga->{type} =~ /^(virtio|mdev)/;修改PCI.pm
可以使用nano命令来编辑PCI.pm文件nano /usr/share/perl5/PVE/QemuServer/PCI.pm
1)第一处,在这2句中间加入mdev类型的判断处理
my $mf_addr = $multifunction ? ".$j" : '';
$devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
if ($j == 0) {
$devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
$devicestr .= "$xvga";修改完成后效果:
my $mf_addr = $multifunction ? ".$j" : '';
$devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
+ my $mdevtype = $d->{mdev} // undef;
+ if ($mdevtype =~ /^(.*?)-/) {
+ $mdevtype = $1;
+ }
if ($j == 0) {
$devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
$devicestr .= "$xvga";2)第二处,在这2句中间加入mdev类型的判断处理
if ($j == 0) {
$devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
$devicestr .= "$xvga";
$devicestr .= ",multifunction=on" if $multifunction;
$devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
$devicestr .= ",bootindex=$bootorder->{$id}" if $bootorder->{$id};
for my $option (qw(vendor-id device-id sub-vendor-id sub-device-id)) {
$devicestr .= ",x-pci-$option=$d->{$option}" if $d->{$option};
}
}
push @$devices, '-device', $devicestr;
last if $d->{mdev};
}
}修改完成后效果:
if ($j == 0) {
$devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
$devicestr .= "$xvga";
$devicestr .= ",multifunction=on" if $multifunction;
$devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
$devicestr .= ",bootindex=$bootorder->{$id}" if $bootorder->{$id};
for my $option (qw(vendor-id device-id sub-vendor-id sub-device-id)) {
$devicestr .= ",x-pci-$option=$d->{$option}" if $d->{$option};
}
}
+ if ($mdevtype && $vga->{type} eq 'mdev'){
+ $devicestr .= ",display=on,ramfb=on,driver=vfio-pci-nohotplug";
+ }
push @$devices, '-device', $devicestr;
last if $d->{mdev};
}
}修改pvemanagerlib.js
可以使用nano命令来编辑pvemanagerlib.js文件nano /usr/share/pve-manager/js/pvemanagerlib.js
修改前端文件,在这句结尾加入mdev: 'NVIDIA vGPU',参数
std: gettext('Standard VGA'),
vmware: gettext('VMware compatible'),
qxl: 'SPICE',
qxl2: 'SPICE dual monitor',
qxl3: 'SPICE three monitors',
qxl4: 'SPICE four monitors',
serial0: gettext('Serial terminal') + ' 0',
serial1: gettext('Serial terminal') + ' 1',
serial2: gettext('Serial terminal') + ' 2',
serial3: gettext('Serial terminal') + ' 3',
virtio: 'VirtIO-GPU',
'virtio-gl': 'VirGL GPU',
+ mdev: 'NVIDIA vGPU',
none: Proxmox.Utils.noneText,
},重启服务让其生效,重启完记得Ctrl+F5清理下浏览器缓存。
systemctl restart {pvedaemon,pveproxy}最后就是给虚拟机添加上vGPU设备了,然后在显示那选择NVIDIA vGPU显示就能正常输入vGPU画面了。只测试了vGPU11 16 17 18的驱动版本可以正常显示,vGPU19 黑屏异常可能驱动版本过高导致。
一键修补脚本
懒人专属,附上个一键脚本进行修补,仅在PVE9.1.6版本测试通过,其他版本自测或者手动替换。
使用方法:
# 使用nano命令新建一个patch-mdev.sh文件
nano patch-mdev.sh
# 粘贴下边的脚本
# 添加可执行权限
chmod +x patch-mdev.sh
# 运行脚本
./patch-mdev.sh以下是脚本内容:
#!/bin/bash
set -e
echo "===== PVE mdev Patch====="
backup_file() {
local file=$1
if ls ${file}.bak.* 1>/dev/null 2>&1; then
echo " ↪ 已存在备份 $file"
else
cp "$file" "${file}.bak.$(date +%F-%H%M%S)"
echo "[备份] $file"
fi
}
patch_qemuserver() {
local file="/usr/share/perl5/PVE/QemuServer.pm"
backup_file "$file"
echo "[处理] QemuServer.pm"
# 1️⃣ 修改第一处
if ! grep -q "vmware mdev)" "$file"; then
sed -i "s/qw(\(.*vmware\))/qw(\1 mdev)/" "$file"
echo " ✔ 已修补完成"
else
echo " ↪ 已存在,跳过"
fi
# 2️⃣ 修改第二处
if ! grep -q "'mdev' => 'mdev'" "$file"; then
sed -i "/'virtio-gl' => 'virtio-vga-gl',/a\ 'mdev' => 'mdev'," "$file"
echo " ✔ 已修补完成"
else
echo " ↪ 已存在,跳过"
fi
# 3️⃣ 修改第三处
if ! grep -q "if (\$vga->{type} ne 'mdev')" "$file"; then
echo " ✔ 已修补完成"
awk '
{
if ($0 ~ /push @\$devices, '\''-device'\''/ && !done) {
getline nextline
if (nextline ~ /print_vga_device/) {
print " if ($vga->{type} ne '\''mdev'\''){"
print " push @$devices, '\''-device'\'',"
print "" nextline
print " }"
done=1
next
} else {
print $0
print nextline
next
}
}
print $0
}
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
else
echo " ↪ 已存在,跳过"
fi
# 4️⃣ 修改第四处
if ! grep -q "virtio|mdev" "$file"; then
echo " ✔ 已修补完成"
sed -i '/my \$is_spice = \$qxlnum || \$vga->{type} =~ \/\^virtio\//c\ my $is_spice = $qxlnum || $vga->{type} =~ /^(virtio|mdev)/;' "$file"
else
echo " ↪ 已存在,跳过"
fi
}
patch_pci() {
local file="/usr/share/perl5/PVE/QemuServer/PCI.pm"
backup_file "$file"
echo "[处理] PCI.pm"
# 1️⃣ 修改第一处
if ! grep -q "mdevtype" "$file"; then
sed -i "/id=\${id}/a\ my \$mdevtype = \$d->{mdev} \/\/ undef;\n if (\$mdevtype =~ \/^(.*?)-\/) {\n \$mdevtype = \$1;\n }" "$file"
echo " ✔ 已修补完成"
else
echo " ↪ 已存在,跳过"
fi
# 2️⃣ 修改第二处
if ! grep -q "vfio-pci-nohotplug" "$file"; then
awk '
{
print
if ($0 ~ /if \(\$j == 0\)/) {
in_block=1
brace=0
}
if (in_block) {
brace += gsub(/{/, "{")
brace -= gsub(/}/, "}")
if (brace == 0) {
print " if ($mdevtype && $vga->{type} eq '\''mdev'\''){"
print " $devicestr .= \",display=on,ramfb=on,driver=vfio-pci-nohotplug\";"
print " }"
in_block=0
}
}
}
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
echo " ✔ 已修补完成"
else
echo " ↪ 已存在,跳过"
fi
}
patch_ui() {
local file="/usr/share/pve-manager/js/pvemanagerlib.js"
backup_file "$file"
echo "[处理] 前端UI"
if ! grep -q "mdev: 'NVIDIA vGPU'" "$file"; then
sed -i "/'virtio-gl': 'VirGL GPU',/a\ mdev: 'NVIDIA vGPU'," "$file"
echo " ✔ 已修补完成"
else
echo " ↪ 已存在,跳过"
fi
}
restart_services() {
echo "[重启服务]"
systemctl restart {pvedaemon,pveproxy}
}
# ===== 执行 =====
patch_qemuserver
patch_pci
patch_ui
restart_services
echo ""
echo "===== ✅ 完成 ====="
echo "请 Ctrl+F5 刷新浏览器"
6 条评论
能提高web的帧率吗?
只是用来输出vGPU画面
改完了..只有第三处找不到..然后用脚本跑了一次..结果只要是选vGPU就无法开机(ó﹏ò。)
不知道跟我显卡是1060有没有关系
不支持的型号可能会遇到卡主或者花屏和黑屏情况
第3处必须修补,否则无法识别mdev类型,用脚本跑请在PVE9.1版本中尝试,同时最好使用原文件来跑,手动修改到一半再用脚本跑可能有问题