我设计的内核模块pstore/blk及其派生pstore/zone,mtdpstore最终在v5.8-rc1版本合并到torvalds/linux.git中(见参考链接[1]),发现国内外对pstore的介绍很少,所以就来一波科普。简介pstore文件系统(没错,这就是文件系统)是PersistentStorage的缩写。它最初由TonyLuck于2010年设计,并合并到Linux的主要分支中。设计初衷是在内核Panic/Oops(log_buf)时自动转储内核日志,在Panic重启后将转储的日志以文件的形式呈现给用户空间,用于分析内核崩溃问题。这对于分析概率很小,现场抓不到的问题非常有用,尤其是现在智能互联网设备越来越普及。远程设备可以自行捕获崩溃日志,并通过网络传输到服务器。可以根据收集到的日志定位和解决问题,然后通过OTA让设备迭代升级。根据网上搜索的资料,在pstore文件系统之前其实还有很多类似的实现。apanicAndroid是最早记录panic信息的方案。在linux2.6的Android内核中发现,但是没有提交到社区,后来废弃维护。在网上找不到放弃的理由。我猜是因为它只适用于mtdnand,但是现在Android基本都是用emmc。apanic应该是AndroidPanic的缩写,它可以在内核崩溃时将日志传输到mtdnand。这里的Ramoops指的是最早的ramoops实现。最新的代码已经集成到pstore中,以pstore/rambackend的形式存在。Ramoops可以将日志dump到重启后不掉电的ram上。这里对ram有一点要求,即使重启ram数据也不能丢失。crashlog这是openwrt提供的内核补丁,还没有提交给内核社区。它也是基于ram的,只能转储Panic/Oops日志。mtdoopsMTD子系统支持的功能与pstore非常相似。它只支持转储Panic/Oops日志,不能以文件形式呈现。用户需要自己解析整个MTD分区。(因为功能相似,我实现了mtdpstore来代替mtdoops)kdump如果说pstore是一个轻量级的内核崩溃日志转储解决方案,那么kdump就是一个重量级的问题分析工具。当发生崩溃时,kdump会生成一个用于捕获当前信息的内核,它将所有内存信息收集到转储核心文件中。重新启动后,捕获的信息将保存在特定文件中。类似的还有netdump和diskdump。kdump方案适用于服务器等资源量大的设备,功能也很强大,但对嵌入式设备很不友好。经过长期迭代,pstore不仅可以转储Panic/Oops日志(dmesg前端),还支持pmsg、console、ftrace前端。除了pstore/rambackend,还有我设计的pstore/blkbackend,除了支持转ram外,还有块设备和mtd设备。pstore前端是指dump日志的类型,pstore后端是指dump到的设备类型。目前支持以下前端:dmesg:主要是在转储Panic/Oops时将内核日志转储到log_buf中pmsg:提供用户空间存储日志的入口。在Android中,可以看到用来存放系统的日志。console:terminallogftrace:functiontraceinformation目前支持以下后端:pstore/ram:PersistentRam,重启后不会丢失数据的内存pstore/blk:(v5.8之后的版本)所有可写的块设备,比如磁盘,U盘、emmc、NFTLnand等mtd设备:(v5.8之后的版本)mtd设备,如mtdnand。(mtd设备的支持依赖于pstore/blk后端,准确的说不是一个独立的后端。)如何使用就像把大象放进冰箱一样。你只需要打开冰箱,把大象放进去,然后关上冰箱门。步骤,使用pstore只需要3步:启用pstore挂载pstore文件系统和读取dumped日志文件。有关详细说明,请参阅源代码上的文档。本文仅介绍基本功能。Documentation/admin-guide/ramoops.rstDocumentation/admin-guide/pstore-blk.rst在menuconfig$makemenuconfig|->Filesystems|->Miscellaneousfilesystems|->Persistentstoresupport|->Logkernelconsolemessages#consolefrontend|中启用选择内核pstore模块->Loguserspacemessages#pmsgfrontend|->Persistentfunctiontracer#ftracefrontend|->Logpanic/oopstoaRAMbuffer#pstore/rambackend|->Logpanic/oopstoablockdevice#pstore/blkbackend以上两个backend任选其一,frontend是选根据你自己的需要。至于dmesg前端,没有选择默认开启。如果要在mtd设备上使用,还需要选择mtdpstore模块:$makemenuconfig|->DeviceDrivers|->MemoryTechnologyDevice(MTD)support|->Logpanic/oopstoanMTDbufferbasedonpstore如果选择可以使用吗?虽然很想说“是”,但事实是有点“骨感”。即使所有前端都使用默认配置,pstore/ram至少需要知道可用内存范围?pstore/blk还需要知道至少要使用哪个块设备?pstore/ram支持模块参数(cmdline)、设备树和PlatformData从代码来看,优先级关系是:模块参数>PlatformData>devicetree。pstore/blk支持Kconfig和模块参数(cmdline)两种配置方式,模块参数优先级高于Kconfig。我对pstore/ram接触不多,直接介绍pstore/blk的使用方法。新同学请忽略很多乱七八糟的属性配置(使用默认值),直接告诉pstore/blk后端使用哪个块设备即可。在Kconfig中配置:$makemenuconfig|->Filesystems|->Miscellaneousfilesystems|->Persistentstoresupport|->Logpanic/oopstoablockdevice#pstore/blkbackend|->()blockdeviceidentifier#使用哪个块设备?如果使用cmdline,可以这样写:pstore_blk.blkdev=XXXX或者作为模块加载:$sudoinsmodpstore_blk.koblkdev=XXX这里的块设备可以是代表整个磁盘的sda,也可以是代表一个分区的mmcblk0p4。虽然支持7种变体,但常用的有两种:/dev/:例如U盘的第二个分区是/dev/sdb2::例如mmcdevice第六个分区是形式为179:6:$sudoinsmodpstore_blk.koblkdev=/dev/sdb2or$cat/proc/cmdline....pstore_blk.blkdev=179:6...如果是mtd设备,可以直接指定mtd分区名称或编号,例如:pstore_blk.blkdev=pstore#假设有一个名为pstore的MTD分区就OK了,对于新同学来说,这里配置就够了。您可以从我的github上看到我之前是如何测试它的(参见参考链接[2])。如果需要了解各个配置项的作用,请阅读内核文档(ramoops.rst或pstore_blk.rst),或者在Kconfig中按h键显示相关配置项的说明。启用挂载并正确配置设备后,启动时应该有这样一条日志:pstore_zone:registeredpstore_blkasbackendforkmsg(Oops,panic_write)pstore:Registeredpstore_blkaspersistentstorebackend这说明pstore已经找到设备并正常注册了。接下来,我们还需要通过挂载的方式触发pstore从设备中读取数据。常见的挂载如下:mount-tpstorepstore/sys/fs/pstore挂载后可以通过mount看到类似这样的信息:#mount...pstoreon/sys/fs/pstoretypepstore(rw,relatime)..如果crashlog曾经触发过,挂载点应该有这样一个文件:#ll/sys/fs/pstore...-r--r--r--1rootroot15521Jan100:06dmesg-pstore_blk-0...如果验证需要,我们可以这样主动触发内核崩溃:#echoc>/proc/sysrq-trigger我在U盘、SD卡、mmc、nand上都验证过了。维护者KeesCook提供了另一种基于loop的验证方法,用文件模拟块设备。当然这个方法不适合转储Panic日志,只能用于Oops或者其他前端:#insmodpstore.kocompress=off#insmodpstore_zone.ko#truncatepstore-blk.raw--size100M#losetup-f--showpstore-blk.raw/dev/loop0#insmodpstore_blk.koblkdev=/dev/loop0kmsg_size=16console_size=64best_effort=on看完上面的挂载,可以看到挂载点的dump日志文件。既然是文件,就必须支持读取、删除等一系列文件操作。root@TinaLinux:/sys/fs/pstore#head-n10dmesg-pstore_blk-1Oops:Total2timesOops#1Part1<6>[2.743794]蓝牙:RFCOMMsocketlayerinitialized<6>[2.743813]蓝牙:RFCOMMver1.11<6>[2.743822]8021q:802.1QVLANSupportv1.8<3>[2.751766]reg-virt-consumerreg-virt-consumer.1:Failedtoobtainsupply'drivevbus':-517<3>[2.752330]reg-virt-consumerreg-virt-consumer.1:Failedtoobtainsupply'drivevbus':-517<5>[2.752742]ubi0:attachingmtd4<5>[2.890302]random:crnginitdone<5>[2.965927]ubi0:scanningisfinishedroot@TinaLinux:/sys/fs/pstore#lldrwxr-x---2rootroot0Jan100:11.drwxr-xr-x5rootroot0Jan100:11..-r--r--r--1rootroot15521Jan100:06dmesg-pstore_blk-0-r--r--r--1rootroot15128Jan100:11dmesg-pstore_blk-1root@TinaLinux:/sys/fs/pstore#rmdmesg-pstore_blk-1root@TinaLinux:/sys/fs/pstore#lldrwxr-x---2rootroot0Jan100:13.drwxr-xr-x5rootroot0Jan100:11..-r--r--r--1rootroot15521Jan100:06dmesg-pstore_blk-0对dmesg前端的Panic/Oops日志,pstore会自动添加两个行统计信息。例如:Oops:Total2times#表示触发Oops,这是系统安装后首次启动后第二次触发Oops。Oops#1Part1#表示这是上次运行时第一次触发Oops的日志。可以发现,第一行是累计的触发次数,第二行是最后启动的触发次数。每个文件名的格式为<前端名称>-<后端名称>-,例如dmesg-pstore_blk-1表示dmesg前端,pstore_blk后端和dmesg前端第一区的日志。当然除了dmesg前端,其他前端的名字大概是这样的:#ll-r--r--r--1rootroot3November1511:53console-pstore-blk-0-r--r--r--1rootroot366611511年1月:53demsg-pstore-blk-0-r--r--r--1rootroot655241511年1月:53ftrace-pstore-blk-0-r--r--r--1rootroot1511年1月9日:53pmsg-pstore-blk-0除此之外,每个文件的时间戳都指示崩溃的触发时间。在上面的例子中,由于系统没有同步更新系统时间,时间戳不合理。展望未来正如我刚才所说,现在物联网设备越来越流行,比如智能音箱和扫地机,pstore可以发挥很大的作用。全功能支持到目前为止,无论是block设备还是mtd设备,社区的代码都没有能够支持pstore的所有前端。Devicedmesg(Oops)dmesg(Panic)pmsgconsoleftraceblockdeviceYNYYYMTDdeviceYYNNNramdeviceYYYYYblockdevice如果需要记录panic日志,需要提供panic时写入块设备的接口。我在Allwinner的mmc和nand驱动中实现了这样的接口,但是由于各种原因不适合提交给社区。社区街区推动的适应,有望依靠更多同学的努力。MTD设备很早之前就有panic_write()的定义,所以可以支持paniclogdumping。由于擦除的物理性质,不支持其他前端。对于pmsg、console、ftrace等不能页面对齐写入的前端,需要做更多的适配工作。当前pstore中迁移pstore/ram的目录结构如下:$treefs/pstorefs/pstore/├──blk.c#pstore/blk后端实现├──ftrace.c#ftrace前端实现├──inode。c#pstore文件系统的注册与运行├──internal.h├──Kconfig├──Makefile├──platform.c#pstore前端函数核心├──pmsg.c#pmsg前端实现├──ram.c#pstore/ram后端实现├──ram_core.c#pstore/ram后端实现└──zone.c#pstore/zone实现存储空间的分配和管理在我打补丁之前,只支持转储登录到ram,所以如果我们看代码,会发现ram.c和ram_core.c实现了两个功能:dram空间分配和dram读写操作的管理。我实现的blk.c支持转储到块设备。但是后来发现无论是pstore/ram还是pstore/blk,它们对存储空间的分配和管理都非常相似,所以我提取了pstore/zone。因此,期望的代码水平应该是这样的:将pstore/ram集成到pstore/zone中已经和maintainer达成了共识,但是需要更多的同学一起来做更多的兼容性,比如ecc的支持。参考https://git.kernel.org/torvalds/c/829f3b9401fe7cc3c1f3642bb2520751a42a87dfhttps://github.com/gmpy/articles/blob/master/pstore/Test-Pstore-Block.md代码字段”,你可以通过以下二维码,转载本文请联系Linux代码阅读圈公众号。