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” 接受以下變體

  1. /dev/<disk_name> 表示磁碟的裝置號

  2. /dev/<disk_name><decimal> 表示分割槽的裝置號 - 磁碟的裝置號加上分割槽號

  3. /dev/<disk_name>p<decimal> - 與上述相同;當分割槽磁碟的磁碟名稱以數字結尾時,使用此形式。

當 pstore/blk 構建到核心中時,“blkdev” 接受以下變體

  1. <hex_major><hex_minor> 十六進位制表示的裝置號,沒有前導 0x,例如 b302。

  2. PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF 表示分割槽表的唯一 id(如果分割槽表提供)。UUID 可以是 EFI/GPT UUID,也可以使用 SSSSSSSS-PP 格式引用 MSDOS 分割槽,其中 SSSSSSSS 是 32 位 “NT 磁碟簽名” 的零填充十六進位制表示,PP 是從 1 開始的分割槽號的零填充十六進位制表示。

  3. PARTUUID=<UUID>/PARTNROFF=<int> 用於選擇與具有已知唯一 id 的分割槽相關的分割槽。

  4. <major>:<minor> 裝置的主裝置號和次裝置號,用冒號分隔。

它接受 MTD 裝置的以下變體

  1. <裝置名稱> MTD 裝置名稱。建議使用 “pstore”。

  2. <裝置號> 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

console 前端的塊大小(KB)。它**必須**是 4 的倍數。如果您不關心 console 日誌,則它是可選的。

與 pmsg 前端類似,console 前端只有一個塊。

所有 console 日誌都將附加到塊中。重新啟動後,內容在 /sys/fs/pstore/console-pstore-blk-0 中可用。

ftrace_size

ftrace 前端的塊大小(KB)。它**必須**是 4 的倍數。如果您不關心 ftrace 日誌,則它是可選的。

與 oops 前端類似,ftrace 前端有多個塊,具體取決於 CPU 處理器的數量。每個塊的大小等於 ftrace_size / 處理器數量。

所有 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_devicestruct pstore_device_info 註冊到 pstore/blk。

int register_pstore_device(struct pstore_device_info *dev)

將非塊設備註冊到 pstore/blk

引數

struct pstore_device_info *dev

非塊裝置資訊

返回值

  • 0 - OK

  • 其他 - 發生錯誤。

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 檔案系統中讀取轉儲資料。這些檔案的格式為 dmesg-pstore-blk-[N] 用於 oops/panic 前端,pmsg-pstore-blk-0 用於 pmsg 前端,依此類推。轉儲檔案的時間戳記錄觸發時間。要從塊裝置中刪除儲存的記錄,只需取消連結相應的 pstore 檔案。

panic 讀/寫 API 中的注意事項

如果在 panic 時,核心不會執行太久,任務將不會被排程,並且大多數核心資源將停止服務。它看起來像在單核計算機上執行的單執行緒程式。

以下幾點需要特別注意 panic 讀/寫 API

  1. **不能**分配任何記憶體。如果您需要記憶體,只需在塊驅動程式初始化時分配,而不是等到發生 panic 時才分配。

  2. 必須輪詢,**不能**中斷驅動。不再進行任務排程。塊驅動程式應延遲以確保寫入成功,但**不能**睡眠。

  3. **不能**獲取任何鎖。沒有其他任務,也沒有任何共享資源;您可以安全地打破所有鎖。

  4. 僅使用 CPU 進行傳輸。除非您確定 DMA 不會保持鎖定,否則不要使用 DMA 進行傳輸。

  5. 直接控制暫存器。請直接控制暫存器,而不是使用 Linux 核心資源。在初始化時進行 I/O 對映,而不是等到發生 panic 時才進行。

  6. 如有必要,重置您的塊裝置和控制器。如果您不確定在發生 panic 時塊裝置和控制器的狀態,您可以安全地停止並重置它們。

pstore/blk 支援 psblk_blkdev_info(),它定義在 *linux/pstore_blk.h* 中,用於獲取有關使用塊裝置的資訊,例如裝置號、扇區計數和整個磁碟的起始扇區。

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 xor 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

console 儲存區域

fpszs

ftrace 儲存區域

kmsg_max_cnt

**kpszs** 的最大計數

kmsg_read_cnt

讀取的總 kmsg 轉儲的計數器

kmsg_write_cnt

kmsg 轉儲寫入的總計數器

pmsg_read_cnt

讀取的總 pmsg 區域的計數器

console_read_cnt

讀取的總 console 區域的計數器

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

console 區域的大小,與 **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

console 儲存區域的總大小

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。