Files
PVE-Tools-9/src/modules/04-gpu-passthrough/controller.sh
Maple 886e82f0cb refactor: 模块化重构 — 拆分 lib/ 基础设施层与 src/modules/ 功能模块
PVE-Tools.sh 从 ~14,700 行单文件重构为 150 行入口点,
所有函数按职责拆分到独立文件,通过 source 机制加载。

lib/ — 基础设施层(零业务逻辑)
  config.sh: 全局变量与常量
  core.sh: 日志、UI、确认、备份、GRUB、进度条
  network.sh: 网络检测、镜像选择
  runtime.sh: 运行时守卫、main() 入口

src/modules/ — 功能模块(10 个目录)
  01-optimization  02-sources  03-boot-kernel
  04-gpu-passthrough  05-vm-container  06-networking
  07-storage-disk  08-tools-about  09-security  10-third-party

新增 build.sh(合并编译)和 dev.sh(开发 source 入口),
release.yml 新增构建步骤,CI 适配模块化结构。
2026-07-08 11:15:59 +08:00

293 lines
9.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# SPDX-License-Identifier: GPL-3.0-only
# Copyright (C) 2026 Ciriu Networks
qm_is_q35_machine() {
local vmid="$1"
local machine
machine="$(qm config "$vmid" 2>/dev/null | awk -F': ' '/^machine:/{print $2}' | head -n 1)"
if echo "$machine" | grep -q 'q35'; then
return 0
fi
return 1
}
# 获取可用的 hostpci 插槽号0-15
qm_find_free_hostpci_index() {
local vmid="$1"
local cfg used
cfg="$(qm config "$vmid" 2>/dev/null)"
used="$(echo "$cfg" | awk -F'[: ]' '/^hostpci[0-9]+:/{gsub("hostpci","",$1); print $1}' | sort -n | uniq)"
local i
for ((i=0; i<=15; i++)); do
if ! echo "$used" | grep -qx "$i"; then
echo "$i"
return 0
fi
done
return 1
}
# 从 VM 配置中查找某个 BDF 是否已被直通
qm_has_hostpci_bdf() {
local vmid="$1"
local bdf="$2"
qm config "$vmid" 2>/dev/null | grep -qE "^hostpci[0-9]+:.*\\b${bdf}\\b"
}
# 直通整个 SATA/SCSI/RAID 控制器到 VM含系统盘控制器保护
storage_controller_passthrough() {
log_step "磁盘控制器直通 - 扫描控制器"
if ! iommu_is_enabled; then
display_error "未检测到 IOMMU 已开启" "请先在 BIOS 开启 VT-d/AMD-Vi并在 PVE 中启用 IOMMU可在“硬件直通一键配置(IOMMU)”里开启)。"
return 1
fi
local controllers
controllers="$(list_storage_controllers)"
if [[ -z "$controllers" ]]; then
display_error "未发现 SATA/SCSI/RAID 控制器" "可尝试手工执行 lspci -Dnn 确认控制器是否存在。"
return 1
fi
echo -e "${CYAN}可用控制器列表:${NC}"
echo "$controllers" | awk '{printf " [%d] %s\n", NR, $0}'
echo -e "${UI_DIVIDER}"
local pick
read -p "请选择控制器序号 (返回请输入 0): " pick
pick="${pick:-0}"
if [[ "$pick" == "0" ]]; then
return 0
fi
if [[ ! "$pick" =~ ^[0-9]+$ ]]; then
display_error "序号必须是数字"
return 1
fi
local line bdf
line="$(echo "$controllers" | awk -v n="$pick" 'NR==n{print $0}')"
if [[ -z "$line" ]]; then
display_error "无效的序号: $pick"
return 1
fi
bdf="$(echo "$line" | awk '{print $1}')"
echo -e "${CYAN}该控制器下识别到的整盘设备:${NC}"
show_disks_under_pci_bdf "$bdf"
echo -e "${UI_DIVIDER}"
local protected
protected="$(get_protected_pci_bdfs)"
if echo "$protected" | grep -qx "$bdf"; then
display_error "安全拦截:禁止直通系统盘所在控制器 $bdf" "请勿直通包含 PVE 系统盘的控制器,否则会导致宿主机不可用。"
return 1
fi
local vmid
read -p "请输入目标 VMID: " vmid
if ! validate_qm_vmid "$vmid"; then
return 1
fi
if qm_has_hostpci_bdf "$vmid" "$bdf"; then
display_error "该控制器已在 VM 配置中存在直通记录" "无需重复直通。"
return 1
fi
local idx
idx="$(qm_find_free_hostpci_index "$vmid" 2>/dev/null)" || {
display_error "未找到可用 hostpci 插槽" "请先释放 VM 的 hostpci0-hostpci15 后再试。"
return 1
}
local hostpci_value="$bdf"
if qm_is_q35_machine "$vmid"; then
hostpci_value="${hostpci_value},pcie=1"
fi
local conf_path
conf_path="$(get_qm_conf_path "$vmid")"
if [[ -f "$conf_path" ]]; then
log_tips "修改 VM 配置前建议备份原配置"
backup_file "$conf_path" >/dev/null 2>&1 || true
fi
if ! confirm_action "为 VM $vmid 直通控制器hostpci$idx = $hostpci_value"; then
return 0
fi
if qm set "$vmid" "-hostpci${idx}" "$hostpci_value" >/dev/null 2>&1; then
local status
status="$(qm status "$vmid" 2>/dev/null | awk '{print $2}' | head -n 1)"
display_success "控制器直通已写入 VM 配置" "当前 VM 状态: ${status:-unknown}(如在运行中,需重启 VM 后生效)"
return 0
else
display_error "qm set 执行失败" "请检查 IOMMU/IOMMU group、VM 是否锁定,或查看 /var/log/pve-tools.log。"
return 1
fi
}
# 判断 NVMe 设备是否建议启用 MSI-X 重定位(启发式:存在 MSI-X 且存在 BAR2/Region 2
nvme_should_enable_msix_relocation() {
local bdf="$1"
local vv
vv="$(lspci -vv -s "$bdf" 2>/dev/null || true)"
if echo "$vv" | grep -q 'MSI-X:' && echo "$vv" | grep -qE 'Region 2: Memory|Region 2:.*Memory'; then
return 0
fi
return 1
}
# 获取当前 VM args不存在则返回空
qm_get_args() {
local vmid="$1"
qm config "$vmid" 2>/dev/null | awk -F': ' '/^args:/{sub(/^args: /,""); print $0; exit}'
}
# 幂等追加 VM args 片段(通过 qm set -args 覆盖式写入,但内容基于现有 args 合并)
qm_append_args() {
local vmid="$1"
local token="$2"
if [[ -z "$token" ]]; then
return 1
fi
local current
current="$(qm_get_args "$vmid")"
if echo "$current" | grep -Fq "$token"; then
return 0
fi
local new_args
if [[ -z "$current" ]]; then
new_args="$token"
else
new_args="${current} ${token}"
fi
qm set "$vmid" -args "$new_args" >/dev/null 2>&1
}
# NVMe 控制器直通到 VM含系统盘控制器保护与 MSI-X 重定位 args
nvme_passthrough() {
log_step "NVMe 直通 - 扫描 NVMe 控制器"
if ! iommu_is_enabled; then
display_error "未检测到 IOMMU 已开启" "请先在 BIOS 开启 VT-d/AMD-Vi并在 PVE 中启用 IOMMU可在“硬件直通一键配置(IOMMU)”里开启)。"
return 1
fi
local controllers
controllers="$(list_nvme_controllers)"
if [[ -z "$controllers" ]]; then
display_error "未发现 NVMe 控制器" "可尝试手工执行 lspci -Dnn | grep -i NVMe 确认设备是否存在。"
return 1
fi
echo -e "${CYAN}可用 NVMe 控制器列表:${NC}"
echo "$controllers" | awk '{printf " [%d] %s\n", NR, $0}'
echo -e "${UI_DIVIDER}"
local pick
read -p "请选择 NVMe 控制器序号 (返回请输入 0): " pick
pick="${pick:-0}"
if [[ "$pick" == "0" ]]; then
return 0
fi
if [[ ! "$pick" =~ ^[0-9]+$ ]]; then
display_error "序号必须是数字"
return 1
fi
local line bdf
line="$(echo "$controllers" | awk -v n="$pick" 'NR==n{print $0}')"
if [[ -z "$line" ]]; then
display_error "无效的序号: $pick"
return 1
fi
bdf="$(echo "$line" | awk '{print $1}')"
echo -e "${CYAN}该 NVMe 控制器下识别到的整盘设备:${NC}"
show_disks_under_pci_bdf "$bdf"
echo -e "${UI_DIVIDER}"
local protected
protected="$(get_protected_pci_bdfs)"
if echo "$protected" | grep -qx "$bdf"; then
display_error "安全拦截:禁止直通系统盘所在 NVMe 控制器 $bdf" "请勿直通包含 PVE 系统盘的 NVMe 控制器,否则会导致宿主机不可用。"
return 1
fi
local vmid
read -p "请输入目标 VMID: " vmid
if ! validate_qm_vmid "$vmid"; then
return 1
fi
if qm_has_hostpci_bdf "$vmid" "$bdf"; then
display_error "该 NVMe 已在 VM 配置中存在直通记录" "无需重复直通。"
return 1
fi
local idx
idx="$(qm_find_free_hostpci_index "$vmid" 2>/dev/null)" || {
display_error "未找到可用 hostpci 插槽" "请先释放 VM 的 hostpci0-hostpci15 后再试。"
return 1
}
local hostpci_value="$bdf"
if qm_is_q35_machine "$vmid"; then
hostpci_value="${hostpci_value},pcie=1"
fi
local enable_msix="no"
if nvme_should_enable_msix_relocation "$bdf"; then
echo -e "${YELLOW}检测到该 NVMe 可能需要 MSI-X 重定位bar2以提高兼容性。${NC}"
local ans
read -p "是否写入 MSI-X 重定位 args(yes/no) [yes]: " ans
ans="${ans:-yes}"
if [[ "$ans" == "yes" || "$ans" == "YES" ]]; then
enable_msix="yes"
fi
fi
local conf_path
conf_path="$(get_qm_conf_path "$vmid")"
if [[ -f "$conf_path" ]]; then
log_tips "修改 VM 配置前建议备份原配置"
backup_file "$conf_path" >/dev/null 2>&1 || true
fi
if ! confirm_action "为 VM $vmid 直通 NVMehostpci$idx = $hostpci_value),并写入 MSI-X 重定位参数(${enable_msix}"; then
return 0
fi
if ! qm set "$vmid" "-hostpci${idx}" "$hostpci_value" >/dev/null 2>&1; then
display_error "qm set 执行失败" "请检查 IOMMU/IOMMU group、VM 是否锁定,或查看 /var/log/pve-tools.log。"
return 1
fi
if [[ "$enable_msix" == "yes" ]]; then
local token
token="-set device.hostpci${idx}.x-msix-relocation=bar2"
if qm_append_args "$vmid" "$token"; then
log_success "已写入 args: $token"
else
log_warn "args 写入失败(已完成 hostpci 直通)"
fi
fi
local status
status="$(qm status "$vmid" 2>/dev/null | awk '{print $2}' | head -n 1)"
display_success "NVMe 直通已写入 VM 配置" "当前 VM 状态: ${status:-unknown}(如在运行中,需重启 VM 后生效)"
return 0
}
# ============ 引导配置辅助 ============
# 解析用户输入的磁盘路径为真实整盘设备(返回 /dev/sdX 或 /dev/nvme0n1