前言

合上盖子,再打开,右上角的 Wi-Fi 图标还在,点开却是一片空白。过一会儿网络自己回来,也可能一直不回来。另一件更让人难受的事是,锁屏界面只显示壁纸和时钟,密码输入框不见了,只能盲按回车或者强行重启。

这两种现象出现在同一台机器上,看起来像两个问题,实际分别落在 PCIe 电源管理和 GNOME 登录认证两条链路上。本文记录排查过程、已经做的修改、验证方法和回滚方式。

测试机器:

项目 值
机器 ALLDOCUBE iWork GT Ultra
平台 Intel Meteor Lake
系统 Arch Linux,GNOME 51.1 / GDM 51.1.1,Wayland
内核 7.2.14-arch1-1
无线网卡 Realtek RTL8852BE,驱动 rtw89_8852be
睡眠方式 s2idle

现象

Wi-Fi 的问题有几个共同点:

  • 熄屏或挂起后唤醒,Wi-Fi 有时直接掉线,NetworkManager 里设备变成 unavailable;
  • GNOME 右上角的 Wi-Fi 列表为空,点不开任何网络;
  • 有时等十几秒会自己恢复,有时只能重启 NetworkManager 或重启系统;
  • 用网线、USB 网卡都不受影响,问题只出现在这块 RTL8852BE 上。

锁屏的问题更随机:

  • 唤醒后只看到背景和时钟,没有密码输入框;
  • 动一下鼠标、按几下键盘,偶尔能恢复,偶尔一直不出现;
  • 不是每次锁屏都复现,和 Wi-Fi 是否掉线没有严格对应关系。

Wi-Fi:PCIe 链路没有回来

日志

先看内核日志。问题发生时,几个关键错误会连在一起出现:

1
2
3
4
5
6
pcieport 0000:00:06.0: Data Link Layer Link Active not set in 100 msec
rtw89_8852be 0000:01:00.0: failed to write DBI register, addr=0xB48
rtw89_8852be 0000:01:00.0: xtal si not ready(W): offset=90 val=10 mask=10
rtw89_8852be 0000:01:00.0: mac preinit fail, ret: -110
NetworkManager: wpa_supplicant couldn't grab this interface
gnome-shell: NM.DeviceError: Scanning not allowed while unavailable

这几行日志对应的顺序是:

  1. 0000:00:06.0 是 Wi-Fi 卡所在的 PCIe 根端口。系统从 s2idle 唤醒时,根端口在 100 ms 内没有进入 Data Link Layer Active 状态。
  2. 链路没起来,rtw89_8852be 访问网卡寄存器就会超时,failed to write DBI register 和 xtal si not ready 都是这个阶段的表现。
  3. 驱动初始化失败,返回 -110,设备进入不可用状态。
  4. NetworkManager 拿不到可用接口,wpa_supplicant 无法绑定,GNOME 自然也无法扫描,Wi-Fi 列表就是空的。

这里有一个容易误判的地方:桌面上看到的是 Wi-Fi 掉线,但 Wi-Fi 省电策略只是诱因之一,根因在 PCIe 链路训练和根端口电源管理。只改 Wi-Fi 省电,可能让问题少出现几次,不能保证不再出现。

先确认设备和根端口

不同机器的 PCI 地址不一样,照抄命令之前先确认自己的设备:

1
2
lspci -nnk | grep -A3 -i 'network\|wireless'
lspci -tv

输出里会看到无线网卡和它上面的根端口。本文的机器是:

1
2
3
根端口:0000:00:06.0
无线网卡:0000:01:00.0
接口名:wlp1s0

接口名可以用 ip link 或 nmcli device status 确认。后面的配置都要按自己的地址替换,不能直接照搬。

第一层:关闭 Wi-Fi 省电

先排除最常见的省电因素。新建 NetworkManager 配置:

1
2
3
# /etc/NetworkManager/conf.d/99-wifi-powersave-off.conf
[connection]
wifi.powersave = 2

2 表示关闭 Wi-Fi 省电。保存后让配置生效:

1
sudo systemctl reload NetworkManager

当前连接也可以单独设置:

1
2
nmcli connection modify "<你的连接名>" 802-11-wireless.powersave 2
nmcli connection up "<你的连接名>"

确认网卡当前的省电状态:

1
iw dev wlp1s0 get power_save

应输出:

1
Power save: off

这一步只能减少唤醒时的电源状态切换,不能修复 PCIe 链路训练失败。如果日志里仍然出现 Data Link Layer Link Active not set,继续往下做。

第二层:调整 rtw89 驱动参数

给 rtw89_pci 和 rtw89_core 加参数:

1
2
3
# /etc/modprobe.d/rtw89.conf
options rtw89_pci disable_clkreq=1 disable_aspm_l1=1 disable_aspm_l1ss=1
options rtw89_core disable_ps_mode=1

这几个参数分别关闭时钟请求、ASPM L1、L1 子状态和驱动内部的电源管理模式。它们针对的是唤醒阶段网卡和 PCIe 链路的低功耗状态,不是日常吞吐性能。

