内存焊在主板上了,坏了只能把坏地址从内核里挖掉继续用
Ben Hardill 的 X1 Carbon 开始频繁崩标签页、根分区隔三差五变成只读。memtest86+ 跑了几分钟就报 7919 个错误,而内存是焊死在主板上的,换不了,最后是用 memmap 把出问题的地址段从内核里圈出去,让这台机器继续跑。
中文
复制

现代笔记本把内存焊死在主板上的趋势实在缺德,尤其是在眼下这种缺货导致价格高企的艰难时期。
我平时用两台笔记本,一台处理私事,一台处理工作,试图以此把两者分开,好让我下班时能把工作那台留在“办公室”里。2021 年拿到现在这台工作笔记本时,我写过一篇博客。
这台 Lenovo X1 Carbon 一直很好用,直到最近我发现浏览器标签页崩溃的次数明显变多,而且时不时根文件系统会把自己标记成只读,这实在不怎么好用。
Oct 15 23:33:35 razor-crest kernel: BTRFS warning (device dm-0): csum failed root 257 ino 166754 off 5859786752 csum 0x31019647 expected csum 0x6d009929 mirror 1
Oct 15 23:33:35 razor-crest kernel: BTRFS error (device dm-0): bdev /dev/mapper/luks-571c7357-a343-49f3-993c-167c8c5dcec6 errs: wr 0, rd 0, flush 0, corrupt 3856936704, gen 0
这些故障让我怀疑是不是内存出了问题,于是我把 memtest86+ 的启动镜像加进 Ventoy U 盘,从它启动。
Memtest86+
这个工具会启动一个极其精简的操作系统,然后向尽可能多的物理内存地址写入一系列测试模式,再读回来比对。如果读回的值和写入的不一致,就说明有问题。
放着不管它会跑多轮,因为有些错误只是间歇性出现。但这次它几乎立刻就报出了一堆错误。
放在过去,这本来很好解决:输出足以定位是哪条 DIMM 出了问题,最坏的情况也不过是拔掉一条再跑一遍测试,做个二分查找。确定之后,订一条替换的 DIMM(或者趁机升级)就能继续用。
但为了把笔记本做得更薄(一个 DIMM 插槽大约要多占 6mm 厚度),或许还出于一种不太善意的考虑——让任何故障都变成必须买一台新笔记本——现在大多数笔记本的内存都直接焊在主板上(FrameWork 这类是例外),除非你擅长更换 BGA 芯片,否则基本没法更换。
按 5 年折旧再加上技术进步来算,现在其实差不多该换新笔记本了,但我想看看有没有办法让这台 Lenovo X1 Carbon 继续用下去——它依然是一台很能打的机器,还能顶替我当个人笔记本用的那台更老的 Dell XPS 13。
替换的事我之后再写。
Memmap
等新笔记本到货的这段时间,我想看看能不能让它更稳定一些。
稍微查了一下,找到两个可以传给 Linux 内核、让它避开坏扇区的选项:
- badram
- memmap
badram 原来是一个树外补丁,通常只在基于 Debian/Ubuntu 的发行版里提供。这台机器上我跑的是 Fedora,所以这条路走不通。
memmap 是内核的核心功能,Fedora 上应该可用。需要在 kernel 命令行上按 length$offset 的格式提供一组内存地址和长度。
Memtest86+ 能输出正确的格式(badram 和 memmap 都行),甚至能存成文件,但我用 Ventoy U 盘上那个 iso 镜像怎么也跑不起来,只好对着屏幕拍照。

然后只能手动抄录,真是“有趣”。
所有关于添加 kernel 命令行参数的文档都说,编辑 /etc/default/grub 文件,把值加到 GRUB_CMDLINE_LINUX 条目里,然后运行 grub2-mkconfig -o /boot/grub2/grub.cfg
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.luks.uuid=luks-571c7357-a343-49f3-993c-167c8c5dcec6 rhgb quiet memmap=\$memmap_val memmap=0x30$0x3fe90140,0x48$0x3fe90b08,0x48$0x3fe90ba8,0x48$0x3fe916a8,0xf0$0x3fe91c00,0x90$0x3fe92560,0xf0$0x3fe92f00,0x50$0x3fe93200,0x90$0x3fe93260,0xf0$0x3fe93800,0xf0$0x3fed4300,0x70$0x3fed4900,0x70$0x3fed4980,0xf0$0x3fed5400,0xf0$0x3fed5e00,0xf0$0x3fed6700,0xf0$0x3fed6d00,0xf0$0x3fed7000,0x30$0x3fed7a00,0xb0$0x3fed7a40"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
我照做了,但重启后机器起不来。
我设法进了 grub 界面,查看它试图引导的 kernel 命令行,看起来 memmap 条目里的 $0 被替换成了 grub2-mkconfig。这很像典型的环境变量替换($0 通常是正在运行的脚本名)。
我找到了 RedHat 的这个 bug,描述的是类似问题,解决办法是把值作为变量添加。
# grub2-editenv /boot/grub2/grubenv set memmap_val='0x30\$0x3fe90140,0x48\$0x3fe90b08,0x48\$0x3fe90ba8,0x48\$0x3fe916a8,0xf0\$0x3fe91c00,0x90\$0x3fe92560,0xf0\$0x3fe92f00,0x50\$0x3fe93200,0x90\$0x3fe93260,0xf0\$0x3fe93800,0xf0\$0x3fed4300,0x70\$0x3fed4900,0x70\$0x3fed4980,0xf0\$0x3fed5400,0xf0\$0x3fed5e00,0xf0\$0x3fed6700,0xf0\$0x3fed6d00,0xf0\$0x3fed7000,0x30\$0x3fed7a00,0xb0\$0x3fed7a40'
把 /etc/default/grub 改成
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.luks.uuid=luks-571c7357-a343-49f3-993c-167c8c5dcec6 rhgb quiet memmap=\$memmap_val"
GRUB_DISABLE_RECOVERY="true"
GRUB_ENABLE_BLSCFG=true
再运行一次 grub2-mkconfig -o /boot/grub2/grub.cfg。
这次正常启动了,在 /proc/cmdline 里能看到正确的值
BOOT_IMAGE=(hd0,gpt2)/vmlinuz-7.2.11-200.fc44.x86_64 root=UUID=869b183b-b858-40e5-8da7-e923dd367c2a ro rootflags=subvol=root rd.luks.uuid=luks-571c7357-a343-49f3-993c-167c8c5dcec6 rhgb quiet memmap=0x30$0x3fe90140,0x48$0x3fe90b08,0x48$0x3fe90ba8,0x48$0x3fe916a8,0xf0$0x3fe91c00,0x90$0x3fe92560,0xf0$0x3fe92f00,0x50$0x3fe93200,0x90$0x3fe93260,0xf0$0x3fe93800,0xf0$0x3fed4300,0x70$0x3fed4900,0x70$0x3fed4980,0xf0$0x3fed5400,0xf0$0x3fed5e00,0xf0$0x3fed6700,0xf0$0x3fed6d00,0xf0$0x3fed7000,0x30$0x3fed7a00,0xb0$0x3fed7a40
我在 U 盘上建了一个文本文件,存下 memmap 的值,这样万一彻底重装还能重新应用。这应该能撑到新笔记本到货,希望还能再撑久一点。
接下来的任务是确认需要备份的东西都备份好了,然后运行 btrfs check --fix,看看能不能把文件系统里的元数据理顺。