普通视图

发现新文章,点击刷新页面。
昨天以前WuSiYu Blog

虚拟机检测绕过和nvme性能优化的libvirt配置

作者 WuSiYu
2026年1月24日 03:36

免责声明:我只是为了在自建的云游戏串流虚拟机上进行远程游戏,用虚拟机是因为All-in-boom宿主机还要跑别的东西。请勿用于非法用途,当然对于非法用途的人也早就知道这些了。

参考自:https://github.com/zhaodice/qemu-anti-detection ,但这个配置会极大影响直通nvme硬盘的4k多线程性能,会从700MB/s跌倒20MB/s,导致游戏加载非常慢。因此研究了一些不影响通过检测的优化,使其恢复到200~300MB/s左右,不再成为瓶颈。

以下是xml配置文件关键段落:

<domain type='kvm' id='62' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  ... 略 ...
  <features>
    <acpi/>
    <apic/>
    <kvm>
      <hidden state='on'/>
    </kvm>
    <vmport state='off'/>
    <smm state='on'/>
    <ioapic driver='kvm'/>
  </features>
  <cpu mode='host-passthrough' check='none' migratable='off'/>
  <clock offset='localtime'>
    <timer name='rtc' tickpolicy='catchup'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
    <timer name='kvmclock' present='no'/>
  </clock>
  
  <devices>
    ... 略 ...
    <memballoon model='none'/>
  </devices>
  
  <qemu:commandline>
    <qemu:arg value='-smbios'/>
    ... 略(请参考最上面的链接) ...

    <qemu:arg value='-cpu'/>
    <qemu:arg value='host,family=6,model=167,stepping=1,l3-cache=on,model_id=Intel(R) Xeon(R) E-2378 CPU @ 2.60GHz,vmware-cpuid-freq=false,enforce=false,host-phys-bits=true,hypervisor=off,+x2apic,hv-time,hv-relaxed,hv-vapic,hv-spinlocks=0x1fff,hv-vendor-id=GenuineIntel,tsc-frequency=2600000000'/>
  </qemu:commandline>
</domain>

NVME硬盘4k多线程性能差主要是因为时钟源问题,为通过检测我们无法使用常用的虚拟机高性能时钟源,导致中断性能问题。(是的,pcie直通并非一定没有性能损耗)

其中最为关键的是qemu:arg中的-cpu段落,family=6,model=167,stepping=1,l3-cache=on,model_id=Intel(R) Xeon(R) E-2378 CPU @ 2.60GHz根据实际修改。经过反复测试,必须使用这种方式(libvirt原生配置不完全),同时不能加入migratable=no以打开+invtsc,哪怕加入更多参数修正也会有cpu feature细微差异,导致检测不过。但我们可以设置tsc-frequency=2600000000,强行使用tsc,数值为你的CPU基频(我这里是2.6GHz),睿频不影响。另外这里依然保留了一些hv特性,这对于开启了 Hyper-V 功能(如 VBS/WSL2)的物理机也是存在的,对于Win11机器很常见。

虚拟机检测绕过和nvme性能优化的libvirt配置最先出现在WuSiYu Blog

UCG Fiber 主路由器 + OpenWrt 透明代理 + 高可用 方案和评测(对比ROS)

作者 WuSiYu
2026年1月15日 00:44

背景和需求

直连流量尽量通过主路由直连,同时内网所有设备透明代理。为了代理性能和方便管理,OpenWrt 可以跑在再服务器的虚拟机中,因此有必要在其离线时实现自动 failover,将内网降级为正常全直连网络。

过去用 RouterOS (ROS) 主路由(CCR2004) + OpenWrt 网关 + VRRP 实现,不过 ROS 的问题是设备管理不直观,不像 Unifi 的系统一样有着很好的大屏,以及 DPI、IDS/IPS 等功能。同时 ROS 没有 PPPoE 硬件 offload 支持,在这一点上甚至不如几百块的家用无线路由器。

因此我购入了一台 Unifi Cloud Gateway Fiber(UCG Fiber),三个10G口(2光1电)+ 4个2.5G电口,本文将探讨将其代替 ROS 作为高可用魔法(代理)家用网络环境的主路由器的方案和局限性。

TLDR:Unifi与ROS优缺点对比

(这里的优缺点仅针对本场景)

Unifi UCG Fiber
优点:

  • 界面美观,对客户端识别更智能(比如常见手机型号),DPI 功能还能分析流量种类和记录客户端的网络活动历史
  • PPPoE hardware offload 支持,千兆宽带满速下载时 CPU 占用没什么变化,据其他人测试能完全跑满 10G PPPoE
  • 小体积、高颜值,非常静音的主动散热,还有个小屏幕能看实时网速

缺点:

  • 灵活性很低,不支持 VRRP(就算是Unifi的机架式设备也只能自己跟自己组HA),高可用代理方案配置麻烦,目前没找到可用的方法实现高可用的IPv6代理,只能强制让代理都走 IPv4
  • 自带功能有些很不完善:
    • IPv6 支持烂,任何情况下 IPv6 只会走主要 WAN,哪怕 IPv6 地址下发的都是备用 WAN 的 v6,且主要 WAN 都没启用 v6
    • 策略路由(PBR)不支持 IPv6
    • 策略路由只能指定出口 WAN,没法指定下一跳到内网机器。而且 Fallback 功能残废,只有在 WAN 接口 Down 了时才会生效,哪怕系统都检测到该 WAN 不可用了也不会切换
    • Wireguard Server + 双 WAN 支持差,无法在备用 WAN 上接受连接(回程路由有 bug),除非主要 WAN 不可用
  • 其他一些问题:端口转发的 NAT Loopback(在内网通过映射到公网的地址访问内部服务时)无法硬件加速,只能跑到1~3GBps,跑不满内网带宽
  • 开启 Smart Queue QoS 后 PPPoE offload 失效,只能跑到 600Mbps 不到,且无法只在一个方向上开启(比如只在上行开启 QoS)
  • 客户端管理功能很强,但没有一个地方能看当前所有的网络连接(Conntrack)和实时速度(类似ROS的IP -> Firewall -> Connections)