重新生成 initramfs,让参数在尽早的阶段生效:

1
sudo mkinitcpio -P

如果模块已经加载,可以临时卸载再加载做一次验证:

1
2
sudo modprobe -r rtw89_8852be rtw89_pci rtw89_core
sudo modprobe rtw89_8852be

卸载无线模块会断开当前网络,远程操作时不要直接执行。参数是否生效,可以在重启后查:

1
2
3
4
cat /sys/module/rtw89_pci/parameters/disable_aspm_l1
cat /sys/module/rtw89_pci/parameters/disable_aspm_l1ss
cat /sys/module/rtw89_pci/parameters/disable_clkreq
cat /sys/module/rtw89_core/parameters/disable_ps_mode

四个值都应为 Y。

第三层:关闭 PCIe 根端口电源管理

如果驱动参数还不够,问题通常还在根端口的运行时电源管理。把参数加到内核启动项:

1
pcie_port_pm=off pcie_aspm=off

我使用的是 systemd-boot,编辑:

1
2
/boot/loader/entries/arch.conf
/boot/loader/entries/arch-lts.conf

在 options 行末尾追加这两个参数。示意如下,UUID 用占位符代替:

1
options root=UUID=<根分区UUID> rw resume=UUID=<交换分区UUID> quiet loglevel=3 nowatchdog pcie_port_pm=off pcie_aspm=off

改完后重启。确认参数已经进入内核命令行:

1
cat /proc/cmdline

输出里应包含:

1
pcie_port_pm=off pcie_aspm=off

这两个参数的作用不同:

  • pcie_port_pm=off 关闭 PCIe 端口的运行时电源管理,避免根端口在唤醒后无法重新建立链路。
  • pcie_aspm=off 全局关闭 PCIe ASPM,作为兜底。

代价也要说清楚:关闭 ASPM 后 PCIe 省电会变差,续航可能略有下降;pcie_port_pm=off 影响的不只是无线网卡,而是所有 PCIe 端口的运行时电源管理。它们是排障用的保守配置,不是所有机器都该长期开启。

如果使用 GRUB,不要改 systemd-boot 的 entry,应该改 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT,再执行:

1
sudo grub-mkconfig -o /boot/grub/grub.cfg

第四层:唤醒后的兜底恢复

即使前面三层都做了,偶尔还是可能遇到链路没有恢复。可以加一个 systemd sleep hook,在唤醒后检查接口,必要时重新训练 PCIe 链路。

我最初把脚本放在:

1
/usr/lib/systemd/system-sleep/rtw89-resume.sh

这个目录属于软件包管理范围,系统升级有可能被覆盖。更稳妥的位置是:

1
/etc/systemd/system-sleep/rtw89-resume.sh

脚本只处理 post 阶段,逻辑是:

  1. 等待 3 秒,给驱动和 NetworkManager 一点恢复时间;
  2. 检查 wlp1s0 是否已经处于 connected 或 disconnected;
  3. 如果设备仍不可用,对根端口触发 PCIe 链路重训练;
  4. 移除并重新扫描无线网卡;
  5. 如果 PCI 设备回来了但驱动没有绑定,再执行 modprobe rtw89_8852be;
  6. 最多重试 3 次,过程写入 journal,tag 为 rtw89-resume。

脚本必须可执行:

1
sudo chmod 755 /etc/systemd/system-sleep/rtw89-resume.sh

查看执行记录:

1
journalctl -t rtw89-resume -b --no-pager

这里的 PCIe 链路重训练是兜底手段,不是根因修复。它通过写根端口的 Link Control 寄存器触发重新训练,再让系统重新枚举网卡。不同主板的根端口和寄存器行为可能不一样,脚本里的 PCI 地址必须换成自己的,执行前先确认 lspci 输出。

锁屏:GNOME Shell 走了 SSSD 分支

日志

锁屏不显示密码框时,journal 里有这样的 GNOME Shell 报错:

1
2
3
4
JS ERROR: TypeError: can't convert undefined to object
_findItems@resource:///org/gnome/shell/gdm/authMenuButton.js:231
_selectAuthMechanism@resource:///org/gnome/shell/ui/unlockDialog.js:817
_handleOnServiceUnavailable@resource:///org/gnome/shell/gdm/authServicesSSSDSwitchable.js:376

这台机器没有安装 sssd,但 GNOME Shell 51.1 在锁屏和唤醒时仍然进入了 authServicesSSSDSwitchable 这条可切换认证路径。认证服务不可用后,_findItems 拿到 undefined,解锁对话框的密码输入区域没有正常渲染。

所以密码本身没有问题,键盘也有响应,问题出在认证界面:认证服务不可用时,解锁对话框抛了异常。

关闭可切换认证

用户会话里先关掉可切换认证和指纹认证:

1
2
gsettings set org.gnome.login-screen enable-switchable-authentication false
gsettings set org.gnome.login-screen enable-fingerprint-authentication false

GDM 登录界面读的是系统级 dconf,不是当前用户的 dconf。再写一份 GDM 配置:

1
2
3
# /etc/dconf/db/gdm.d/01-switchable-auth
[org/gnome/login-screen]
enable-switchable-authentication=false

