pstore 块 oops/panic 日志记录器¶
简介¶
pstore 块 (pstore/blk) 是一个 oops/panic 日志记录器,它在系统崩溃之前将其日志写入块设备和非块设备。您可以通过挂载 pstore 文件系统来获取这些日志文件,例如
mount -t pstore pstore /sys/fs/pstore
pstore 块概念¶
pstore/blk 为 pstore/blk 提供了高效的配置方法,它将所有配置分为两部分,用户配置和驱动程序配置。
用户配置决定了 pstore/blk 的工作方式,例如 pmsg_size、kmsg_size 等。它们都支持 Kconfig 和模块参数,但模块参数的优先级高于 Kconfig。
驱动程序配置是关于块设备和非块设备的所有内容,例如块设备的总大小和读/写操作。
用户配置¶
所有这些配置都支持 Kconfig 和模块参数,但模块参数的优先级高于 Kconfig。
这是一个模块参数的示例
pstore_blk.blkdev=/dev/mmcblk0p7 pstore_blk.kmsg_size=64 best_effort=y
您可能对每个配置的详细信息感兴趣。
blkdev¶
要使用的块设备。大多数时候,它是块设备的分区。这是 pstore/blk 所必需的。它也用于 MTD 设备。
当 pstore/blk 作为模块构建时,“blkdev” 接受以下变体
/dev/<disk_name> 表示磁盘的设备号
/dev/<disk_name><decimal> 表示分区的设备号 - 磁盘的设备号加上分区号
/dev/<disk_name>p<decimal> - 与上述相同;当分区的磁盘的磁盘名称以数字结尾时使用此形式。
当 pstore/blk 构建到内核中时,“blkdev” 接受以下变体
<hex_major><hex_minor> 十六进制表示的设备号,没有前导 0x,例如 b302。
PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF 表示如果分区表提供了分区,则该分区的唯一 ID。UUID 可以是 EFI/GPT UUID,也可以使用 SSSSSSSS-PP 格式引用 MSDOS 分区,其中 SSSSSSSS 是 32 位“NT 磁盘签名”的零填充十六进制表示,而 PP 是从 1 开始的分区号的零填充十六进制表示。
PARTUUID=<UUID>/PARTNROFF=<int> 选择与具有已知唯一 ID 的分区相关的分区。
<major>:<minor> 设备的 major 和 minor 号,用冒号分隔。
它接受 MTD 设备的以下变体
<device name> MTD 设备名称。“pstore” 是推荐的。
<device number> MTD 设备编号。
kmsg_size¶
oops/panic 前端的块大小,以 KB 为单位。它 **必须** 是 4 的倍数。如果您不关心 oops/panic 日志,则它是可选的。
根据除其他 pstore 前端之外的剩余空间,oops/panic 前端有多个块。
pstore/blk 将逐个记录到 oops/panic 块,如果没有更多的空闲块,则始终覆盖最旧的块。
pmsg_size¶
pmsg 前端的块大小,以 KB 为单位。它 **必须** 是 4 的倍数。如果您不关心 pmsg 日志,则它是可选的。
与 oops/panic 前端不同,pmsg 前端只有一个块。
Pmsg 是用户空间可访问的 pstore 对象。写入 * /dev/pmsg0 * 的内容将附加到该块。在重新启动时,这些内容可在 * /sys/fs/pstore/pmsg-pstore-blk-0 * 中找到。
console_size¶
控制台前端的块大小,以 KB 为单位。它 **必须** 是 4 的倍数。如果您不关心控制台日志,则它是可选的。
与 pmsg 前端类似,控制台前端只有一个块。
所有控制台日志都将附加到该块。在重新启动时,这些内容可在 * /sys/fs/pstore/console-pstore-blk-0 * 中找到。
ftrace_size¶
ftrace 前端的块大小,以 KB 为单位。它 **必须** 是 4 的倍数。如果您不关心 ftrace 日志,则它是可选的。
与 oops 前端类似,根据 cpu 处理器的数量,ftrace 前端有多个块。每个块的大小等于 ftrace_size / processors_count。
所有 ftrace 日志都将附加到该块。在重新启动时,这些内容被组合并在 * /sys/fs/pstore/ftrace-pstore-blk-0 * 中可用。
持久函数跟踪可能有助于调试软件或硬件相关的挂起。这是一个用法示例
# mount -t pstore pstore /sys/fs/pstore
# mount -t debugfs debugfs /sys/kernel/debug/
# echo 1 > /sys/kernel/debug/pstore/record_ftrace
# reboot -f
[...]
# mount -t pstore pstore /sys/fs/pstore
# tail /sys/fs/pstore/ftrace-pstore-blk-0
CPU:0 ts:5914676 c0063828 c0063b94 call_cpuidle <- cpu_startup_entry+0x1b8/0x1e0
CPU:0 ts:5914678 c039ecdc c006385c cpuidle_enter_state <- call_cpuidle+0x44/0x48
CPU:0 ts:5914680 c039e9a0 c039ecf0 cpuidle_enter_freeze <- cpuidle_enter_state+0x304/0x314
CPU:0 ts:5914681 c0063870 c039ea30 sched_idle_set_state <- cpuidle_enter_state+0x44/0x314
CPU:1 ts:5916720 c0160f59 c015ee04 kernfs_unmap_bin_file <- __kernfs_remove+0x140/0x204
CPU:1 ts:5916721 c05ca625 c015ee0c __mutex_lock_slowpath <- __kernfs_remove+0x148/0x204
CPU:1 ts:5916723 c05c813d c05ca630 yield_to <- __mutex_lock_slowpath+0x314/0x358
CPU:1 ts:5916724 c05ca2d1 c05ca638 __ww_mutex_lock <- __mutex_lock_slowpath+0x31c/0x358
max_reason¶
可以通过 max_reason
值来控制存储哪些类型的 kmsg 转储,如 include/linux/kmsg_dump.h 的 enum kmsg_dump_reason
中定义的那样。例如,要同时存储 Oopses 和 Panics,max_reason
应设置为 2 (KMSG_DUMP_OOPS),要仅存储 Panics,max_reason
应设置为 1 (KMSG_DUMP_PANIC)。将其设置为 0 (KMSG_DUMP_UNDEF) 意味着原因过滤将由 printk.always_kmsg_dump
启动参数控制:如果未设置,则为 KMSG_DUMP_OOPS,否则为 KMSG_DUMP_MAX。
驱动程序配置¶
设备驱动程序使用 register_pstore_device
和 struct pstore_device_info
注册到 pstore/blk。
-
int register_pstore_device(struct pstore_device_info *dev)¶
将非块设备注册到 pstore/blk
参数
struct pstore_device_info *dev
非块设备信息
返回值
0 - 成功
其他 - 发生错误。
-
void unregister_pstore_device(struct pstore_device_info *dev)¶
从 pstore/blk 中注销非块设备
参数
struct pstore_device_info *dev
非块设备信息
压缩和头部¶
块设备对于未压缩的 oops 数据来说足够大。实际上,我们不建议进行数据压缩,因为 pstore/blk 会在 oops/panic 数据的第一行插入一些信息。例如:
Panic: Total 16 times
这表示自从第一次启动以来,这是第 16 次出现 OOPS|Panic。有时,自从第一次启动以来发生 oops|panic 的次数对于判断系统是否稳定非常重要。
以下行由 pstore 文件系统插入。例如:
Oops#2 Part1
这表示在上一次启动中,这是第 2 次出现 OOPS。
读取数据¶
可以从 pstore 文件系统读取转储数据。这些文件的格式为 oops/panic 前端的 dmesg-pstore-blk-[N]
,pmsg 前端的 pmsg-pstore-blk-0
等等。转储文件的时间戳记录触发时间。要从块设备中删除存储的记录,只需取消链接相应的 pstore 文件。
panic 读取/写入 API 中的注意事项¶
如果发生 panic,内核不会运行太久,任务将不会被调度,并且大多数内核资源将无法使用。它看起来像一个在单核计算机上运行的单线程程序。
以下几点需要特别注意 panic 读取/写入 API
不能分配任何内存。如果需要内存,请在块驱动程序初始化时分配,而不是等到发生 panic 时。
必须轮询,不能中断驱动。不再进行任务调度。块驱动程序应延迟以确保写入成功,但不能休眠。
不能获取任何锁。没有其他任务,也没有任何共享资源;您可以安全地打破所有锁。
仅使用 CPU 进行传输。除非您确定 DMA 不会保持锁定,否则请勿使用 DMA 进行传输。
直接控制寄存器。请直接控制寄存器,而不是使用 Linux 内核资源。在初始化时进行 I/O 映射,而不是等到发生 panic 时。
如有必要,请重置您的块设备和控制器。如果您不确定在发生 panic 时块设备和控制器的状态,您可以安全地停止并重置它们。
pstore/blk 支持在 linux/pstore_blk.h 中定义的 psblk_blkdev_info(),以获取有关使用块设备的信息,例如设备号、扇区计数和整个磁盘的起始扇区。
pstore 块内部结构¶
为了方便开发人员参考,以下是所有重要的结构和 API
-
struct psz_buffer¶
要刷新到存储的区域的头部
定义:
struct psz_buffer {
#define PSZ_SIG (0x43474244) ;
uint32_t sig;
atomic_t datalen;
atomic_t start;
uint8_t data[];
};
成员
sig
指示头部的签名 (PSZ_SIG 异或 PSZONE 类型值)
datalen
data 中数据的长度
start
data 中存储的字节开始位置的偏移量
data
区域数据。
-
struct psz_kmsg_header¶
要刷新到存储的 kmsg 转储专用头部
定义:
struct psz_kmsg_header {
#define PSTORE_KMSG_HEADER_MAGIC 0x4dfc3ae5 ;
uint32_t magic;
struct timespec64 time;
bool compressed;
uint32_t counter;
enum kmsg_dump_reason reason;
uint8_t data[];
};
成员
magic
kmsg 转储头部的幻数
time
kmsg 转储触发时间
compressed
是否压缩
counter
kmsg 转储计数器
reason
kmsg 转储原因(例如,oops、panic 等)
data
指向日志数据的指针
描述
这是 kmsg 转储的子头部,在 psz_buffer
之后。
-
struct pstore_zone¶
单个存储的缓冲区
定义:
struct pstore_zone {
loff_t off;
const char *name;
enum pstore_type_id type;
struct psz_buffer *buffer;
struct psz_buffer *oldbuf;
size_t buffer_size;
bool should_recover;
atomic_t dirty;
};
成员
off
存储的区域偏移量
name
此区域的前端名称
type
此区域的前端类型
buffer
指向此区域管理的數據缓冲区的指针
oldbuf
指向旧数据缓冲区的指针
buffer_size
buffer->data 中的字节数
should_recover
此区域是否应从存储中恢复
dirty
buffer 中的数据是否脏
描述
内存中的区域结构。
-
struct psz_context¶
有关 pstore/zone 运行状态的所有信息
定义:
struct psz_context {
struct pstore_zone **kpszs;
struct pstore_zone *ppsz;
struct pstore_zone *cpsz;
struct pstore_zone **fpszs;
unsigned int kmsg_max_cnt;
unsigned int kmsg_read_cnt;
unsigned int kmsg_write_cnt;
unsigned int pmsg_read_cnt;
unsigned int console_read_cnt;
unsigned int ftrace_max_cnt;
unsigned int ftrace_read_cnt;
unsigned int oops_counter;
unsigned int panic_counter;
atomic_t recovered;
atomic_t on_panic;
struct mutex pstore_zone_info_lock;
struct pstore_zone_info *pstore_zone_info;
struct pstore_info pstore;
};
成员
kpszs
kmsg 转储存储区域
ppsz
pmsg 存储区域
cpsz
控制台存储区域
fpszs
ftrace 存储区域
kmsg_max_cnt
kpszs 的最大计数
kmsg_read_cnt
读取 kmsg 转储的总计数
kmsg_write_cnt
写入 kmsg 转储的总计数
pmsg_read_cnt
读取 pmsg 区域的总计数
console_read_cnt
读取控制台区域的总计数
ftrace_max_cnt
fpszs 的最大计数
ftrace_read_cnt
读取 ftrace 区域的最大计数
oops_counter
oops 转储的计数器
panic_counter
panic 转储的计数器
recovered
是否完成从存储中恢复数据
on_panic
是否正在发生 panic
pstore_zone_info_lock
pstore_zone_info 的锁
pstore_zone_info
来自后端的信息
pstore
pstore 的结构
-
enum psz_flush_mode¶
psz_zone_write() 的刷新模式
常量
FLUSH_NONE
不刷新到存储,但更新内存中的数据
FLUSH_PART
仅刷新包括元数据在内的数据的一部分到存储
FLUSH_META
仅刷新区域的元数据到存储
FLUSH_ALL
刷新所有区域
-
int psz_recovery(struct psz_context *cxt)¶
从存储中恢复数据
参数
struct psz_context *cxt
pstore/zone 的上下文
描述
恢复意味着在重新启动后从存储中读取回数据
返回值
成功返回 0,失败返回其他值。
-
struct pstore_zone_info¶
pstore/zone 后端驱动程序结构
定义:
struct pstore_zone_info {
struct module *owner;
const char *name;
unsigned long total_size;
unsigned long kmsg_size;
int max_reason;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
pstore_zone_read_op read;
pstore_zone_write_op write;
pstore_zone_erase_op erase;
pstore_zone_write_op panic_write;
};
成员
owner
负责此后端驱动程序的模块。
name
后端驱动程序的名称。
total_size
pstore/zone 可以使用的总大小(以字节为单位)。它必须大于 4096 并且是 4096 的倍数。
kmsg_size
oops/panic 区域的大小。零表示禁用,否则,它必须是 SECTOR_SIZE(512 字节)的倍数。
max_reason
要存储的最大 kmsg 转储原因。
pmsg_size
pmsg 区域的大小,与 kmsg_size 相同。
console_size
控制台区域的大小,与 kmsg_size 相同。
ftrace_size
ftrace 区域的大小,与 kmsg_size 相同。
read
通用读取操作。函数参数 size 和 offset 都是相对于存储的值。成功时,应返回字节数,其他值表示错误。
write
与 read 相同,但以下错误代码:-EBUSY 表示稍后尝试写入。-ENOMSG 表示尝试下一个区域。
erase
具有特殊删除作业的设备的通用擦除操作。函数参数 size 和 offset 都是相对于存储的值。成功返回 0,失败返回其他值。
panic_write
仅用于 panic 情况的写入操作。如果您不关心 panic 日志,则它是可选的。参数是相对于存储的值。成功时,应返回字节数,其他值(不包括 -ENOMSG)表示错误。-ENOMSG 表示尝试下一个区域。
-
struct pstore_device_info¶
后端 pstore/blk 驱动程序结构。
定义:
struct pstore_device_info {
unsigned int flags;
struct pstore_zone_info zone;
};
成员
flags
请参阅 linux/pstore.h 中定义的以 PSTORE_FLAGS 开头的宏。它表示此设备支持哪些前端。零表示所有后端都兼容。
zone
struct pstore_zone_info
详细信息。
-
struct pstore_blk_config¶
pstore_blk 后端配置
定义:
struct pstore_blk_config {
char device[80];
enum kmsg_dump_reason max_reason;
unsigned long kmsg_size;
unsigned long pmsg_size;
unsigned long console_size;
unsigned long ftrace_size;
};
成员
device
所需块设备的名称
max_reason
要存储到块设备的最大 kmsg 转储原因
kmsg_size
用于 kmsg 转储的总大小
pmsg_size
pmsg 存储区域的总大小
console_size
控制台存储区域的总大小
ftrace_size
用于 ftrace 日志数据的总大小(适用于所有 CPU)
-
int pstore_blk_get_config(struct pstore_blk_config *info)¶
获取 pstore_blk 后端配置的副本
参数
struct pstore_blk_config *info
要填充的 sturct pstore_blk_config
描述
失败返回负错误代码,成功返回 0。