RouterOS

  • 优点:灵活性很高,可以直接跟 OpenWrt 组VRRP,且 VRRP 支持脚本,ROS 的脚本可以便利的修改所有网络设置,可以实现各种自动化功能
  • 缺点1:对于管理客户端流量不直观,Connections 里虽然能看到一个内网 IP 连了一个外网 IP,但还得去 DHCP 分配里查这个内网 IP 的主机名或MAC地址,可能还得上网查一下这个 MAC 地址的 vendor,才能确定这个客户端是谁;对于外网 IP 也没法跟 DNS 查询记录里的域名做关联,很不直观。
  • 缺点2:MikroTik 家的硬路由性能有限,在我的 CCR2004 上 PPPoE 硬跑倒是能跑满千兆宽带下行,但一开双向的 Cake QoS 就不行了(哪怕开了fasttrack),不过可以只开上行的 QoS,下行带宽够大也不需要 QoS 了

UCG Fiber 硬件分析

通过dmesg和拆解图分析得到内部拓扑:

SoC 为高通 IPQ9574,拥有 NSS / PPE 硬件加速功能,CPU 为4x A73 @ 2.2GHz,不算太强,还是比较依赖硬件加速的。

拓扑方面,两个 10G 光口直连 SoC,所有电口(4×2.5G + 10G)连接到内部的 RTL8372 交换芯片,共享一个 10G 链路到 SoC。由于有硬件加速,光口和电口的交换不会占用CPU,但电口侧确实存在带宽瓶颈(虽然一般遇不到)。

硬件方面,内存物理大小共4G,OS中可用3G(包括Unifi宣传的也是3G),大概是有1G被SoC其他组件占用,也可能是ECC。

另外UCG Fiber还有一个加速度传感器用来检测摆放姿态,可以根据姿态调整小屏幕的显示方向。(有时间搞这些能不能把 IPv6 搞完善点啊喂)

方案概述

为实现 Unifi 主路由 + OpenWrt 全局透明代理 + 高可用,首先 Unifi 作为主路由器,连接光猫进行 PPPoE 拨号,OpenWrt 连接到 Unifi 的 LAN 上作为客户端(类似二级路由,但不同的是终端设备不去连接 OpenWrt),然后利用 Unifi 的双 WAN 主备切换的功能,让 OpenWrt 再作为 Unifi 的一个 WAN,即OpenWrt WAN Loopback,具体如下:

  • 将 Unifi 的 WAN1 (使用DHCP,IPv6关闭)连接 OpenWrt 的 LAN ,OpenWrt 的 WAN 再连接到 Unifi 的 LAN
  • 将 Unifi 的 WAN2 接光猫拨号,开启IPv6,并把内网的 IPv6 前缀来源也设置为 WAN2
  • Unifi 上设置 WAN1(OpenWrt)为 Primary,WAN2(PPPoE)为 Backup,模式为Failover Only
  • 26/02/15 补充:Unifi 上需要给 WAN1(OpenWrt)手动设置 DNS 服务器为 OpenWrt IP,不能勾选“Auto DNS”,不然Unifi 会偷偷给你塞一个1.1.1.1
  • Unifi 上设置策略路由:
    • 1. 目标 Region 为 China 的都走 WAN2(不用勾 Kill Switch,没啥用且会跟下面的DNS hijack有奇妙的冲突);
    • 2. 来自 OpenWrt 的流量都走 WAN2(避免循环,勾上Kill Switch)
  • OpenWrt 上和标准的路由+代理设置基本相同,WAN 为 DHCP,连接 Unifi 内网。然后要将 WAN 和 WAN6 接口高级设置中的“自动获取 DNS 服务器”禁用,并填入运营商的 DNS 地址,避免循环
  • 效果:正常情况下,客户端的 DNS 请求先到 Unifi 的 DNS 服务器上(对于 DPI 功能有帮助),然后走 WAN1 -> OpenWrt 的无污染 DNS,解析得到国外地址继续走 OpenWrt 做代理(OpenWrt 再通过 Unifi LAN -> PPPoE 连接代理服务器),国内地址直接走 PPPoE 发出。一旦 OpenWrt 挂掉,Unifi 检测到 WAN1 不通,会自动把全部路由和 DNS 都切换到 PPPoE,实现自动降级直连
  • (可选)此时 Unifi 上可以再设置一个 DNS hijack,让不遵守 DHCP 下发的 DNS 服务器的设备的所有 UDP 53 端口流量都强制发到 Unifi DNS 服务器:增加 DNAT 规则,匹配来源 IP 非 OpenWrt,目标 IP 非 Unifi,目标 UDP 53,将目标 IP 改写为 Unifi 的 IP

IPv6 与 WireGuard Server 问题

按上述方案配置后,你会发现两个问题:

IPv6 不通:哪怕客户端已经具有了来自 PPPoE 的 v6 地址,但 v6 网络就是不通。这里来到了 Unifi 最逆天的地方了:除非主 WAN 断开,不然在任何情况下都试图让 IPv6 走主 WAN,哪怕主 WAN 都没配 IPv6,导致网络不通。

Unifi 上运行的 WireGuard Server 不通:哪怕 WireGuard Server 上设置了监听的是 WAN2 (PPPoE),只要主 WAN 还在,所有回包都会走主 WAN,导致外界的客户端连不上。最新的Unifi OS 5.0.12 + Network 10.1.84版本已经修复此问题

==> 对于Unifi OS 5.0.12 + Network 10.1.84或更新的版本(2026年2月):

已经无WireGuard的问题,仅有IPv6问题,而IPv6修复也可以通过Unifi内置的静态路由来实现,不再需要SSH+脚本魔改:

如上图:新建Policy,选择Route,类型选择静态,接口选择WAN2(真正连运营商的WAN),地址填写2000::/3(全球公网v6单播地址)

==> 对于早于Unifi OS 5.0.12 + Network 10.1.84的版本,若在Unifi上使用WireGuard Server,需要通过脚本修复WireGuard Server不通的问题,下方提供了旧版方案的脚本,能一并修复IPv6问题和WireGuard Server问题

点击展开旧版方案+脚本

需要 SSH 进去并设置自启脚本来修正:

编辑 /data/internet_as_backup_wan_fix.sh ,内容为(按需调整IFACE和WG_PORT):

#!/bin/sh