然后更新 dconf 数据库:

1
sudo dconf update

当前认证设置可以用下面的命令核对:

1
2
3
4
gsettings get org.gnome.login-screen enable-password-authentication
gsettings get org.gnome.login-screen enable-fingerprint-authentication
gsettings get org.gnome.login-screen enable-smartcard-authentication
gsettings get org.gnome.login-screen enable-switchable-authentication

预期结果是密码认证为 true,指纹、智能卡、可切换认证为 false。

这台机器本来就没有安装 fprintd,关闭指纹认证不会影响现有登录方式。如果以后要用指纹或智能卡,需要重新开启对应选项并安装相应软件。修改锁屏设置不需要重启,下一次锁屏就会生效。

验证

Wi-Fi 参数

重启后逐项确认:

1
2
3
4
5
6
7
8
cat /proc/cmdline

cat /sys/module/rtw89_pci/parameters/disable_aspm_l1
cat /sys/module/rtw89_pci/parameters/disable_aspm_l1ss
cat /sys/module/rtw89_pci/parameters/disable_clkreq
cat /sys/module/rtw89_core/parameters/disable_ps_mode

iw dev wlp1s0 get power_save

期望结果:

  • /proc/cmdline 包含 pcie_port_pm=off pcie_aspm=off;
  • 四个 rtw89 参数都是 Y;
  • Power save: off。

挂起和唤醒

手动触发一次挂起:

1
systemctl suspend

唤醒后等 5 到 10 秒,再检查:

1
2
3
nmcli device status
journalctl -t rtw89-resume -b --no-pager
journalctl -b -k | grep -E 'pcieport 0000:00:06.0|rtw89|0000:01:00.0' | tail -40

期望结果:

  • wlp1s0 是 connected,至少也应是 disconnected,不能长期停在 unavailable;
  • journalctl -t rtw89-resume 有记录,但没有反复重试失败;
  • 内核日志里不再出现 Data Link Layer Link Active not set。

一次成功不代表稳定。建议连续做十次以上合盖、熄屏、手动挂起,分别记录成功次数和失败时的日志。只有多次唤醒都不再掉线,才能说这个问题基本解决。

锁屏

锁屏后唤醒,确认密码输入框正常显示。再测试这几种路径:

  • 手动 Super+L 锁屏后立即唤醒;
  • 熄屏后等待几分钟再唤醒;
  • 挂起后唤醒;
  • 插拔电源、切换电池供电后再锁屏。

如果某一种路径仍然不显示密码框,先看 journal 里有没有同一条 authServicesSSSDSwitchable.js 报错,再决定是否继续调整 GDM 配置。

回滚

这些修改都留有回滚路径。

关闭 Wi-Fi 省电的配置:

1
2
3
sudo rm /etc/NetworkManager/conf.d/99-wifi-powersave-off.conf
nmcli connection modify "<你的连接名>" 802-11-wireless.powersave 0
sudo systemctl reload NetworkManager

rtw89 驱动参数:

1
2
sudo rm /etc/modprobe.d/rtw89.conf
sudo mkinitcpio -P

PCIe 启动参数:

1
2
sudo cp /boot/loader/entries/arch.conf.bak.<时间戳> /boot/loader/entries/arch.conf
sudo cp /boot/loader/entries/arch-lts.conf.bak.<时间戳> /boot/loader/entries/arch-lts.conf

唤醒脚本:

1
sudo rm /etc/systemd/system-sleep/rtw89-resume.sh

锁屏认证设置:

1
2
3
4
gsettings reset org.gnome.login-screen enable-switchable-authentication
gsettings reset org.gnome.login-screen enable-fingerprint-authentication
sudo rm /etc/dconf/db/gdm.d/01-switchable-auth
sudo dconf update

内核启动参数、驱动参数和唤醒脚本需要重启后生效,锁屏设置下一次锁屏生效。

还没解决的问题

这套修改的目标是让机器可用,不是证明内核驱动没有 bug。还有几件事没有闭合:

  • pcie_aspm=off 和 pcie_port_pm=off 是全局关闭,续航和功耗会有代价,长期是否值得需要自己权衡;
  • 唤醒 hook 的 PCIe 链路重训练依赖具体根端口,换机器不能照抄地址;
  • 当前睡眠方式是 s2idle,可以尝试 mem_sleep_default=deep 切到 S3,但部分 Intel 平台对 S3 支持不完整,测试前保存工作;
  • 如果更新 BIOS 或内核后问题仍然存在,应该把 journalctl -b -k、lspci -vv -s 00:06.0、lspci -vv -s 01:00.0 一起反馈给 linux-wireless 和 rtw89 维护者;
  • 写这篇文章时,Wi-Fi 和锁屏都还需要更多次唤醒验证,当前只能确认参数已经生效,不能承诺彻底不再复现。

如果你也遇到类似现象,建议按“先确认 PCI 地址,再关省电,再加驱动参数,再加内核参数,最后才上唤醒 hook”的顺序做。每加一层就单独测试,出问题时才能知道是哪一层带来的变化。