diff --git a/ddns-go/Makefile b/ddns-go/Makefile index 3946bc8a..ea647cb1 100644 --- a/ddns-go/Makefile +++ b/ddns-go/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ddns-go -PKG_VERSION:=6.17.3 -PKG_RELEASE:=20 +PKG_VERSION:=6.17.4 +PKG_RELEASE:=21 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)? diff --git a/luci-app-daede/Makefile b/luci-app-daede/Makefile index 94e92ac4..ac0791b6 100644 --- a/luci-app-daede/Makefile +++ b/luci-app-daede/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-daede PKG_VERSION:=1.14.7 -PKG_RELEASE:=35 +PKG_RELEASE:=36 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js index 8c7ab969..2eb1e78c 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/dae.js @@ -15,6 +15,7 @@ const RE_PLACEHOLDER_URL = /(['"])(?:(?:https?|https-file|file):\/\/[^'"]*?(?:ex const DEFAULT_TEMPLATE = '# luci-app-daede 默认配置:把 subscription 里的占位链接换成你的机场订阅,保存即可运行。\n' + 'global {\n' + + ' # 端口与网卡:lan_interface 填内网桥(通常 br-lan),wan_interface 保持 auto 即可。\n' + ' tproxy_port: 12345\n' + ' tproxy_port_protect: true\n' + ' so_mark_from_dae: 0\n' + @@ -35,6 +36,7 @@ const DEFAULT_TEMPLATE = '}\n' + '\n' + 'dns {\n' + + ' # 国内域名走 cndns 解析,其余走 fallbackdns,避免 DNS 污染。\n' + ' ipversion_prefer: 4\n' + ' response_ttl: 0\n' + ' upstream {\n' + @@ -50,6 +52,7 @@ const DEFAULT_TEMPLATE = '}\n' + '\n' + 'group {\n' + + ' # 节点分组:filter 决定用订阅里的哪些节点,policy 决定怎么选(min_moving_avg = 自动挑最快)。\n' + ' proxy {\n' + ' filter: subtag(my_airport)\n' + ' policy: min_moving_avg\n' + @@ -57,6 +60,8 @@ const DEFAULT_TEMPLATE = '}\n' + '\n' + 'routing {\n' + + ' # 分流规则:从上往下匹配,命中即停,最后由 fallback 兜底。\n' + + ' # 想让某个网站直连,在 fallback 之前加一行,例如:domain(example.com) -> direct\n' + ' pname(NetworkManager) -> direct\n' + ' dip(224.0.0.0/3) -> direct\n' + ' dip(255.255.255.255/32) -> direct\n' + @@ -577,8 +582,12 @@ function renderDaeEditor() { function syncHL() { hlCode.innerHTML = highlightDae(textarea.value) + '\n'; hlPre.scrollTop = textarea.scrollTop; + hlPre.scrollLeft = textarea.scrollLeft; } - textarea.addEventListener('scroll', function() { hlPre.scrollTop = textarea.scrollTop; }); + textarea.addEventListener('scroll', function() { + hlPre.scrollTop = textarea.scrollTop; + hlPre.scrollLeft = textarea.scrollLeft; + }); const save = E('button', { 'class': 'cbi-button cbi-button-positive' }, _('Save manual config')); const init = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Load default config')); const status = E('span', { 'class': 'dd-editor-status' }, ''); diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js index 0be84cb4..7dd3eac8 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/styles.js @@ -116,6 +116,7 @@ const CSS = [ /* keep selected text transparent too — else the browser force-colors it and it ghosts over the highlight pre underneath */ '.dd-edit-wrap .dd-editor-hl::selection{background:rgba(56,134,161,.25);color:transparent}', + '@media screen and (max-device-width:600px){.dd-edit-wrap .dd-editor,.dd-edit-wrap .dd-hl,.dd-edit-wrap .dd-hl code{font-size:16px}}', '.dh-c{color:#8a919a;font-style:italic}', '.dh-s{color:#2a8a4a}', '.dh-k{color:#9a3fb5;font-weight:600}', diff --git a/luci-app-daede/root/usr/share/luci-app-daede/update-geo.sh b/luci-app-daede/root/usr/share/luci-app-daede/update-geo.sh index 11a2f03f..783f1161 100755 --- a/luci-app-daede/root/usr/share/luci-app-daede/update-geo.sh +++ b/luci-app-daede/root/usr/share/luci-app-daede/update-geo.sh @@ -28,6 +28,34 @@ esac LOCK="/tmp/luci-app-daede.${TYPE}.lock" LOG="/tmp/luci-app-daede.${TYPE}.log" +RLOCK="/tmp/luci-app-daede.geo-reload.lock" + +schedule_backend_reload() { + if [ -d "$RLOCK" ]; then + rmt=$(date -r "$RLOCK" +%s 2>/dev/null || echo 0) + [ $(( $(date +%s) - rmt )) -gt 300 ] && rmdir "$RLOCK" 2>/dev/null + fi + mkdir "$RLOCK" 2>/dev/null || return 0 + ( + sleep 25 + ab="$(uci -q get daede.config.active_backend 2>/dev/null || echo daed)" + if [ "$ab" = dae ]; then + bin=dae; act=hot_reload + else + bin=daed; act=reload + fi + if /bin/pidof "$bin" >/dev/null 2>&1; then + if "/etc/init.d/$bin" "$act" >/dev/null 2>&1; then + echo "$(date '+%F %T') reloaded $bin" + else + echo "$(date '+%F %T') $bin reload failed" + fi + else + echo "$(date '+%F %T') $bin not running; skip reload" + fi + rmdir "$RLOCK" 2>/dev/null + ) >"$LOG" 2>&1 & +} if [ -f "$LOCK" ]; then # Lock is fresh (< 5 min)? Refuse. Stale? Remove and proceed. @@ -73,6 +101,7 @@ fi else mv "$TMP" "$DEST" echo "$(date '+%F %T') updated $DEST ($size bytes)" + schedule_backend_reload fi fi if [ "$rc" = 0 ]; then echo "$(date '+%F %T') ✓ 完成"; else echo "$(date '+%F %T') ✗ 失败 (rc=$rc)"; fi diff --git a/luci-app-qemu-vms/Makefile b/luci-app-qemu-vms/Makefile index fc248284..664de946 100644 --- a/luci-app-qemu-vms/Makefile +++ b/luci-app-qemu-vms/Makefile @@ -4,7 +4,7 @@ LUCI_TITLE:=Web UI for QEMU Virtual Machine Simple (VMS) LUCI_DEPENDS:=@TARGET_x86_64 +qemu-vms +ttyd +usbutils +pciutils PKG_LICENSE:=GPLv3 PKG_VERSION:=0.0.1 -PKG_RELEASE:=6 +PKG_RELEASE:=7 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/networks.js b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/networks.js index 6c02fa20..d8dd06c6 100644 --- a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/networks.js +++ b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/networks.js @@ -73,7 +73,7 @@ return view.extend({ }); var bridge = s.option(form.Value, 'bridge', _('Bridge (optional)')); - bridge.value('', _('leave empty')); + bridge.value('', _('not assign')); bridgeList.forEach(function(name) { bridge.value(name, name); }); diff --git a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/passthrough.js b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/passthrough.js index 1b6b8f89..65e5e67d 100644 --- a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/passthrough.js +++ b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/passthrough.js @@ -34,16 +34,17 @@ return view.extend({ return found; }, - pciOwner: function(addr) { + pciOwners: function(addr) { var section = this.findPciSection(addr); if (!section) - return null; - var owner = null; + return []; + var owners = []; uci.sections('qemu-vms', 'vm').forEach(function(vm) { - if (vm.pci_passthrough === section) - owner = vm['.name']; + var list = [].concat(vm.pci_passthrough || []); + if (list.indexOf(section) !== -1) + owners.push(vm['.name']); }); - return owner; + return owners; }, // --- USB helpers --- @@ -70,6 +71,22 @@ return view.extend({ return owners; }, + cleanupVmReferences: function(sectionType, sectionName) { + // sectionType: 'pci_passthrough' or 'usb_passthrough' + uci.sections('qemu-vms', 'vm').forEach(function(vm) { + var list = [].concat(vm[sectionType] || []); + var index = list.indexOf(sectionName); + if (index !== -1) { + list.splice(index, 1); + if (list.length) + uci.set('qemu-vms', vm['.name'], sectionType, list); + else + uci.unset('qemu-vms', vm['.name'], sectionType); + } + } + ); + }, + createPciPassthrough: function(dev, ev) { var self = this; var addr = dev.addr; @@ -144,9 +161,9 @@ return view.extend({ return; } - var owner = this.pciOwner(addr); - if (owner) { - ui.addNotification(null, E('p', _('This device is currently attached to VM "%s". Please detach it from the VM first.').format(owner)), 'error'); + var owners = this.pciOwners(addr); + if (owners.length) { + ui.addNotification(null, E('p', _('This device is currently attached to VM(s): %s. Please detach it from all VMs first.').format(owners.join(', '))), 'error'); return; } @@ -159,6 +176,7 @@ return view.extend({ 'class': 'btn cbi-button-negative', 'click': function() { uci.remove('qemu-vms', section); + self.cleanupVmReferences('pci_passthrough', section); return uci.save().then(function() { ui.hideModal(); ui.addNotification(null, E('p', _('Passthrough removed for %s.').format(addr)), 'info'); @@ -269,6 +287,7 @@ return view.extend({ 'class': 'btn cbi-button-negative', 'click': function() { uci.remove('qemu-vms', section); + self.cleanupVmReferences('usb_passthrough', section); return uci.save().then(function() { ui.hideModal(); ui.addNotification(null, E('p', _('Passthrough removed.')), 'info'); @@ -286,7 +305,8 @@ return view.extend({ var self = this; var rows = devices.map(function(dev) { var section = self.findPciSection(dev.addr); - var owner = self.pciOwner(dev.addr); + var owners = self.pciOwners(dev.addr); + var owner = owners.length ? owners.join(', ') : null; var passthroughStatus = section ? _('Active
Section: %s').format(section) : _('Not passthrough'); if (owner) passthroughStatus += ' ' + _('
Host: %s').format(owner); @@ -397,7 +417,7 @@ return view.extend({ renderContent: function(data) { var hw = data[0]; var self = this; - + var description = E('div', { 'class': 'cbi-section' }, [ E('h2', {}, _('Hardware Passthrough')), E('p', { 'class': 'cbi-section-descr' }, diff --git a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/status.js b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/status.js index b71b4545..fe0ee057 100644 --- a/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/status.js +++ b/luci-app-qemu-vms/htdocs/luci-static/resources/view/qemu-vms/status.js @@ -423,11 +423,18 @@ return view.extend({ }); vncPortRow.style.display = ((vm.display_type || 'serial') === 'vnc') ? '' : 'none'; - var pciSelect = E('select', { 'class': 'cbi-input-select', 'id': 'edit-pci' }, [ - E('option', { 'value': '', 'selected': !vm.pci_passthrough || null }, _('-- none --')) - ].concat(allPci.map(function(p) { - return E('option', { 'value': p, 'selected': vm.pci_passthrough === p || null }, p); - }))); + var attachedPci = [].concat(vm.pci_passthrough || []); + var pciSelect = E('div', { 'style': 'display: flex; flex-wrap: wrap; gap: 0.3em 1.2em;' }, allPci.map(function(p) { + return E('label', { 'style': 'white-space: nowrap; font-weight: normal;' }, [ + E('input', { + 'type': 'checkbox', + 'class': 'edit-pci-check', + 'value': p, + 'checked': attachedPci.indexOf(p) !== -1 || null + }),' ' + p + ]); + })); + if (!allPci.length) pciSelect = E('em', {}, _('No PCI passthrough sections defined yet')); var networkChecks = allNetworks.map(function(net) { return E('div', {}, [ @@ -612,9 +619,10 @@ return view.extend({ else uci.unset('qemu-vms', name, 'custom_arg'); - var pci = document.getElementById('edit-pci').value; - if (pci) - uci.set('qemu-vms', name, 'pci_passthrough', pci); + var newPci = Array.prototype.slice.call(document.querySelectorAll('.edit-pci-check:checked')) + .map(function(el) { return el.value; }); + if (newPci.length) + uci.set('qemu-vms', name, 'pci_passthrough', newPci); else uci.unset('qemu-vms', name, 'pci_passthrough'); diff --git a/luci-app-ssr-plus/Makefile b/luci-app-ssr-plus/Makefile index aa32f432..bcb3a065 100644 --- a/luci-app-ssr-plus/Makefile +++ b/luci-app-ssr-plus/Makefile @@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus LUCI_PKGARCH:=all PKG_NAME:=luci-app-ssr-plus PKG_VERSION:=196 -PKG_RELEASE:=52 +PKG_RELEASE:=53 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ diff --git a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr index 951a26e0..cedae0be 100755 --- a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr +++ b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr @@ -1529,107 +1529,107 @@ get_name() { gen_config_file() { #server1 type2 code3 local_port4 socks_port5 chain6 threads5 local server="$1" local type="$2" - local mode="$3" + local code="$3" local local_port="$4" local socks_port="$5" local chain="$6" local tmp_port local ss_protocol - case "$mode" in + case "$code" in 1) - config_file=$tcp_config_file + config_file="$tcp_config_file" chain_config_file=$(echo "${config_file}" | sed 's/ssrplus\//ssrplus\/chain-/') ;; 2) - config_file=$udp_config_file + config_file="$udp_config_file" chain_config_file=$(echo "${config_file}" | sed 's/ssrplus\//ssrplus\/chain-/') ;; 3) if [ -n "$tmp_local_port" ]; then - tmp_port=$tmp_local_port + tmp_port="$tmp_local_port" else - tmp_port=$tmp_shunt_local_port + tmp_port="$tmp_shunt_local_port" fi - config_file=$shunt_config_file + config_file="$shunt_config_file" chain_config_file=$(echo "${config_file}" | sed 's/ssrplus\//ssrplus\/chain-/') ;; 4) ss_protocol="socks" - config_file=$local_config_file + config_file="$local_config_file" chain_config_file=$(echo "${config_file}" | sed 's/ssrplus\//ssrplus\/chain-/') ;; esac case "$type" in ss | ssr) - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" "${ss_protocol:-redir}" >"$config_file" - if [ "$mode" = "3" ]; then - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$tmp_port" socks >"$shunt_dns_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port ${ss_protocol:-redir} >"$config_file" + if [ "$code" = "3" ]; then + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $tmp_port socks >"$shunt_dns_config_file" fi ;; v2ray) - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" "$socks_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port $socks_port >"$config_file" ;; trojan) - case "$mode" in + case "$code" in 1) - lua /usr/share/shadowsocksr/gen_config.lua "$server" nat "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server nat $local_port >"$config_file" ;; 2) - lua /usr/share/shadowsocksr/gen_config.lua "$server" client "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server client $local_port >"$config_file" ;; 3) - lua /usr/share/shadowsocksr/gen_config.lua "$server" nat "$local_port" >"$config_file" - lua /usr/share/shadowsocksr/gen_config.lua "$server" client "$tmp_port" >"$shunt_dns_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server nat $local_port >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server client $tmp_port >"$shunt_dns_config_file" ;; 4) - lua /usr/share/shadowsocksr/gen_config.lua "$server" client "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server client $local_port >"$config_file" ;; esac ;; naiveproxy) - case "$mode" in + case "$code" in 1) - lua /usr/share/shadowsocksr/gen_config.lua "$server" redir "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server redir $local_port >"$config_file" ;; 3) - lua /usr/share/shadowsocksr/gen_config.lua "$server" redir "$local_port" >"$config_file" - lua /usr/share/shadowsocksr/gen_config.lua "$server" socks "$tmp_port" >"$shunt_dns_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server redir $local_port >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server socks $tmp_port >"$shunt_dns_config_file" ;; 4) - lua /usr/share/shadowsocksr/gen_config.lua "$server" socks "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server socks $local_port >"$config_file" ;; esac ;; hysteria2) - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" "$socks_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port $socks_port >"$config_file" ;; tuic) - case "$mode" in + case "$code" in 1|2|4) - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port >"$config_file" ;; 3) if [ -z "$chain" ]; then - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" >"$shunt_dns_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port >"$shunt_dns_config_file" else - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port >"$config_file" fi ;; esac ;; shadowtls) - case "$mode" in + case "$code" in 1|2|4) if [ -z "$chain" ]; then - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$type" "$local_port" >"$chain_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $type $local_port >"$chain_config_file" else - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" "$socks_port" "$chain" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port $socks_port $chain >"$config_file" fi ;; 3) - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$type" "$local_port" >"$chain_config_file" - lua /usr/share/shadowsocksr/gen_config.lua "$server" "$mode" "$local_port" "$socks_port" "$chain" >"$config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $type $local_port >"$chain_config_file" + lua /usr/share/shadowsocksr/gen_config.lua $server $mode $local_port $socks_port $chain >"$config_file" ;; esac ;; diff --git a/qemu-vms/Makefile b/qemu-vms/Makefile index 739fec9f..92795cb7 100644 --- a/qemu-vms/Makefile +++ b/qemu-vms/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=qemu-vms PKG_VERSION:=0.0.1 -PKG_RELEASE:=6 +PKG_RELEASE:=7 PKG_MAINTAINER:=Konstantine Shevlakov include $(INCLUDE_DIR)/package.mk diff --git a/qemu-vms/root/etc/init.d/qemu-vms b/qemu-vms/root/etc/init.d/qemu-vms index 034a122d..487672d1 100755 --- a/qemu-vms/root/etc/init.d/qemu-vms +++ b/qemu-vms/root/etc/init.d/qemu-vms @@ -102,6 +102,41 @@ _add_usb() { usb_needed=1 } +_add_pci() { + local pci_section="$1" + local pci_id + config_get pci_id "$pci_section" pci_id + [ -n "$pci_id" ] || return 0 + + local pci_path="/sys/bus/pci/devices/0000:$pci_id" + local iommu_group="" + if [ -L "$pci_path/iommu_group" ]; then + iommu_group="$(basename "$(readlink -f "$pci_path/iommu_group")")" + fi + if [ -n "$iommu_group" ]; then + host_log "$cfg" "PCI passthrough: device $pci_id is in IOMMU group $iommu_group" + for dev in /sys/kernel/iommu_groups/$iommu_group/devices/*; do + local dev_name="$(basename "$dev")" + local current_driver="$(basename "$(readlink -f "$dev/driver" 2>/dev/null)" 2>/dev/null)" + if [ -n "$current_driver" ] && [ "$current_driver" != "vfio-pci" ]; then + echo -n "$dev_name" > "$dev/driver/unbind" 2>/dev/null + host_log "$cfg" "PCI: unbound $dev_name from $current_driver" + fi + echo "vfio-pci" > "$dev/driver_override" 2>/dev/null + echo "$dev_name" > /sys/bus/pci/drivers/vfio-pci/bind 2>/dev/null + if [ -e "$dev/driver" ] && [ "$(basename "$(readlink -f "$dev/driver")")" = "vfio-pci" ]; then + host_log "$cfg" "PCI: successfully bound $dev_name to vfio-pci" + else + host_log "$cfg" "ERROR: failed to bind $dev_name to vfio-pci" + fi + done + sleep 3 + pci_opts="$pci_opts -device vfio-pci,host=$pci_id" + else + host_log "$cfg" "WARNING: Cannot determine IOMMU group for $pci_id" + fi +} + #_unbind_usb_host() { # local usbcfg="$1" # local vendor_id product_id @@ -244,7 +279,7 @@ start_vm() { config_get vnc_port "$cfg" vnc_port config_get machine "$cfg" machine "q35" - local console_sock="$STAUS_DIR/qemu-$cfg.sock" + local console_sock="$STATUS_DIR/qemu-$cfg.sock" local console_log="$LOG_DIR/qemu-$cfg-console.log" local qmp_sock="$STATUS_DIR/qemu-$cfg.qmp" @@ -276,41 +311,8 @@ start_vm() { # fi #fi - local pci_id="" pci_opts="" - if [ -n "$pci_passthrough" ]; then - config_get pci_id "$pci_passthrough" pci_id - if [ -n "$pci_id" ]; then - local pci_path="/sys/bus/pci/devices/0000:$pci_id" - local iommu_group="" - if [ -L "$pci_path/iommu_group" ]; then - iommu_group="$(basename "$(readlink -f "$pci_path/iommu_group")")" - fi - if [ -n "$iommu_group" ]; then - host_log "$cfg" "PCI passthrough: device $pci_id is in IOMMU group $iommu_group" - for dev in /sys/kernel/iommu_groups/$iommu_group/devices/*; do - local dev_name="$(basename "$dev")" - local current_driver="$(basename "$(readlink -f "$dev/driver" 2>/dev/null)" 2>/dev/null)" - if [ -n "$current_driver" ] && [ "$current_driver" != "vfio-pci" ]; then - echo -n "$dev_name" > "$dev/driver/unbind" 2>/dev/null - host_log "$cfg" "PCI: unbound $dev_name from $current_driver" - fi - echo "vfio-pci" > "$dev/driver_override" 2>/dev/null - echo "$dev_name" > /sys/bus/pci/drivers/vfio-pci/bind 2>/dev/null - if [ -e "$dev/driver" ] && [ "$(basename "$(readlink -f "$dev/driver")")" = "vfio-pci" ]; then - host_log "$cfg" "PCI: successfully bound $dev_name to vfio-pci" - else - host_log "$cfg" "ERROR: failed to bind $dev_name to vfio-pci" - fi - done - sleep 3 - pci_opts="-device vfio-pci,host=$pci_id" - else - host_log "$cfg" "WARNING: Cannot determine IOMMU group for $pci_id" - fi - else - host_log "$cfg" "WARNING: pci_passthrough section exists but pci_id is empty - skipping" - fi - fi + local pci_opts="" + config_list_foreach "$cfg" pci_passthrough _add_pci local cdrom_opts="" [ -n "$cdrom" ] && cdrom_opts="-cdrom $cdrom"