# ================= Configuration =================
# backup WAN Interface (real Internet, e.g. PPPoE to ISP)
IFACE="ppp1"
# Routing Table ID for backup WAN (Usually 202 for WAN2)
TABLE="202"
# WireGuard Server Listen Port
WG_PORT="16384"
# Log Tag for syslog
LOG_TAG="UniFi_Network_Fixer"
# =================================================

# Function to clean up rules on exit or restart
cleanup() {
    # Remove IPv6 default route
    ip -6 route del default dev "$IFACE" metric 1 2>/dev/null

    # Remove Policy Routing rule based on Source Port
    ip rule del sport "$WG_PORT" lookup "$TABLE" 2>/dev/null

    # Remove Force-SNAT rule from NAT table
    iptables -t nat -D POSTROUTING -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE 2>/dev/null
}

# Run cleanup on start to ensure a clean slate
cleanup

logger -t "$LOG_TAG" "Starting network fix script..."

while true; do
    # Check if the interface exists
    if ip link show "$IFACE" > /dev/null 2>&1; then

        # -----------------------------------------------------
        # Task 1: Fix IPv6 Direct Access
        # Problem: Unifi doesn't add a default IPv6 route for Secondary WAN.
        # Fix: Manually add a default route to Main Table via ppp1.
        # -----------------------------------------------------
        HAS_V6_ROUTE=$(ip -6 route show default dev "$IFACE" metric 1)
        if [ -z "$HAS_V6_ROUTE" ]; then
            ip -6 route add default dev "$IFACE" metric 1
            logger -t "$LOG_TAG" "IPv6: Added default route via $IFACE"
        fi

        # -----------------------------------------------------
        # Task 2: Fix WireGuard VPN Server on Secondary WAN
        # Problem: Local UDP traffic uses Primary WAN gateway & source IP.
        # Fix A: Policy Routing based on Source Port (Directs traffic to WAN2 Table)
        # -----------------------------------------------------
        if ! ip rule show | grep -q "sport $WG_PORT lookup $TABLE"; then
            # Priority 98 ensures it runs before Unifi's default rules
            ip rule add sport "$WG_PORT" lookup "$TABLE" priority 98
            ip route flush cache
            logger -t "$LOG_TAG" "VPN: Added Policy Routing for Source Port $WG_PORT"
        fi

        # -----------------------------------------------------
        # Fix B: Force SNAT (Masquerade)
        # Problem: Source IP might be internal (e.g., 192.168.x.x) even if routed correctly.
        # Fix: Force NAT on egress to ensure Source IP matches the Public IP.
        # -----------------------------------------------------
        if ! iptables -t nat -C POSTROUTING -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE 2>/dev/null; then
            # Insert at top (1) to override any conflicting rules
            iptables -t nat -I POSTROUTING 1 -o "$IFACE" -p udp --sport "$WG_PORT" -j MASQUERADE
            logger -t "$LOG_TAG" "VPN: Added Force-SNAT rule for port $WG_PORT"
        fi

    fi

    # Check every 30 seconds to handle re-dials or IP changes
    sleep 30
done

加上执行权限:chmod +x /data/internet_as_backup_wan_fix.sh

然后使用 crontab -e 并添加以下内容,设置开机自启:

@reboot /data/internet_as_backup_wan_fix.sh > /dev/null 2>&1 &

注意:每次底层 Unifi OS 升级后需要重新添加 crontab,因此建议关闭自动更新

为什么一定要用这种方案?

  1. 为什么不能 OpenWrt 直接做二级网关(LAN 侧)? Unifi 不支持 VRRP。如果把 OpenWrt 设为 DHCP 下发的默认网关,一旦 OpenWrt 挂了,Unifi 无法自动把网关切回自己,全家断网。
  2. 为什么不能 OpenWrt 做旁路网关配合 PBR? Unifi 的策略路由(Traffic Routes)只能选 WAN 接口作为出口,不能指定 LAN 侧的某个 IP(OpenWrt)作为下一跳。
  3. 为什么必须让 OpenWrt 充当 Primary WAN?
    如果反过来让 PPPoE 作为 Primary WAN,OpenWrt 作为 Backup WAN,则无法利用双 WAN 的 Failover 功能来应对 OpenWrt 下线的情况,通过策略路由则会有以下问题:
    • 策略路由的 Fallback 功能残废:需要设置策略路由让海外流量走 Backup WAN,但问题是策略路由的 Fallback 与双 WAN Fallback 机制不通,后者会用 ping 检测是否真正可用,而前者只会在相应接口 Down 掉时才会被禁用,导致就算 OpenWrt 挂掉或死机,但接口还在,海外可直连的流量依然无法 Fallback
    • DNS 配置麻烦:默认的 DNS 行为会直接走 PPPoE,造成 DNS 无法抗污染。而若指定走 OpenWrt 的话又难以 Failover,需要 SSH 进去 Unifi 来配置 dnsmasq 为 strict-order 模式,然后使用主-备 DNS 服务器,也很麻烦

附录:或许还有一种更简单但更不优雅的方案,就是 OpenWrt 或者其他运行代理插件的主机再跑一个 WireGuard Server,然后在 Unifi 上用 VPN Client 连接并用策略路由进行分流,但对UCG孱弱的CPU是个挑战,尤其是千兆梯子已经普遍的今天。

另外还有网友提到,组一个 OSPF 并向 Unifi 广播需要代理的网段也是一种办法。

UCG Fiber 主路由器 + OpenWrt 透明代理 + 高可用 方案和评测(对比ROS)最先出现在WuSiYu Blog

OpenWrt上AdGuardHome前置配置的一些小坑

作者 WuSiYu
2025年12月3日 19:46

为了配合一些上网插件并达到最好的过滤效果,需要将AdGuardHome前置于OpenWrt自带的dnsmasq:

[设备] -> [AdGuardHome 53端口] -> [dnsmasq 54端口] -> 一些插件 -> 多种DNS服务器

AdGuardHome在这里作为第一级DNS服务器,运行在标准53端口上,直接接受客户端连接。然后其上游为OpenWrt的自带dnsmasq的DNS服务器,这里需要将其端口改为端口54以避免冲突。之后一些上网插件会自动将dnsmasq的上游设置为它的一些程序,最终连接到大陆或外网的DNS。

这一套似乎没什么问题,但其实有一些坑:

坑1: DHCP v4 不下发DNS服务器配置

