什么是pstore
pstore最初是用于系統(tǒng)發(fā)生oops或panic時,自動保存內(nèi)核log buffer中的日志。不過在當前內(nèi)核版本中,其已經(jīng)支持了更多的功能,如保存console日志、ftrace消息和用戶空間日志。同時,它還支持將這些消息保存在不同的存儲設(shè)備中,如內(nèi)存、塊設(shè)備或mtd設(shè)備。 為了提高靈活性和可擴展性,pstore將以上功能分別抽象為前端和后端,其中像dmesg、console等為pstore提供數(shù)據(jù)的模塊稱為前端,而內(nèi)存設(shè)備、塊設(shè)備等用于存儲數(shù)據(jù)的模塊稱為后端,pstore core則分別為它們提供相關(guān)的注冊接口。
通過模塊化的設(shè)計,實現(xiàn)了前端和后端的解耦,因此若某些模塊需要利用pstore保存信息,就可以方便地向pstore添加新的前端。而若需要將pstore數(shù)據(jù)保存到新的存儲設(shè)備上,也可以通過向其添加后端設(shè)備的方式完成。
除此之外,pstore還設(shè)計了一套pstore文件系統(tǒng),用于查詢和操作上一次重啟時已經(jīng)保存的pstore數(shù)據(jù)。當該文件系統(tǒng)被掛載時,保存在backend中的數(shù)據(jù)將被讀取到pstore fs中,并以文件的形式顯示。
pstore工作原理
pstore 源文件主要有以下幾個:fs/pstore/ram_core.c
fs/pstore/
├──?ftrace.c??#?ftrace?前端的實現(xiàn)
├──?inode.c??#?pstore?文件系統(tǒng)的注冊與操作
├──?internal.h
├──?Kconfig
├──?Makefile
├──?platform.c??#?pstore?前后端功能的核心
├──?pmsg.c??#?pmsg?前端的實現(xiàn)
├──?ram.c???#?pstore/ram?后端的實現(xiàn),dram空間分配與管理
├──?ram_core.c??#?pstore/ram?后端的實現(xiàn),dram的讀寫操作
文件創(chuàng)建
pstore文件系統(tǒng)位置在:
#?ls?/sys/fs/pstore
console-ramoops-0?dmesg-ramoops-0
控制臺日志位于 pstore 目錄下的console-ramoops文件中,因為采用console機制,該文件中的日志信息也受printk level控制,并不一定是全的。
oops/panic日志位于 pstore 目錄下的dmesg-ramoops-x文件中,根據(jù)緩沖區(qū)大小可以有多個文件,x從0開始。
函數(shù)調(diào)用序列日志位于 pstore 目錄下的ftrace-ramoops文件中。
相關(guān)代碼在inode.c
pstore_mkfile
里:
/*
?*?Make?a?regular?file?in?the?root?directory?of?our?file?system.
?*?Load?it?up?with?"size"?bytes?of?data?from?"buf".
?*?Set?the?mtime?&?ctime?to?the?date?that?this?record?was?originally?stored.
?*/
int?pstore_mkfile(enum?pstore_type_id?type,?char?*psname,?u64?id,?int?count,
????char?*data,?bool?compressed,?size_t?size,
????struct?timespec?time,?struct?pstore_info?*psi)
{
........................
?rc?=?-ENOMEM;
?inode?=?pstore_get_inode(pstore_sb);
..............................
?switch?(type)?{
?case?PSTORE_TYPE_DMESG:
??scnprintf(name,?sizeof(name),?"dmesg-%s-%lld%s",
?????psname,?id,?compressed???".enc.z"?:?"");
??break;
?case?PSTORE_TYPE_CONSOLE:
??scnprintf(name,?sizeof(name),?"console-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_FTRACE:
??scnprintf(name,?sizeof(name),?"ftrace-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_MCE:
??scnprintf(name,?sizeof(name),?"mce-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_PPC_RTAS:
??scnprintf(name,?sizeof(name),?"rtas-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_PPC_OF:
??scnprintf(name,?sizeof(name),?"powerpc-ofw-%s-%lld",
?????psname,?id);
??break;
?case?PSTORE_TYPE_PPC_COMMON:
??scnprintf(name,?sizeof(name),?"powerpc-common-%s-%lld",
?????psname,?id);
??break;
?case?PSTORE_TYPE_PMSG:
??scnprintf(name,?sizeof(name),?"pmsg-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_PPC_OPAL:
??sprintf(name,?"powerpc-opal-%s-%lld",?psname,?id);
??break;
?case?PSTORE_TYPE_UNKNOWN:
??scnprintf(name,?sizeof(name),?"unknown-%s-%lld",?psname,?id);
??break;
?default:
??scnprintf(name,?sizeof(name),?"type%d-%s-%lld",
?????type,?psname,?id);
??break;
?}
....................
?dentry?=?d_alloc_name(root,?name);
.......................
?d_add(dentry,?inode);
................
}
pstore_mkfile
根據(jù)不同的type
,使用snprintf
函數(shù)生成文件名name
。生成的文件名格式為<type>-<psname>-<id>
,其中type
是enum pstore_type_id
類型的一個值,psname
是給定的psname
參數(shù),id
是給定的id
參數(shù)。
接著使用d_alloc_name
函數(shù)為根目錄創(chuàng)建一個目錄項dentry
,最后使用d_add
函數(shù)將目錄項dentry
與索引節(jié)點inode
關(guān)聯(lián)起來,將其添加到文件系統(tǒng)中。
pstore_register
ramoops負責把message write到某個ram區(qū)域上,platform負責從ram讀取存到/sys/fs/pstore
,ok,先來看機制代碼platform.c
。
backend需要用pstore_register
來注冊:
/*
?*?platform?specific?persistent?storage?driver?registers?with
?*?us?here.?If?pstore?is?already?mounted,?call?the?platform
?*?read?function?right?away?to?populate?the?file?system.?If?not
?*?then?the?pstore?mount?code?will?call?us?later?to?fill?out
?*?the?file?system.
?*/
int?pstore_register(struct?pstore_info?*psi)
{
????struct?module?*owner?=?psi->owner;
????if?(backend?&&?strcmp(backend,?psi->name))
????????return?-EPERM;
????spin_lock(&pstore_lock);
????if?(psinfo)?{
????????spin_unlock(&pstore_lock);
????????return?-EBUSY;
????}
????if?(!psi->write)
????????psi->write?=?pstore_write_compat;
????if?(!psi->write_buf_user)
????????psi->write_buf_user?=?pstore_write_buf_user_compat;
????psinfo?=?psi;
????mutex_init(&psinfo->read_mutex);
????spin_unlock(&pstore_lock);
????...
????/*
?????*?Update?the?module?parameter?backend,?so?it?is?visible
?????*?through?/sys/module/pstore/parameters/backend
?????*/
????backend?=?psi->name;
????module_put(owner);
backend
判斷確保一次只能有一個并記錄了全局psinfo
。
看下結(jié)構(gòu)體pstore_info
:
struct?pstore_info?{
????struct?module????*owner;
????char????????*name;
????spinlock_t????buf_lock;????/*?serialize?access?to?'buf'?*/
????char????????*buf;
????size_t????????bufsize;
????struct?mutex????read_mutex;????/*?serialize?open/read/close?*/
????int????????flags;
????int????????(*open)(struct?pstore_info?*psi);
????int????????(*close)(struct?pstore_info?*psi);
????ssize_t????????(*read)(u64?*id,?enum?pstore_type_id?*type,
????????????int?*count,?struct?timespec?*time,?char?**buf,
????????????bool?*compressed,?ssize_t?*ecc_notice_size,
????????????struct?pstore_info?*psi);
????int????????(*write)(enum?pstore_type_id?type,
????????????enum?kmsg_dump_reason?reason,?u64?*id,
????????????unsigned?int?part,?int?count,?bool?compressed,
????????????size_t?size,?struct?pstore_info?*psi);
????int????????(*write_buf)(enum?pstore_type_id?type,
????????????enum?kmsg_dump_reason?reason,?u64?*id,
????????????unsigned?int?part,?const?char?*buf,?bool?compressed,
????????????size_t?size,?struct?pstore_info?*psi);
????int????????(*write_buf_user)(enum?pstore_type_id?type,
????????????enum?kmsg_dump_reason?reason,?u64?*id,
????????????unsigned?int?part,?const?char?__user?*buf,
????????????bool?compressed,?size_t?size,?struct?pstore_info?*psi);
????int????????(*erase)(enum?pstore_type_id?type,?u64?id,
????????????int?count,?struct?timespec?time,
????????????struct?pstore_info?*psi);
????void????????*data;
};
name
就是backend的name了。
*write
和*write_buf_user
如果backend沒有給出會有個默認compat func,最終都走的*write_buf
。
if?(!psi->write)
????????psi->write?=?pstore_write_compat;
????if?(!psi->write_buf_user)
????????psi->write_buf_user?=?pstore_write_buf_user_compat;
static?int?pstore_write_compat(enum?pstore_type_id?type,
???????????????????enum?kmsg_dump_reason?reason,
???????????????????u64?*id,?unsigned?int?part,?int?count,
???????????????????bool?compressed,?size_t?size,
???????????????????struct?pstore_info?*psi)
{
????return?psi->write_buf(type,?reason,?id,?part,?psinfo->buf,?compressed,
?????????????????size,?psi);
}
static?int?pstore_write_buf_user_compat(enum?pstore_type_id?type,
???????????????????enum?kmsg_dump_reason?reason,
???????????????????u64?*id,?unsigned?int?part,
???????????????????const?char?__user?*buf,
???????????????????bool?compressed,?size_t?size,
???????????????????struct?pstore_info?*psi)
{
...
????????ret?=?psi->write_buf(type,?reason,?id,?part,?psinfo->buf,
...
}
繼續(xù)pstore注冊:
?if?(pstore_is_mounted())
????????pstore_get_records(0);
如果pstore已經(jīng)mounted,那就創(chuàng)建并填充文件by pstore_get_records
:
/*
?*?Read?all?the?records?from?the?persistent?store.?Create
?*?files?in?our?filesystem.??Don't?warn?about?-EEXIST?errors
?*?when?we?are?re-scanning?the?backing?store?looking?to?add?new
?*?error?records.
?*/
void?pstore_get_records(int?quiet)
{
????struct?pstore_info?*psi?=?psinfo;?//tj:?global?psinfo
????...
????mutex_lock(&psi->read_mutex);
????if?(psi->open?&&?psi->open(psi))
????????goto?out;
????while?((size?=?psi->read(&id,?&type,?&count,?&time,?&buf,?&compressed,
?????????????????&ecc_notice_size,?psi))?>?0)?{
????????if?(compressed?&&?(type?==?PSTORE_TYPE_DMESG))?{
????????????if?(big_oops_buf)
????????????????unzipped_len?=?pstore_decompress(buf,
????????????????????????????big_oops_buf,?size,
????????????????????????????big_oops_buf_sz);
????????????if?(unzipped_len?>?0)?{
????????????????if?(ecc_notice_size)
????????????????????memcpy(big_oops_buf?+?unzipped_len,
???????????????????????????buf?+?size,?ecc_notice_size);
????????????????kfree(buf);
????????????????buf?=?big_oops_buf;
????????????????size?=?unzipped_len;
????????????????compressed?=?false;
????????????}?else?{
????????????????pr_err("decompression?failed;returned?%dn",
???????????????????????unzipped_len);
????????????????compressed?=?true;
????????????}
????????}
????????rc?=?pstore_mkfile(type,?psi->name,?id,?count,?buf,
???????????????????compressed,?size?+?ecc_notice_size,
???????????????????time,?psi);
????????if?(unzipped_len?<?0)?{
????????????/*?Free?buffer?other?than?big?oops?*/
????????????kfree(buf);
????????????buf?=?NULL;
????????}?else
????????????unzipped_len?=?-1;
????????if?(rc?&&?(rc?!=?-EEXIST?||?!quiet))
????????????failed++;
????}
????if?(psi->close)
????????psi->close(psi);
out:
????mutex_unlock(&psi->read_mutex);????
if needed,call pstore_decompress
解壓然后創(chuàng)建pstore文件by vfs接口pstore_mkfile
。
pstore注冊接下來是按類別分別注冊:
????if?(psi->flags?&?PSTORE_FLAGS_DMESG)
????????pstore_register_kmsg();
????if?(psi->flags?&?PSTORE_FLAGS_CONSOLE)
????????pstore_register_console();
????if?(psi->flags?&?PSTORE_FLAGS_FTRACE)
????????pstore_register_ftrace();
????if?(psi->flags?&?PSTORE_FLAGS_PMSG)
????????pstore_register_pmsg();
psi->flags
仍是由backend決定,只看pstore_register_kmsg
和pstore_register_console
。
pstore panic log注冊
static?struct?kmsg_dumper?pstore_dumper?=?{
????.dump?=?pstore_dump,
};
/*
?*?Register?with?kmsg_dump?to?save?last?part?of?console?log?on?panic.
?*/
static?void?pstore_register_kmsg(void)
{
????kmsg_dump_register(&pstore_dumper);
}
pstore_dump
最終會call backend的write,直接用全局psinfo。
/*
?*?callback?from?kmsg_dump.?(s2,l2)?has?the?most?recently
?*?written?bytes,?older?bytes?are?in?(s1,l1).?Save?as?much
?*?as?we?can?from?the?end?of?the?buffer.
?*/
static?void?pstore_dump(struct?kmsg_dumper?*dumper,
????????????enum?kmsg_dump_reason?reason)
{
????...
????????ret?=?psinfo->write(PSTORE_TYPE_DMESG,?reason,?&id,?part,
????????????????????oopscount,?compressed,?total_len,?psinfo);
kmsg_dump_register
是內(nèi)核一種增加log dumper方法,called when kernel oopses or panic。
/**
?*?kmsg_dump_register?-?register?a?kernel?log?dumper.
?*?@dumper:?pointer?to?the?kmsg_dumper?structure
?*
?*?Adds?a?kernel?log?dumper?to?the?system.?The?dump?callback?in?the
?*?structure?will?be?called?when?the?kernel?oopses?or?panics?and?must?be
?*?set.?Returns?zero?on?success?and?%-EINVAL?or?%-EBUSY?otherwise.
?*/
int?kmsg_dump_register(struct?kmsg_dumper?*dumper)
{
?unsigned?long?flags;
?int?err?=?-EBUSY;
?/*?The?dump?callback?needs?to?be?set?*/
?if?(!dumper->dump)
??return?-EINVAL;
?spin_lock_irqsave(&dump_list_lock,?flags);
?/*?Don't?allow?registering?multiple?times?*/
?if?(!dumper->registered)?{
??dumper->registered?=?1;
??list_add_tail_rcu(&dumper->list,?&dump_list);
??err?=?0;
?}
?spin_unlock_irqrestore(&dump_list_lock,?flags);
?return?err;
}
/**
?*?kmsg_dump?-?dump?kernel?log?to?kernel?message?dumpers.
?*?@reason:?the?reason?(oops,?panic?etc)?for?dumping
?*
?*?Call?each?of?the?registered?dumper's?dump()?callback,?which?can
?*?retrieve?the?kmsg?records?with?kmsg_dump_get_line()?or
?*?kmsg_dump_get_buffer().
?*/
void?kmsg_dump(enum?kmsg_dump_reason?reason)
{
?struct?kmsg_dumper?*dumper;
?unsigned?long?flags;
?if?((reason?>?KMSG_DUMP_OOPS)?&&?!always_kmsg_dump)
??return;
?rcu_read_lock();
?list_for_each_entry_rcu(dumper,?&dump_list,?list)?{
??if?(dumper->max_reason?&&?reason?>?dumper->max_reason)
???continue;
??/*?initialize?iterator?with?data?about?the?stored?records?*/
??dumper->active?=?true;
??raw_spin_lock_irqsave(&logbuf_lock,?flags);
??dumper->cur_seq?=?clear_seq;
??dumper->cur_idx?=?clear_idx;
??dumper->next_seq?=?log_next_seq;
??dumper->next_idx?=?log_next_idx;
??raw_spin_unlock_irqrestore(&logbuf_lock,?flags);
??/*?invoke?dumper?which?will?iterate?over?records?*/
??dumper->dump(dumper,?reason);
??/*?reset?iterator?*/
??dumper->active?=?false;
?}
?rcu_read_unlock();
}
pstore console 注冊
static?struct?console?pstore_console?=?{
????.name????=?"pstore",
????.write????=?pstore_console_write,
????.flags????=?CON_PRINTBUFFER?|?CON_ENABLED?|?CON_ANYTIME,
????.index????=?-1,
};
static?void?pstore_register_console(void)
{
????register_console(&pstore_console);
}
->write
最終也會call backend write:
#ifdef?CONFIG_PSTORE_CONSOLE
static?void?pstore_console_write(struct?console?*con,?const?char?*s,?unsigned?c)
{
????const?char?*e?=?s?+?c;
????while?(s?<?e)?{
????????unsigned?long?flags;
????????u64?id;
????????if?(c?>?psinfo->bufsize)
????????????c?=?psinfo->bufsize;
????????if?(oops_in_progress)?{
????????????if?(!spin_trylock_irqsave(&psinfo->buf_lock,?flags))
????????????????break;
????????}?else?{
????????????spin_lock_irqsave(&psinfo->buf_lock,?flags);
????????}
????????memcpy(psinfo->buf,?s,?c);
????????psinfo->write(PSTORE_TYPE_CONSOLE,?0,?&id,?0,?0,?0,?c,?psinfo);??//?tj:?here
????????spin_unlock_irqrestore(&psinfo->buf_lock,?flags);
????????s?+=?c;
????????c?=?e?-?s;
????}
}
ramoops
下面來看下RAM backend: ramoops,先看probe:
static?int?ramoops_probe(struct?platform_device?*pdev)
{
????struct?device?*dev?=?&pdev->dev;
????struct?ramoops_platform_data?*pdata?=?dev->platform_data;
????...
????if?(!pdata->mem_size?||?(!pdata->record_size?&&?!pdata->console_size?&&
????????????!pdata->ftrace_size?&&?!pdata->pmsg_size))?{
????????pr_err("The?memory?size?and?the?record/console?size?must?be?"
????????????"non-zeron");
????????goto?fail_out;
????}
????...
????
????cxt->size?=?pdata->mem_size;
????cxt->phys_addr?=?pdata->mem_address;
????cxt->memtype?=?pdata->mem_type;
????cxt->record_size?=?pdata->record_size;
????cxt->console_size?=?pdata->console_size;
????cxt->ftrace_size?=?pdata->ftrace_size;
????cxt->pmsg_size?=?pdata->pmsg_size;
????cxt->dump_oops?=?pdata->dump_oops;
????cxt->ecc_info?=?pdata->ecc_info;
pdata
應該來源ramoops_register_dummy
:
static?void?ramoops_register_dummy(void)
{
????...
????pr_info("using?module?parametersn");
????dummy_data?=?kzalloc(sizeof(*dummy_data),?GFP_KERNEL);
????if?(!dummy_data)?{
????????pr_info("could?not?allocate?pdatan");
????????return;
????}
????dummy_data->mem_size?=?mem_size;
????dummy_data->mem_address?=?mem_address;
????dummy_data->mem_type?=?mem_type;
????dummy_data->record_size?=?record_size;
????dummy_data->console_size?=?ramoops_console_size;
????dummy_data->ftrace_size?=?ramoops_ftrace_size;
????dummy_data->pmsg_size?=?ramoops_pmsg_size;
????dummy_data->dump_oops?=?dump_oops;
????/*
?????*?For?backwards?compatibility?ramoops.ecc=1?means?16?bytes?ECC
?????*?(using?1?byte?for?ECC?isn't?much?of?use?anyway).
?????*/
????dummy_data->ecc_info.ecc_size?=?ramoops_ecc?==?1???16?:?ramoops_ecc;
????dummy?=?platform_device_register_data(NULL,?"ramoops",?-1,
????????????dummy_data,?sizeof(struct?ramoops_platform_data));
有幾個可配參數(shù):
/*
?*?Ramoops?platform?data
?*?@mem_size????memory?size?for?ramoops
?*?@mem_address????physical?memory?address?to?contain?ramoops
?*/
struct?ramoops_platform_data?{
????unsigned?long????mem_size;??
????phys_addr_t????mem_address;?
????unsigned?int????mem_type;
????unsigned?long????record_size;
????unsigned?long????console_size;
????unsigned?long????ftrace_size;
????unsigned?long????pmsg_size;
????int????????dump_oops;
????struct?persistent_ram_ecc_info?ecc_info;
};
mem_size
-
- :用于Ramoops的內(nèi)存大小,表示分配給Ramoops的物理內(nèi)存的大小。
mem_address
-
- :用于Ramoops的物理內(nèi)存地址,指定用于存儲Ramoops的物理內(nèi)存的起始地址。
mem_type
-
- :內(nèi)存類型,用于進一步描述內(nèi)存的屬性和特征。
record_size
-
- :每個記錄的大小
console_size
-
- :控制臺記錄的大小
ftrace_size
-
- :Ftrace記錄的大小
pmsg_size
-
- :pmsg消息記錄的大小
dump_oops
-
- :是否轉(zhuǎn)儲oops信息的標志,表示是否將oops信息轉(zhuǎn)儲到Ramoops中。
ecc_info
- :RAM的ECC(糾錯碼)信息,用于提供關(guān)于ECC配置和處理的詳細信息。
有個結(jié)構(gòu)表示了ramoops的context:
struct?ramoops_context?{
????struct?persistent_ram_zone?**przs;
????struct?persistent_ram_zone?*cprz;
????struct?persistent_ram_zone?*fprz;
????struct?persistent_ram_zone?*mprz;
????phys_addr_t?phys_addr;
????unsigned?long?size;
????unsigned?int?memtype;
????size_t?record_size;
????size_t?console_size;
????size_t?ftrace_size;
????size_t?pmsg_size;
????int?dump_oops;
????struct?persistent_ram_ecc_info?ecc_info;
????unsigned?int?max_dump_cnt;
????unsigned?int?dump_write_cnt;
????/*?_read_cnt?need?clear?on?ramoops_pstore_open?*/
????unsigned?int?dump_read_cnt;
????unsigned?int?console_read_cnt;
????unsigned?int?ftrace_read_cnt;
????unsigned?int?pmsg_read_cnt;
????struct?pstore_info?pstore;
};
在ramoops_probe
時也是把ramoops_platform_data
的成員賦給了context對應的。要了解具體含義,繼續(xù)probe:
????paddr?=?cxt->phys_addr;
????dump_mem_sz?=?cxt->size?-?cxt->console_size?-?cxt->ftrace_size
????????????-?cxt->pmsg_size;
????err?=?ramoops_init_przs(dev,?cxt,?&paddr,?dump_mem_sz);
????if?(err)
????????goto?fail_out;
????err?=?ramoops_init_prz(dev,?cxt,?&cxt->cprz,?&paddr,
???????????????????cxt->console_size,?0);
????if?(err)
????????goto?fail_init_cprz;
????err?=?ramoops_init_prz(dev,?cxt,?&cxt->fprz,?&paddr,?cxt->ftrace_size,
???????????????????LINUX_VERSION_CODE);
????if?(err)
????????goto?fail_init_fprz;
????err?=?ramoops_init_prz(dev,?cxt,?&cxt->mprz,?&paddr,?cxt->pmsg_size,?0);
????if?(err)
????????goto?fail_init_mprz;
????cxt->pstore.data?=?cxt;
可見,是逐個init每個persistant ram zone
,size一共有4段:
dump_mem_sz?+?cxt->console_size?+?cxt->ftrace_size?+?cxt->pmsg_size?=?cxt->size
mem_size
就是總大小了,mem_address
是ramoops的物理地址,record_size
再看下oops/panic ram:
static?int?ramoops_init_przs(struct?device?*dev,?struct?ramoops_context?*cxt,
?????????????????phys_addr_t?*paddr,?size_t?dump_mem_sz)
{
????int?err?=?-ENOMEM;
????int?i;
????if?(!cxt->record_size)
????????return?0;
????if?(*paddr?+?dump_mem_sz?-?cxt->phys_addr?>?cxt->size)?{
????????dev_err(dev,?"no?room?for?dumpsn");
????????return?-ENOMEM;
????}
????cxt->max_dump_cnt?=?dump_mem_sz?/?cxt->record_size;
????if?(!cxt->max_dump_cnt)
????????return?-ENOMEM;
ok dump_mem_size
大小的區(qū)域分成max_dump_cnt
個,每個記錄大小是record_size
。
接著會call persistent_ram_new
來分配內(nèi)存給這個ram zone。
????for?(i?=?0;?i?<?cxt->max_dump_cnt;?i++)?{
????????cxt->przs[i]?=?persistent_ram_new(*paddr,?cxt->record_size,?0,
??????????????????????????&cxt->ecc_info,
??????????????????????????cxt->memtype,?0);
console/ftrace/pmsg
ram zone同上分配。
最后處理flags并注冊pstore:
????cxt->pstore.flags?=?PSTORE_FLAGS_DMESG;?//tj:?默認dump?oops/panic
????if?(cxt->console_size)
????????cxt->pstore.flags?|=?PSTORE_FLAGS_CONSOLE;
????if?(cxt->ftrace_size)
????????cxt->pstore.flags?|=?PSTORE_FLAGS_FTRACE;
????if?(cxt->pmsg_size)
????????cxt->pstore.flags?|=?PSTORE_FLAGS_PMSG;
????err?=?pstore_register(&cxt->pstore);
????if?(err)?{
????????pr_err("registering?with?pstore?failedn");
????????goto?fail_buf;
????}
來看下ramoops pstore的定義的callback,他們通過全局psinfo
而來:
static?struct?ramoops_context?oops_cxt?=?{
????.pstore?=?{
????????.owner????=?THIS_MODULE,
????????.name????=?"ramoops",
????????.open????=?ramoops_pstore_open,
????????.read????=?ramoops_pstore_read,?//?psi->read
????????.write_buf????=?ramoops_pstore_write_buf,?//for?non?pmsg
????????.write_buf_user????=?ramoops_pstore_write_buf_user,?//for?pmsg
????????.erase????=?ramoops_pstore_erase,
????},
};
pstore使用方法
ramoops
配置內(nèi)核
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_RAM=y
CONFIG_PANIC_TIMEOUT=-1
由于log數(shù)據(jù)存放于DDR,不能掉電,只能依靠自動重啟機制來查看,故而要配置:CONFIG_PANIC_TIMEOUT,讓系統(tǒng)在 panic 后能自動重啟。
dts
ramoops_mem:?ramoops_mem?{
????reg?=?<0x0?0x110000?0x0?0xf0000>;
????reg-names?=?"ramoops_mem";
};
ramoops?{
????compatible?=?"ramoops";
????record-size?=?<0x0?0x20000>;
????console-size?=?<0x0?0x80000>;
????ftrace-size?=?<0x0?0x00000>;
????pmsg-size?=?<0x0?0x50000>;
????memory-region?=?<&ramoops_mem>;
};
mtdoops
內(nèi)核配置
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_MTD_OOPS=y
CONFIG_MAGIC_SYSRQ=y
分區(qū)配置
cmdline方式:
bootargs?=?"console=ttyS1,115200?loglevel=8?rootwait?root=/dev/mtdblock5?rootfstype=squashfs?mtdoops.mtddev=pstore";
blkparts?=?"mtdparts=spi0.0:64k(spl)ro,256k(uboot)ro,64k(dtb)ro,128k(pstore),3m(kernel)ro,4m(rootfs)ro,-(data)";
part of方式:
bootargs?=?"console=ttyS1,115200?loglevel=8?rootwait?root=/dev/mtdblock5?rootfstype=squashfs?mtdoops.mtddev=pstore";
partition@60000?{
????label?=?"pstore";
????reg?=?<0x60000?0x20000>;
?};
blkoops
配置內(nèi)核
CONFIG_PSTORE=y
CONFIG_PSTORE_CONSOLE=y
CONFIG_PSTORE_PMSG=y
CONFIG_PSTORE_BLK=y
CONFIG_MTD_PSTORE=y
CONFIG_MAGIC_SYSRQ=y
配置分區(qū)
cmdline方式:
bootargs?=?"console=ttyS1,115200?loglevel=8?rootwait?root=/dev/mtdblock5?rootfstype=squashfs?pstore_blk.blkdev=pstore";
blkparts?=?"mtdparts=spi0.0:64k(spl)ro,256k(uboot)ro,64k(dtb)ro,128k(pstore),3m(kernel)ro,4m(rootfs)ro,-(data)";
part of方式:
bootargs?=?"console=ttyS1,115200?loglevel=8?rootwait?root=/dev/mtdblock5?rootfstype=squashfs?pstore_blk.blkdev=pstore";
partition@60000?{
????label?=?"pstore";
????reg?=?<0x60000?0x20000>;
};
pstore fs
掛載pstore文件系統(tǒng)
mount?-t?pstore?pstore?/sys/fs/pstore
掛載后,通過mount
能看到類似這樣的信息:
#?mount
pstore?on?/sys/fs/pstore?type?pstore?(rw,relatime)
如果需要驗證,可以這樣主動觸發(fā)內(nèi)核崩潰:
#?echo?c?>?/proc/sysrq-trigger
不同配置方式日志名稱不同
ramoops
#?mount?-t?pstore?pstore?/sys/fs/pstore/
#?cd?/sys/fs/pstore/
#?ls
console-ramoops-0??dmesg-ramoops-0????dmesg-ramoops-1
mtdoops
#?cat?/dev/mtd3?>?1.txt
#?cat?1.txt
blkoops
cd?/sys/fs/pstore/
ls
dmesg-pstore_blk-0??dmesg-pstore_blk-1
總結(jié)
pstore
setup 流程:
ramoops_init
ramoops_register_dummy
ramoops_probe
ramoops_register
查看 pstore
數(shù)據(jù)保存流程:
register?a?pstore_dumper
//?when?panic?happens,?kmsg_dump?is?called
call?dumper->dump
pstore_dump
查看 pstore
數(shù)據(jù)讀取流程:
ramoops_probe
persistent_ram_post_init
pstore_register
pstore_get_records
ramoops_pstore_read
pstore_decompress?(only?for?dmesg)
pstore_mkfile?(save?to?files)
本文參考
https://heapdump.cn/article/1961461
https://blog.csdn.net/u013836909/article/details/129894795
https://zhuanlan.zhihu.com/p/545560128
https://docs.kernel.org/admin-guide/pstore-blk.html