OpenWrt dnsmasq的DNS服务若运行在未53端口上时,其DHCP v4 服务就不会默认发送DNS选项。导致部分支持IPv6的设备依然可以通过IPv6连接DNS,表现正常,但仅支持IPv4的设备就会无法上网。

解法很简单,需要在OpenWrt -> 网络 -> lan -> DHCP 服务器 -> 高级设置 处,手动强制加入一个DHCP选项来下发DNS服务器配置:6,<路由器IP>

坑2: 在一些插件内,勾选DNS重定向会绕过AdGuardHome

如果你勾选了这个选择,那DNS请求会被强制直接转达到dnsmasq的地址,也就是54端口上,导致AdGuardHome被绕过了。那如果你仍然需要类似的功能呢?可以在 OpenWrt -> 网络 -> 防火墙 -> 端口转发中手动创建一条规则

具体如下:

# /etc/config/firewall
config redirect 'dns_int'
	option name 'Intercept-DNS'
	option family 'any'
	option proto 'tcp udp'
	option src 'lan'
	option src_dport '53'
	option target 'DNAT'

OpenWrt上AdGuardHome前置配置的一些小坑最先出现在WuSiYu Blog

Unraid 7.2+ WebUI美化主题和自定义CSS插件

作者 WuSiYu
2025年11月27日 04:00

由于 Unraid 升级到 7.2+ 版本后,原本常用的 “Theme Engine” 插件已不再兼容,为了继续自定义 WebUI 的样式,我开发了一个轻量级的 CSS 注入插件,并在此基础上发布了一套我个人使用的 “Modernization” 主题。

1. Custom WebUI CSS 插件

这是一个专门为 Unraid 7.2+ 设计的轻量级插件。我的需求很简单:将自定义 CSS 代码注入到网页中。既然旧插件无法使用,我便编写了这个替代方案。

主要功能:

  • 全局 CSS 注入: 将 CSS 代码自动加载到每个页面的 <head> 中。
  • 深色主题附加CSS: 可输入仅在 Unraid 主题设置为 ‘black’ 时加载的额外CSS,用于方便在黑/白主题间兼容和切换。
  • 额外资源文件: 支持从 Flash 到 Web 目录同步额外的静态资源(如背景图片等),可在CSS中引用。

项目地址: https://github.com/WuSiYu/unraid-custom-css

Unraid论坛地址:https://forums.unraid.net/topic/195276-plugin-simple-custom-webui-css-plugin-for-unraid-72

注意: 本插件仅在 Unraid 7.2+ 上测试通过。如果你使用的是 7.1.4 及以下版本,建议继续使用 Theme Engine。

2. Modernization Theme

配合上述插件,我整理了个人使用的 CSS 样式,命名为 Modernization Theme。

设计特点:

  • 现代化外观: 重新设计了 Dashboard 和 Main 等核心页面的视觉体验。
  • 按钮风格: 改善了Unraid原本比较丑的圆角渐变边框设计,现在具有完美的圆角,并在支持的浏览器上还有渐变文字。
  • 黑/白主题兼容: 适配 Unraid 原生的 White 和 Black 两种基础主题。
  • 稳健性: 克制了 CSS 的修改范围,尽量避免破坏第三方插件的显示效果。

3. 安装与使用指南

第一步:安装插件

进入 Unraid 的 PLUGINS -> Install Plugin 页面,输入以下 URL 进行安装:

https://raw.githubusercontent.com/WuSiYu/unraid-custom-css/refs/heads/master/custom.css.plg

(正在等待CA应用商店审核,不出意外等之后也可以直接在上面装)

第二步:输入主题CSS

  1. 安装完成后,前往 SETTINGS -> Custom WebUI CSS
  2. 打开下方链接,复制其中的 CSS 代码: https://github.com/WuSiYu/unraid-custom-css/blob/master/example.css
  3. 将代码粘贴到插件的配置框中,点击应用即可。

本主题有意限制了自定义样式的生效范围,以最大程度避免破坏第三方插件的样式。但如果你在使用过程中依然了遇到任何第三方插件的兼容性问题,欢迎反馈。

Unraid 7.2+ WebUI美化主题和自定义CSS插件最先出现在WuSiYu Blog

杂谈:libvirt/qemu Windows游戏VM的一些优化配置

作者 WuSiYu
2025年10月13日 01:01

一些libvirt配置备忘,针对Windows guest,虚拟机游戏场景

虚拟化功能选项

取自cockpit-machine生成的Windows 10 guest配置

  <features>
    <acpi/>
    <apic/>
    <hyperv mode='custom'>
      <relaxed state='on'/>
      <vapic state='on'/>
      <spinlocks state='on' retries='8191'/>
      <vpindex state='on'/>
      <runtime state='on'/>
      <synic state='on'/>
      <stimer state='on'/>
      <frequencies state='on'/>
      <tlbflush state='on'/>
      <ipi state='on'/>
      <evmcs state='on'/>
      <avic state='on'/>
    </hyperv>
    <vmport state='off'/>
  </features>

CPU feature设置

在CPU节加入一些神奇的选项可以提高CPU-Z跑分,在我的E-2378的单核从450提升到500,物理机为540左右

  <cpu mode='host-passthrough' check='none' migratable='on'>
    <topology sockets='1' dies='1' clusters='1' cores='8' threads='2'/> <!--以实际情况为准-->
    <feature policy='require' name='topoext'/>
    <feature policy='disable' name='vmx'/>
    <feature policy='disable' name='svm'/>
    <feature policy='require' name='apic'/>
    <feature policy='require' name='hypervisor'/>
    <feature policy='require' name='invtsc'/>
  </cpu>

超线程和 CPU Pinning

首先使用lscpu -e查看宿主机超线程CPU序号拓扑,一般是前半都是t0(每个物理核的0号线程),后半都是t1,然后将同一物理核心的宿主机CPU序号分配到相邻的vcpu上,似乎这样在Windows虚拟机中顺序才是正确的(有待进一步验证):

  <vcpu placement='static'>16</vcpu>
  <cputune>
    <vcpupin vcpu='0' cpuset='0'/>
    <vcpupin vcpu='1' cpuset='8'/>
    <vcpupin vcpu='2' cpuset='1'/>
    <vcpupin vcpu='3' cpuset='9'/>
    <vcpupin vcpu='4' cpuset='2'/>
    <vcpupin vcpu='5' cpuset='10'/>
    <vcpupin vcpu='6' cpuset='3'/>
    <vcpupin vcpu='7' cpuset='11'/>
    <vcpupin vcpu='8' cpuset='4'/>
    <vcpupin vcpu='9' cpuset='12'/>
    <vcpupin vcpu='10' cpuset='5'/>
    <vcpupin vcpu='11' cpuset='13'/>
    <vcpupin vcpu='12' cpuset='6'/>
    <vcpupin vcpu='13' cpuset='14'/>
    <vcpupin vcpu='14' cpuset='7'/>
    <vcpupin vcpu='15' cpuset='15'/>
  </cputune>

宿主机优化(可选)

tuned profile选择virtual-host

qemu自动用透明内存大页,也可以显示用手动内存大页,但不足时虚拟机无法启动

杂谈:libvirt/qemu Windows游戏VM的一些优化配置最先出现在WuSiYu Blog

OpenWrt One 路由器(MT7981,主线op)超频bl2编译教程

作者 WuSiYu
2024年12月13日 01:31

近日OpenWrt官方出了台MT7981的路由器,就是价格非常感人,不过看在它带RTC、POE、GPIO、nor/nand双启动、type-c转串口、还有M2 nvme槽(以及情怀)的份上还是买了一台玩玩。这机器最大的短板就是弱鸡的双核1.3G的MT7981,跑NAT + WiFi到千兆就几乎吃满了CPU(无offload情况),所以想着超个频。

前置知识:MT7981是一个ARMv8-A SoC,一般使用ARM Trusted Firmware(ATF)方式启动,启动顺序为:BL1(BOOT ROM)-> BL2 -> BL31 -> U-boot -> Linux Kernel,BL位于SoC内的ROM,本机从BL2开始位于外置SPI NAND Flash中(通过切换开关进入恢复模式时会使用SPI NOR Flash)。BL2包含了诸多SoC设备初始化代码,其中包含ARMPLL的初始化代码,其为CPU提供高速时钟(PLL初始化前CPU直接使用外部晶振的40MHz时钟)

参考了恩山论坛里的帖子:MT798X CPU超频1.65G/一键修改无线最大功率方法,不过里面的nand固件只有开NMBM(一种NAND Flash坏快管理机制)的,这台机器的主线OpenWrt不使用NMBM,所以用不了。于是自己研究了下,发在这里以供参考,其他没NMBM的nand理论上也能用(但请先确定自己的环境能成功救砖bl2再刷)。

我手里这台能从1.3GHz超到1.7GHz,跑源里的coremark能从4400分提高到5750分。之后再到1.76G时无线高负载时会崩溃重启,到1.8GHz时开机到一半就会死机,大概是这SoC的极限了,求稳可以只超到1.6GHz(此时coremark为5400分)。

方法简而言之就是修改bl2的pll初始化代码,源码位置:arm-trusted-firmware/plat/mediatek/mt7981/drivers/pll/pll.c (mtk-openwrt/arm-trusted-firmware),需要修改其中PLL的频率值和分频值

以下是详细步骤:

  1. 首先下载最新的OpenWrt 24.10源码,或者直接下载OpenWrt One原厂固件的源码包,里面自带config:https://mirror2.openwrt.org/sources/OpenWrt-One-sources-3098b4bf07.tar.xz
  2. 先编译一遍代码,确保能成功编译出openwrt-mediatek-filogic-openwrt_one-snand-preloader.bin这个文件,这个就是NAND Flash上的bl2(不要跟nor-preloader搞混,那个是给nor flash的,这机器的nor flash基本只在救砖的时候用),顺便也能把bl2的源码下载下来,供下一步使用
  3. 编译完后,这里偷个懒,直接编辑openwrt源码目录下的./build_dir/target-aarch64_cortex-a53_musl/arm-trusted-firmware-mediatek-mt7981-spim-nand-ubi-ddr4/arm-trusted-firmware-mediatek-2024.01.17~bacca82a/plat/mediatek/mt7981/drivers/pll/pll.c文件(注意这样直接编辑的话make clean后这个文件的改动就丢了,标准做法是写个patch,这里只是图省事),其他机型请根据实际情况修改路径中的spim-nand-ubi-ddr4,找到正确的软件包

    需要修改mtk_pll_init()函数其中的/* Set PLL frequency */这段,具体修改后的代码和解释如下:
 /* Set PLL frequency */
// 241208: overclock hack
NOTICE("THIS IS AN OVERCLOKING BL2!\n");
NOTICE("set arm freq to 1.60 GHz:\n");


// 设置频率值,SDMPLL fractional mode,这里只需修改高8位
// 原始为2分频,0x82 = 130,实际对应1.3GHz:
// mmio_write_32(ARMPLL_CON1, 0x82000000); /* 1.3G */)
// 由于这个值再大一些的话会设置失败,所以我们要使用1分频(不分频):
// 例如:0x50 = 80,此时对应1.6G; 0x55 = 85,对应1.7G,一次类推

mmio_write_32(ARMPLL_CON1, 0x50000000); /* 1.6G */
// mmio_write_32(ARMPLL_CON1, 0x55000000); /* 1.7G */
NOTICE("ARMPLL_CON1 = 0x%x\n", mmio_read_32(ARMPLL_CON1));

// 设置分频比,原始为2分频,还有个注释掉的4分频(用这个就是650MHz):
// //mmio_setbits_32(ARMPLL_CON0, 0x124); /* divider for 650M */
// mmio_setbits_32(ARMPLL_CON0, 0x114); /* divider for 1.3G */

// 修改为不分频,这个寄存器的4~6位(从0开始数)为分频值,由于默认是001(2分频),这里要将其清零(1分频),然后再设置其余bits
mmio_clrbits_32(ARMPLL_CON0, 0x70); /* divider for >1.3G */
mmio_setbits_32(ARMPLL_CON0, 0x104);
NOTICE("ARMPLL_CON0 = 0x%x\n", mmio_read_32(ARMPLL_CON0));

  1. 再次编译(直接make,不要clean),然后刷入bin/targets/mediatek/filogic/openwrt-mediatek-filogic-openwrt_one-snand-preloader.bin到nand中的bl2分区:
    • 对于OpenWrt One,可以用nor flash里带的插U盘刷固件方法(请参考该型号OP wiki页面
    • 对于其他设备,请在刷之前一定要保证有救砖手段,例如亲自试试自己电脑用mtk_uartboot能正常工作后再刷
    • 还可以在上传到现有系统的/tmp里直接刷:
opkg install mtd kmod-mtd-rw
insmod mtd-rw.ko i_want_a_brick
mtd write /tmp/openwrt-mediatek-filogic-openwrt_one-snand-preloader.bin bl2
mtd verify /tmp/openwrt-mediatek-filogic-openwrt_one-snand-preloader.bin bl2
reboot

下面是我编译的bl2:
对于OpenWrt One,可以直接刷入;
对于其他spim-nand-ubi-ddr4且非NMBM机型,也可以尝试直接刷入;
对于NMBM机型,请使用之前论坛里大佬发的超频bl2,也可以自己参考此方法编译,不要直接刷这个版本;

免责声明:由于刷bl2和超频有不小风险,请在刷之前一定要保证有救砖手段;同时超频会导致功耗增加,会增加供电电路的负荷,会有硬件损坏或者折寿的风险。

参考资料:https://mirror2.openwrt.org/docs/MT7981B_Wi-Fi6_Platform_Registers_Open_Part1_V1.0.pdf ,Page 310~311:

OpenWrt One 路由器(MT7981,主线op)超频bl2编译教程最先出现在WuSiYu Blog

白群晖之 在“不受支持”的机型上启用btrfs和快照功能

作者 WuSiYu
2024年7月4日 21:24

400多块买了个便宜的单盘位DS118白群晖当容灾备份机器用,到手发现这个机型居然不支持创建btrfs的存储空间,这就十分难绷了,毕竟数据校验、快照等功能都用不了。

然后发现,尽管最近一代的新品在售的机器都支持btrfs了,但直到2020年群晖NAS的很多低端型号都不支持btrfs,其中20、19、18系列就包括:DS420j,DS220j,DS120j,DS419slim,DS119j,DS418j,DS218play,DS218j,DS118

尽管这些机型的配置确实不高,但btrfs其实也不是会费太多资源的东西,DS118这种机型完全能够支持,毕竟同为RTD1296 CPU的DS218或者同为1G内存的机器DS216+都是支持btrfs的,这么搞大概只是为了差别对待。

实际上,群晖在这些型号上用的系统都是差不多的,底层其实都带有btrfs功能,只是在Web UI上被隐藏了,这些功能的差异被定义在了/etc.defaults/synoinfo.conf中,因此只需要打开SSH功能,连接后执行sudo vim /etc.defaults/synoinfo.conf,加入以下内容即可:

support_btrfs="yes"
support_btrfs_dedupe="yes"
support_iscsi_btrfs_lun="yes"
support_share_snapshot="yes"
support_share_quota="yes"
support_share_user_quota="yes"
support_dr_snap="yes"
support_dr_replica="yes"
max_btrfs_snapshots="65536"
max_snapshot_per_lun="256"
max_snapshots_per_share="1024"
max_dr_replica="64"
max_sdr_replica="64"

若不会使用vim,也可以使用winscp等软件。此时btrfs存储池便可以被创建和使用,但若想使用群晖的快照套件还需要继续操作。

快照功能

DSM 7的快照功能需要使用套件SnapshotReplication和其依赖ReplicationService,然而这两个套件默认无法在不支持btrfs的机型上安装,但我们可以通过暂时修改机型的方式来强行安装,然后修改软件包信息。

1. 首先找到一个相似架构且支持btrfs的机器,比如对于我的DS118,我找到了DS218,然后在 群晖官网 -> DS218下载中心 -> DSM 套件 中找到SnapshotReplication和ReplicationService的安装包,上传到home目录中。

2. 编辑/etc.defaults/synoinfo.conf,修改synobios和unique字段中的机型,例如synobios=”ds118″和unique=”synology_rtd1296_ds118″中的“ds118”改为“ds218”,然后保存。注意:修改机型后,一定要在稍后改回来,期间不要重启,否则系统会无法启动。

3. 安装软件包,在终端中执行(.spk文件的文件名按实际修改):

sudo synopkg install ReplicationService-ds218-1.3.0-0409.spk
sudo synopkg start ReplicationService
sudo synopkg install SnapshotReplication-ds218-7.4.5-1698.spk
sudo synopkg start SnapshotReplication

4. 重要:恢复synoinfo.conf中的机型修改

5. 此时套件虽然已经安装,但机型不匹配,会提示套件损坏,需要我们修改已安装软件包的信息,依次修改下面两个文件:

/var/packages/ReplicationService/INFO
/var/packages/SnapshotReplication/INFO

找到类似model="synology_rtd1296_ds218"的行,修改为机器的真实型号,然后重启,即可正常使用SnapshotReplication进行快照。

疑难解答

如果你这样做后依然无法创建快照,请尝试以下命令(homes为共享文件夹名称,可修改),观察快照能否被创建,或是否有报错:

sudo sh -x synosnapschedtask.sh local share homes

番外

玩过黑群晖的都知道,除了SA6400机型用的Linux内核版本是5.10.55(2021年发布)外,其他机型都还在使用上古4.4.302内核(2016年发布),然而在4.4老内核下,btrfs的压缩功能并不支持zstd算法,因此99%的白群晖NAS的文件系统压缩功能都是快速但效果较差的lzo,包括那些同样上万的机型(不100%确定,群晖毕竟对btrfs进行了不少魔改,也有可能backport了zstd的功能,其他机型的朋友可以看下synoinfo.conf是否包含btrfs_default_compression="zstd"的配置)。

对于黑群晖而言,也有多了一个用SA6400机型的理由。

白群晖之 在“不受支持”的机型上启用btrfs和快照功能最先出现在WuSiYu Blog

解决群晖Photos iOS客户端备份大视频时报错“备份已暂停-空间不足”

作者 WuSiYu
2024年6月24日 15:29

2024/07/24 UPDATE: 最新版的群晖Photos APP似乎已经修复/缓解了此问题

这是由于iOS设备(比如iPhone)的空间不足,使App无法转存较大文件导致的。在iCloud中照片/视频大小较大时,尽管在设置中可能会看到还有大量的剩余空间,但实际上是一种“已占用但可被释放”的状态。卸载一些大App或删除一些照片/视频能够解决,但如果不想这样做的话,可以使用下方的方法强制iOS去释放一些空间(iPhone 13 Pro测试可用):

手机打开 相机,切换到视频,开启“ProRes HDR”录制,此时会提示一个很短的“最长时间”和“正在释放资源”。等待一段时间后,当“最长时间”上升到5分钟左右时,群晖Photos即可正常工作。

解决群晖Photos iOS客户端备份大视频时报错“备份已暂停-空间不足”最先出现在WuSiYu Blog

备忘:OpenWrt在旁路由下Ping通但TCP不通的解决办法

作者 WuSiYu
2023年8月6日 04:49

这里的背景是OpenWRT作为旁路由(aka. 旁路网关,单臂路由,透明网关等),没有一个太准确的叫法,具体的指另一台“主路由”连接公网并作NAT,OpenWrt不启用NAT,用户设备的上行流量先经过OpenWrt旁路由转发到“主路由”,下行时流量直接通过由主路由发送到用户设备,不经过OpenWrt旁路由。主要优点在于下行带宽不会受OpenWrt性能的影响,且OpenWrt设备故障或维护时,可手动(或配置自动)切换到主路由。(OpenWRT旁路由可以是单臂路由,也可以有另一根独立的线路连接到主路由,避免带宽限制,如我之前的文章)

但由于上行和下行的通路不同,仅有部分流量会经过OpenWrt,会导致TCP的conntrack出现问题。

具体表现是:已经正确设置了路由,且能用户设备能ping通外网,但对外进行TCP不通,若在用户设备上抓包分析会看到:

  • (从用户设备,客户端)发送SYN
  • 收到(来自公网设备,服务器)SYN + ACK
  • 发送ACK
  • 发送通信内容(payload)
  • (未收到回应ACK)
  • 重复收到SYN + ACK(多次)
  • (重传)发送通信内容(payload)
  • (还是没有回应ACK)

不难看出,这是由于客户端发送的ACK在中途被丢弃了,使得虽然客户端认为TCP连接已经建立,但在服务端看来没有,因此客户端发送的payload不会被确认,而服务器则会重传SYN + ACK,继续等待客户端的ACK。

这种现象和OpenWrt的conntrack有关,由于服务器回应的SYN + ACK没有经过OpenWrt,使得OpenWrt没有正确追踪TCP连接。在一些特定情况下,OpenWrt认为客户端后续的数据包是“无效数据包”,并丢弃,包括客户端发送的ACK。

在OpenWrt中解决的方法也很简单,首先在“防火墙”页面中关闭“丢弃无效数据包”

其次,若OpenWrt通过独立线路(在防火墙的WAN Zone)连接主路由,则在一些特定情况下(比如因为一些组网需求,对一些内网网段开启了masquerade),还要打开WAN Zone – 编辑 – 连接追踪设置 – 允许“无效”流量,如下图:

具体conntrack的工作方式和该问题的出现条件,有待进一步验证

备忘:OpenWrt在旁路由下Ping通但TCP不通的解决办法最先出现在WuSiYu Blog

论文简读:Benanza: 自动产生μBenchmark以测试GPU上深度学习模型的Latency下界和优化空间

作者 WuSiYu
2023年3月2日 01:05

论文全名:Benanza: Automatic μBenchmark Generation to Compute “Lower-bound” Latency and Inform Optimizations of Deep Learning Models on GPUs

原文链接:https://arxiv.org/pdf/1911.06922.pdf

这周在组会上介绍了这篇ML sys的论文,正好来写成一篇博客。这篇文章他是发表在IPDPS 2020上的,不算新,但其中分析总结的若干在GPU上的优化策略还是有一定参考性。同时本文也是本博客第一篇关于高性能计算(HPC)和深度学习系统(ML sys)的博客。

这篇文章设计了一种layer-level的profiling和analyze工具,叫做Benanza,它提出了一个“lower-bound” latency的概念,和实际测量的latency对比,用于分析深度学习推理框架的潜在优化空间。同时用BR(Benanza Ratio)表示优化程度(BR = lower-bound latency / measured latency),越接近1表示优化程度越高。

这个“lower-bound” latency是通过将原模型的每一个算子,都是用cudnn和cublas中最优的kernel去跑,所得到的时间。具体的将(结合下图),它的benchmark流程(下图黑色箭头)接受一个ONNX格式的模型,解析并找出一系列不重复的算子(包括算子类型和shape),然后在cudnn和cublas中找到最优的实现,并将结果存入benchmark数据库。之后它的analyze流程(下图红色箭头)会首先让这个模型在某个框架中实际跑一下,记录其性能(measured latency),然后将其所有算子在benchmark数据库中的时间取出并求和,作为“lower-bound” latency,与measured latency做对比,反应其在各个角度的优化空间。

Benchmark流程

对于模型中的每个layer,它会生成一些列micro benchmark,同时也会涉及一些可能的优化手段,包括:

1. 算法选择:对于CNN中的卷积而言,会有很多种可用的算法,包括Implicit GEMM (IGEMM), Implicit PreComputed GEMM (IPGEMM), GEMM, Direct (DRCT), FFT, Tiled FFT (TFFT), Winograd (WING), and Winograd Non-Fused (WINGNF)等,Benanza会把他们都跑一遍,选择一个最优的。

2. 数据类型:Benanza跑了fp32和fp16下的对应kernel,对于fp16来讲,还分别跑了用和不用tensorcore的两种情况。

3. 算子融合:Benanza也做了算子融合的尝试,主要利用的是cudnn的ConvolutionBiasActivationForward API。

以上产生的micro benchmark中最后都会被存在工具的benchmark数据库中。

Analysis流程

这里首先会让ONNX模型在一个现有的推理框架中运行,paper中跑了MXNet,ONNX Runtime和Pytorch,然后发现MXNet的性能普遍最好(见下图),后续也默认以MXNet的测量值作为measured latency。

接下来,结合各个优化点,对测量值和lower-bound latency进行比较:

1. 并行算子执行 vs 串行算子执行 的latency

对于一些模型,某些算子之间不存在数据依赖,是可以并行执行的,这里他假设并行情况下,算子可以以任意程度并行,最终整体的lower-bound latency是取单一的最长路径上的latency之和(下图红线):

(这个我感觉不是很靠谱,现实中并行跑多个kernel肯定会影响latency,导致他这种算法的参考意义也不大)

并行和串行的对比如下:

2. batch size和系统比较

他们对比了不同batch size下和在不同的架构的Nvidia GPU上的性能,他们观察到的比较有趣的一点是,系统的优化程度上:较新的架构 > 最新的架构 >> 旧架构。

3. 卷积算法选择

前面提到,卷积算法有很多种,会有不同的性能特性,比如winograd算法能2~3倍的降低运算量,不同算法的访存特性也会不同。他们对比了框架所选择的卷积算法和最有的卷积算法,发现框架的选择并不一定是最优的:

框架中一般都是通过cudnn的GetConvolutionForwardAlgorithm API来选择卷积算法,其本身是一个启发式的算法,相比实测,自然可能不是最优的。

4. 框架本身的开销

这里他们发现并优化了一些框架中不高效的地方,包括MXNet ONNX Model Loader中不必要的 image_2d_pad_constant_kernel,和PyTorch cuDNN Wrapper中不必要的 cudaStreamWaitEvent。

5. 算子融合

他们发现MXNet、ONNX Runtime和PyTorch进行推理时都没有做算子融合,而它们假设在ResNet50中做了一个简单的Conv -> Bias算子融合,可以得到一定的性能提升

6. Tensor Core的利用

较新的Nvidia GPU上都有tensorcore,是专为深度学习优化的计算单元(主要是推理),支持fp16等较低的精度,可以高效的进行矩阵乘法等操作,FLOPS比常规单元高很多。因此如果推理能利用tensorcore,性能会有挺大的提升:

另外他们还发现NHWC格式要比NHWC数据排布格式快一些,这也是Nvidia所建议的格式。

最后,如果综合上述所有优化手段,带来的提升如下:

个人评价

这篇文章提出的分析工具还是挺有意思的,结合了模型的分析和实际的profiling,同时也算是总结了一遍目前在GPU推理框架上的常见的优化点。就他们所说,他们的主要贡献是在DL Benchmarking上提出了这套生成micro-benchmarks的方法,并在Performance Advising上提出了针对DL领域的优化指导。

但是我个人还是对他们的工作有一些疑问,首先他们只在MXNet、ONNX Runtime和Pytorch上进行了推理性能的实测,但这些框架并不是目前认为的最高效的推理框架。比如在Nvidia GPU上,tensorrt显然是更加高效的,也是目前工业级部署常用的运行时。对于tensorrt而言,文章中很多优化操作都是倍实现了的,甚至要比文章中还要多和好,比如tensorrt能进行比较好的算子融合,也会选择最优的卷积算法,更不用说对于自家tensorcore的支持。如果用了tensorrt作为实测的框架,可能就测不出什么优化空间了,甚至比他的“lower-bound” latency还要好都是有可能的。当然对于学术界在Nvidia tensorrt上确实也没什么可优化的了,不可能卷的过Nvidia自己,因此他这套方案对于其他运行时,乃至于迁移到其他的GPU、DLA等平台,还是有他的意义的。

另外文章中对于“并行执行算子”的优化空间的计算比较草率,上文也有提到。

论文简读:Benanza: 自动产生μBenchmark以测试GPU上深度学习模型的Latency下界和优化空间最先出现在WuSiYu Blog

RISC-V GNU Toolchain 开发环境搭建Quickstart

作者 WuSiYu
2021年9月13日 17:14

完整的开发环境需要构建gcc、binutils、glibc等组件,以及qemu、gdb等辅助工具,他们都分布在不同的repo中。比起手动配置,一种更简单的方法是使用https://github.com/riscv-software-src/riscv-gnu-toolchain,其已通过submodule的方式引入好了这些组件。

0x00 环境配置

这里使用最常见的Ubuntu 20.04作为演示,其他环境可以参考riscv-gnu-toolchain中的README。

sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev

如果需要跑测试,还要安装expect软件包

然后clone该仓库:

git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain

如果你正在参与工具链中的开发/测试,你或许希望切换其中的一些组件到一些正在进行开发工作的下游仓库中,比如下面是一个切换riscv-gcc到https://github.com/WuSiYu/riscv-gcc仓库的例子:

cd riscv-gcc
git submodule update .   #如果未初始化submodule(该目录为空),则执行
git remote add wu git@github.com:WuSiYu/riscv-gcc.git
git remote update
git checkout --track wu/riscv-gcc-experiment   #切换到你需要的分支

其他组件同理

0x01 编译

在riscv-gnu-toolchain目录下使用./configure进行配置,详细参数含义可以参考./configure -h,multilib有时会导致问题,所以建议一次只编译一种架构,存放在不同的prefix里,一种保险的写法是:

./configure --prefix=/whatever/you/want/ --with-arch=<ARCH> --with-abi=<ABI> --with-multilib-generator="<ARCH>-<ABI>--"

替换<ARCH>为你需要的架构,如:rv32i、rv32gbk、rv64g

替换<ABI>为你需要且与架构匹配的ABI,如:ilp32、ilp32d、lp64d

然后执行make newlib编译newlib的工具链(裸机程序),或者执行make linux编译产生linux可执行文件的工具链。通常我们测试工具链的话使用newlib版就可以。

同时你或许希望记录时间、多核编译、记录输出,可以使用类似以下的指令:

time make newlib -j<YOUR_CPU_CORE_COUNT> 2>&1 | tee build.log

替换<YOUR_CPU_CORE_COUNT>为你的CPU线程数,比如你有一个10c20t的i9-10850k硅渣,就使用-j20

然后你就可以使用你编译出的工具链了,比如/whatever/you/want/bin/riscv64-unknown-elf-gcc

0x02 测试

使用类似如下的指令进行测试

make report-newlib SIM=qemu

SIM=表示使用的测试程序运行环境,可选的值有qemu、gdb、spike

备注:如果你使用qemu且需要自定义调用qemu程序时的命令行参数,可以编辑riscv-gnu-toolchain/scripts/wrapper/qemu/riscv64-unknown-elf-run脚本进行临时修改

RISC-V GNU Toolchain 开发环境搭建Quickstart最先出现在WuSiYu Blog

❌
❌