0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

pstore原理和使用方法總結(jié)

嵌入式與Linux那些事 ? 來(lái)源:嵌入式與Linux那些事 ? 2024-03-04 14:23 ? 次閱讀

什么是pstore

pstore最初是用于系統(tǒng)發(fā)生oops或panic時(shí),自動(dòng)保存內(nèi)核log buffer中的日志。不過(guò)在當(dāng)前內(nèi)核版本中,其已經(jīng)支持了更多的功能,如保存console日志、ftrace消息和用戶空間日志。同時(shí),它還支持將這些消息保存在不同的存儲(chǔ)設(shè)備中,如內(nèi)存、塊設(shè)備或mtd設(shè)備。 為了提高靈活性和可擴(kuò)展性,pstore將以上功能分別抽象為前端和后端,其中像dmesg、console等為pstore提供數(shù)據(jù)的模塊稱(chēng)為前端,而內(nèi)存設(shè)備、塊設(shè)備等用于存儲(chǔ)數(shù)據(jù)的模塊稱(chēng)為后端,pstore core則分別為它們提供相關(guān)的注冊(cè)接口

通過(guò)模塊化的設(shè)計(jì),實(shí)現(xiàn)了前端和后端的解耦,因此若某些模塊需要利用pstore保存信息,就可以方便地向pstore添加新的前端。而若需要將pstore數(shù)據(jù)保存到新的存儲(chǔ)設(shè)備上,也可以通過(guò)向其添加后端設(shè)備的方式完成。

1ab81a72-d9e0-11ee-a297-92fbcf53809c.png

除此之外,pstore還設(shè)計(jì)了一套pstore文件系統(tǒng),用于查詢(xún)和操作上一次重啟時(shí)已經(jīng)保存的pstore數(shù)據(jù)。當(dāng)該文件系統(tǒng)被掛載時(shí),保存在backend中的數(shù)據(jù)將被讀取到pstore fs中,并以文件的形式顯示。

pstore工作原理

pstore 源文件主要有以下幾個(gè):fs/pstore/ram_core.c

fs/pstore/
├──ftrace.c#ftrace前端的實(shí)現(xiàn)
├──inode.c#pstore文件系統(tǒng)的注冊(cè)與操作
├──internal.h
├──Kconfig
├──Makefile
├──platform.c#pstore前后端功能的核心
├──pmsg.c#pmsg前端的實(shí)現(xiàn)
├──ram.c#pstore/ram后端的實(shí)現(xiàn),dram空間分配與管理
├──ram_core.c#pstore/ram后端的實(shí)現(xiàn),dram的讀寫(xiě)操作

文件創(chuàng)建

pstore文件系統(tǒng)位置在:

#ls/sys/fs/pstore
console-ramoops-0dmesg-ramoops-0

控制臺(tái)日志位于 pstore 目錄下的console-ramoops文件中,因?yàn)椴捎胏onsole機(jī)制,該文件中的日志信息也受printk level控制,并不一定是全的。

oops/panic日志位于 pstore 目錄下的dmesg-ramoops-x文件中,根據(jù)緩沖區(qū)大小可以有多個(gè)文件,x從0開(kāi)始。函數(shù)調(diào)用序列日志位于 pstore 目錄下的ftrace-ramoops文件中。

相關(guān)代碼在inode.c pstore_mkfile里:

/*
*Makearegularfileintherootdirectoryofourfilesystem.
*Loaditupwith"size"bytesofdatafrom"buf".
*Setthemtime&ctimetothedatethatthisrecordwasoriginallystored.
*/
intpstore_mkfile(enumpstore_type_idtype,char*psname,u64id,intcount,
char*data,boolcompressed,size_tsize,
structtimespectime,structpstore_info*psi)
{
........................

rc=-ENOMEM;
inode=pstore_get_inode(pstore_sb);
..............................

switch(type){
casePSTORE_TYPE_DMESG:
scnprintf(name,sizeof(name),"dmesg-%s-%lld%s",
psname,id,compressed?".enc.z":"");
break;
casePSTORE_TYPE_CONSOLE:
scnprintf(name,sizeof(name),"console-%s-%lld",psname,id);
break;
casePSTORE_TYPE_FTRACE:
scnprintf(name,sizeof(name),"ftrace-%s-%lld",psname,id);
break;
casePSTORE_TYPE_MCE:
scnprintf(name,sizeof(name),"mce-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_RTAS:
scnprintf(name,sizeof(name),"rtas-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_OF:
scnprintf(name,sizeof(name),"powerpc-ofw-%s-%lld",
psname,id);
break;
casePSTORE_TYPE_PPC_COMMON:
scnprintf(name,sizeof(name),"powerpc-common-%s-%lld",
psname,id);
break;
casePSTORE_TYPE_PMSG:
scnprintf(name,sizeof(name),"pmsg-%s-%lld",psname,id);
break;
casePSTORE_TYPE_PPC_OPAL:
sprintf(name,"powerpc-opal-%s-%lld",psname,id);
break;
casePSTORE_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是enum pstore_type_id類(lèi)型的一個(gè)值,psname是給定的psname參數(shù),id是給定的id參數(shù)。

接著使用d_alloc_name函數(shù)為根目錄創(chuàng)建一個(gè)目錄項(xiàng)dentry,最后使用d_add函數(shù)將目錄項(xiàng)dentry與索引節(jié)點(diǎn)inode關(guān)聯(lián)起來(lái),將其添加到文件系統(tǒng)中。

pstore_register

ramoops負(fù)責(zé)把message write到某個(gè)ram區(qū)域上,platform負(fù)責(zé)從ram讀取存到/sys/fs/pstore,ok,先來(lái)看機(jī)制代碼platform.c。

backend需要用pstore_register來(lái)注冊(cè):

/*
*platformspecificpersistentstoragedriverregisterswith
*ushere.Ifpstoreisalreadymounted,calltheplatform
*readfunctionrightawaytopopulatethefilesystem.Ifnot
*thenthepstoremountcodewillcalluslatertofillout
*thefilesystem.
*/
intpstore_register(structpstore_info*psi)
{
structmodule*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);
...
/*
*Updatethemoduleparameterbackend,soitisvisible
*through/sys/module/pstore/parameters/backend
*/
backend=psi->name;

module_put(owner);

backend判斷確保一次只能有一個(gè)并記錄了全局psinfo。

看下結(jié)構(gòu)體pstore_info:

structpstore_info{
structmodule*owner;
char*name;
spinlock_tbuf_lock;/*serializeaccessto'buf'*/
char*buf;
size_tbufsize;
structmutexread_mutex;/*serializeopen/read/close*/
intflags;
int(*open)(structpstore_info*psi);
int(*close)(structpstore_info*psi);
ssize_t(*read)(u64*id,enumpstore_type_id*type,
int*count,structtimespec*time,char**buf,
bool*compressed,ssize_t*ecc_notice_size,
structpstore_info*psi);
int(*write)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,intcount,boolcompressed,
size_tsize,structpstore_info*psi);
int(*write_buf)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,constchar*buf,boolcompressed,
size_tsize,structpstore_info*psi);
int(*write_buf_user)(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,u64*id,
unsignedintpart,constchar__user*buf,
boolcompressed,size_tsize,structpstore_info*psi);
int(*erase)(enumpstore_type_idtype,u64id,
intcount,structtimespectime,
structpstore_info*psi);
void*data;
};

name就是backend的name了。

*write和*write_buf_user如果backend沒(méi)有給出會(huì)有個(gè)默認(rèn)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;
staticintpstore_write_compat(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,
u64*id,unsignedintpart,intcount,
boolcompressed,size_tsize,
structpstore_info*psi)
{
returnpsi->write_buf(type,reason,id,part,psinfo->buf,compressed,
size,psi);
}

staticintpstore_write_buf_user_compat(enumpstore_type_idtype,
enumkmsg_dump_reasonreason,
u64*id,unsignedintpart,
constchar__user*buf,
boolcompressed,size_tsize,
structpstore_info*psi)
{
...
ret=psi->write_buf(type,reason,id,part,psinfo->buf,
...
}

繼續(xù)pstore注冊(cè):

if(pstore_is_mounted())
pstore_get_records(0);

如果pstore已經(jīng)mounted,那就創(chuàng)建并填充文件by pstore_get_records:

/*
*Readalltherecordsfromthepersistentstore.Create
*filesinourfilesystem.Don'twarnabout-EEXISTerrors
*whenwearere-scanningthebackingstorelookingtoaddnew
*errorrecords.
*/
voidpstore_get_records(intquiet)
{
structpstore_info*psi=psinfo;//tj:globalpsinfo
...
mutex_lock(&psi->read_mutex);
if(psi->open&&psi->open(psi))
gotoout;

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("decompressionfailed;returned%d
",
unzipped_len);
compressed=true;
}
}
rc=pstore_mkfile(type,psi->name,id,count,buf,
compressed,size+ecc_notice_size,
time,psi);
if(unzipped_lenclose)
psi->close(psi);
out:
mutex_unlock(&psi->read_mutex);

if needed,call pstore_decompress解壓然后創(chuàng)建pstore文件by vfs接口pstore_mkfile。

pstore注冊(cè)接下來(lái)是按類(lèi)別分別注冊(cè):

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注冊(cè)

staticstructkmsg_dumperpstore_dumper={
.dump=pstore_dump,
};

/*
*Registerwithkmsg_dumptosavelastpartofconsolelogonpanic.
*/
staticvoidpstore_register_kmsg(void)
{
kmsg_dump_register(&pstore_dumper);
}

pstore_dump最終會(huì)call backend的write,直接用全局psinfo。

/*
*callbackfromkmsg_dump.(s2,l2)hasthemostrecently
*writtenbytes,olderbytesarein(s1,l1).Saveasmuch
*aswecanfromtheendofthebuffer.
*/
staticvoidpstore_dump(structkmsg_dumper*dumper,
enumkmsg_dump_reasonreason)
{
...
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-registerakernellogdumper.
*@dumper:pointertothekmsg_dumperstructure
*
*Addsakernellogdumpertothesystem.Thedumpcallbackinthe
*structurewillbecalledwhenthekerneloopsesorpanicsandmustbe
*set.Returnszeroonsuccessand%-EINVALor%-EBUSYotherwise.
*/
intkmsg_dump_register(structkmsg_dumper*dumper)
{
unsignedlongflags;
interr=-EBUSY;

/*Thedumpcallbackneedstobeset*/
if(!dumper->dump)
return-EINVAL;

spin_lock_irqsave(&dump_list_lock,flags);
/*Don'tallowregisteringmultipletimes*/
if(!dumper->registered){
dumper->registered=1;
list_add_tail_rcu(&dumper->list,&dump_list);
err=0;
}
spin_unlock_irqrestore(&dump_list_lock,flags);

returnerr;
}
/**
*kmsg_dump-dumpkernellogtokernelmessagedumpers.
*@reason:thereason(oops,panicetc)fordumping
*
*Calleachoftheregistereddumper'sdump()callback,whichcan
*retrievethekmsgrecordswithkmsg_dump_get_line()or
*kmsg_dump_get_buffer().
*/
voidkmsg_dump(enumkmsg_dump_reasonreason)
{
structkmsg_dumper*dumper;
unsignedlongflags;

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;

/*initializeiteratorwithdataaboutthestoredrecords*/
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);

/*invokedumperwhichwilliterateoverrecords*/
dumper->dump(dumper,reason);

/*resetiterator*/
dumper->active=false;
}
rcu_read_unlock();
}

pstore console 注冊(cè)

staticstructconsolepstore_console={
.name="pstore",
.write=pstore_console_write,
.flags=CON_PRINTBUFFER|CON_ENABLED|CON_ANYTIME,
.index=-1,
};

staticvoidpstore_register_console(void)
{
register_console(&pstore_console);
}

->write最終也會(huì)call backend write:

#ifdefCONFIG_PSTORE_CONSOLE
staticvoidpstore_console_write(structconsole*con,constchar*s,unsignedc)
{
constchar*e=s+c;

while(spsinfo->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

下面來(lái)看下RAM backend: ramoops,先看probe:

staticintramoops_probe(structplatform_device*pdev)
{
structdevice*dev=&pdev->dev;
structramoops_platform_data*pdata=dev->platform_data;
...

if(!pdata->mem_size||(!pdata->record_size&&!pdata->console_size&&
!pdata->ftrace_size&&!pdata->pmsg_size)){
pr_err("Thememorysizeandtherecord/consolesizemustbe"
"non-zero
");
gotofail_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應(yīng)該來(lái)源ramoops_register_dummy:

staticvoidramoops_register_dummy(void)
{
...
pr_info("usingmoduleparameters
");

dummy_data=kzalloc(sizeof(*dummy_data),GFP_KERNEL);
if(!dummy_data){
pr_info("couldnotallocatepdata
");
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;
/*
*Forbackwardscompatibilityramoops.ecc=1means16bytesECC
*(using1byteforECCisn'tmuchofuseanyway).
*/
dummy_data->ecc_info.ecc_size=ramoops_ecc==1?16:ramoops_ecc;

dummy=platform_device_register_data(NULL,"ramoops",-1,
dummy_data,sizeof(structramoops_platform_data));

有幾個(gè)可配參數(shù):

/*
*Ramoopsplatformdata
*@mem_sizememorysizeforramoops
*@mem_addressphysicalmemoryaddresstocontainramoops
*/

structramoops_platform_data{
unsignedlongmem_size;
phys_addr_tmem_address;
unsignedintmem_type;
unsignedlongrecord_size;
unsignedlongconsole_size;
unsignedlongftrace_size;
unsignedlongpmsg_size;
intdump_oops;
structpersistent_ram_ecc_infoecc_info;
};

mem_size:用于Ramoops的內(nèi)存大小,表示分配給Ramoops的物理內(nèi)存的大小。

mem_address:用于Ramoops的物理內(nèi)存地址,指定用于存儲(chǔ)Ramoops的物理內(nèi)存的起始地址。

mem_type:內(nèi)存類(lèi)型,用于進(jìn)一步描述內(nèi)存的屬性和特征。

record_size:每個(gè)記錄的大小

console_size:控制臺(tái)記錄的大小

ftrace_size:Ftrace記錄的大小

pmsg_size:pmsg消息記錄的大小

dump_oops:是否轉(zhuǎn)儲(chǔ)oops信息的標(biāo)志,表示是否將oops信息轉(zhuǎn)儲(chǔ)到Ramoops中。

ecc_info:RAM的ECC(糾錯(cuò)碼)信息,用于提供關(guān)于ECC配置和處理的詳細(xì)信息。

有個(gè)結(jié)構(gòu)表示了ramoops的context:

structramoops_context{
structpersistent_ram_zone**przs;
structpersistent_ram_zone*cprz;
structpersistent_ram_zone*fprz;
structpersistent_ram_zone*mprz;
phys_addr_tphys_addr;
unsignedlongsize;
unsignedintmemtype;
size_trecord_size;
size_tconsole_size;
size_tftrace_size;
size_tpmsg_size;
intdump_oops;
structpersistent_ram_ecc_infoecc_info;
unsignedintmax_dump_cnt;
unsignedintdump_write_cnt;
/*_read_cntneedclearonramoops_pstore_open*/
unsignedintdump_read_cnt;
unsignedintconsole_read_cnt;
unsignedintftrace_read_cnt;
unsignedintpmsg_read_cnt;
structpstore_infopstore;
};

在ramoops_probe時(shí)也是把ramoops_platform_data的成員賦給了context對(duì)應(yīng)的。要了解具體含義,繼續(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)
gotofail_out;

err=ramoops_init_prz(dev,cxt,&cxt->cprz,&paddr,
cxt->console_size,0);
if(err)
gotofail_init_cprz;

err=ramoops_init_prz(dev,cxt,&cxt->fprz,&paddr,cxt->ftrace_size,
LINUX_VERSION_CODE);
if(err)
gotofail_init_fprz;

err=ramoops_init_prz(dev,cxt,&cxt->mprz,&paddr,cxt->pmsg_size,0);
if(err)
gotofail_init_mprz;

cxt->pstore.data=cxt;

可見(jiàn),是逐個(gè)init每個(gè)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:

staticintramoops_init_przs(structdevice*dev,structramoops_context*cxt,
phys_addr_t*paddr,size_tdump_mem_sz)
{
interr=-ENOMEM;
inti;

if(!cxt->record_size)
return0;

if(*paddr+dump_mem_sz-cxt->phys_addr>cxt->size){
dev_err(dev,"noroomfordumps
");
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個(gè),每個(gè)記錄大小是record_size。

接著會(huì)call persistent_ram_new來(lái)分配內(nèi)存給這個(gè)ram zone。

for(i=0;imax_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并注冊(cè)pstore:

cxt->pstore.flags=PSTORE_FLAGS_DMESG;//tj:默認(rèn)dumpoops/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("registeringwithpstorefailed
");
gotofail_buf;
}

來(lái)看下ramoops pstore的定義的callback,他們通過(guò)全局psinfo而來(lái):

staticstructramoops_contextoops_cxt={
.pstore={
.owner=THIS_MODULE,
.name="ramoops",
.open=ramoops_pstore_open,
.read=ramoops_pstore_read,//psi->read
.write_buf=ramoops_pstore_write_buf,//fornonpmsg
.write_buf_user=ramoops_pstore_write_buf_user,//forpmsg
.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,不能掉電,只能依靠自動(dòng)重啟機(jī)制來(lái)查看,故而要配置:CONFIG_PANIC_TIMEOUT,讓系統(tǒng)在 panic 后能自動(dòng)重啟。

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,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfsmtdoops.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,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfsmtdoops.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,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfspstore_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,115200loglevel=8rootwaitroot=/dev/mtdblock5rootfstype=squashfspstore_blk.blkdev=pstore";
partition@60000{
label="pstore";
reg=<0x60000?0x20000>;
};

pstore fs

掛載pstore文件系統(tǒng)

mount-tpstorepstore/sys/fs/pstore

掛載后,通過(guò)mount能看到類(lèi)似這樣的信息:

#mount
pstoreon/sys/fs/pstoretypepstore(rw,relatime)

如果需要驗(yàn)證,可以這樣主動(dòng)觸發(fā)內(nèi)核崩潰:

#echoc>/proc/sysrq-trigger

不同配置方式日志名稱(chēng)不同

ramoops

#mount-tpstorepstore/sys/fs/pstore/
#cd/sys/fs/pstore/
#ls
console-ramoops-0dmesg-ramoops-0dmesg-ramoops-1

mtdoops

#cat/dev/mtd3>1.txt
#cat1.txt

blkoops

cd/sys/fs/pstore/
ls
dmesg-pstore_blk-0dmesg-pstore_blk-1

總結(jié)

pstore setup 流程:

ramoops_init
ramoops_register_dummy
ramoops_probe
ramoops_register

查看 pstore 數(shù)據(jù)保存流程:

registerapstore_dumper
//whenpanichappens,kmsg_dumpiscalled
calldumper->dump
pstore_dump

查看 pstore 數(shù)據(jù)讀取流程:

ramoops_probe
persistent_ram_post_init
pstore_register
pstore_get_records
ramoops_pstore_read
pstore_decompress(onlyfordmesg)
pstore_mkfile(savetofiles)



審核編輯:劉清
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • RAM
    RAM
    +關(guān)注

    關(guān)注

    8

    文章

    1391

    瀏覽量

    116905
  • DDR
    DDR
    +關(guān)注

    關(guān)注

    11

    文章

    731

    瀏覽量

    66459
  • ECC
    ECC
    +關(guān)注

    關(guān)注

    0

    文章

    97

    瀏覽量

    20984
  • vfs
    vfs
    +關(guān)注

    關(guān)注

    0

    文章

    14

    瀏覽量

    5378

原文標(biāo)題:【調(diào)試】pstore原理和使用方法總結(jié)

文章出處:【微信號(hào):嵌入式與Linux那些事,微信公眾號(hào):嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 0人收藏

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法總結(jié)

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法總結(jié)
    發(fā)表于 05-14 21:48

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法總結(jié)

    BTool、PacketSniffer、BLE_Device_Monitor、USBDongle使用方法總結(jié)
    發(fā)表于 05-15 15:29

    轉(zhuǎn):Keil的使用方法 - 常用功能(一)

    單片機(jī)或者嵌入式開(kāi)發(fā)人員的不二選擇。 從今天開(kāi)始的接下來(lái)一些時(shí)間總結(jié)關(guān)于Keil的使用方法。計(jì)劃前一階段總結(jié)常見(jiàn)功能,后面綜合性的總結(jié)Keil的功能。 關(guān)注我的朋友應(yīng)該知道,我前面已經(jīng)
    發(fā)表于 06-13 10:42

    轉(zhuǎn):Keil的使用方法 - 常用功能(二)

    Ⅰ、概述 上一篇文章是總結(jié)關(guān)于Keil使用方法-常用功能(一),關(guān)于(文件和編譯)工具欄每一個(gè)按鈕的功能描述和快捷鍵的使用。我將每一篇Keil使用方法的文章都匯總在一起,回顧前面的總結(jié)
    發(fā)表于 06-13 10:42

    GPIO的常用庫(kù)函數(shù)使用方法總結(jié)

    +GPIOx_LCKR作用和配置STM32F103五分鐘入門(mén)系列(三)GPIO的常用庫(kù)函數(shù)使用方法總結(jié)+一個(gè)網(wǎng)絡(luò)上的誤區(qū)各類(lèi)時(shí)鐘的實(shí)現(xiàn)+各類(lèi)時(shí)鐘寄存器總結(jié)前言一、STM32的時(shí)鐘源(一)時(shí)鐘類(lèi)型(二)時(shí)鐘框圖(極其重要)前言之前
    發(fā)表于 08-23 09:19

    總結(jié)一下串口的幾種使用方法

    使用方法對(duì)以后的開(kāi)發(fā)還是很有幫助的。有關(guān)串口的知識(shí)我在之前的博客中有介紹:點(diǎn)擊鏈接跳轉(zhuǎn)一.僅向上位機(jī)打印調(diào)試信息單純利用串口向上位機(jī)打印調(diào)試信息,程序如下:void USART1_Init( uin...
    發(fā)表于 02-10 08:03

    介紹SPI的使用方法

    ,這篇介紹SPI的使用方法,流程與TIM類(lèi)似。大致總結(jié)為以下幾個(gè)步驟:在RT-thread settings中使能對(duì)應(yīng)的驅(qū)動(dòng)框架在stm32f4xx_hal_conf.h中使能對(duì)應(yīng)的模塊(HAL_XX_MODULE_ENABLED)在board.h中啟用外設(shè)類(lèi)型對(duì)應(yīng)的宏定
    發(fā)表于 02-17 06:32

    示波器的使用方法

    數(shù)字萬(wàn)用表使用方法和示波器的使用方法詳解。
    發(fā)表于 03-14 10:38 ?32次下載

    對(duì)于無(wú)線對(duì)講機(jī)日常使用的常見(jiàn)故障及正確的使用方法總結(jié)

    總結(jié)一些日常使用中最常見(jiàn)的故障及正確的使用方法作一些簡(jiǎn)單介紹,供對(duì)講機(jī)用戶參考。
    的頭像 發(fā)表于 12-25 08:41 ?7348次閱讀

    示波器的使用方法(三):示波器的使用方法詳解

    示波器的使用方法并非很難,重點(diǎn)在于正確使用示波器的使用方法。往期文章中,小編對(duì)模擬示波器的使用方法和數(shù)字示波器的使用方法均有所介紹。為增進(jìn)大家對(duì)示波器的
    的頭像 發(fā)表于 12-24 20:37 ?3744次閱讀

    DWIN屏使用方法總結(jié)(上)

    DWIN屏使用方法總結(jié)(上)DWIN屏使用方法總結(jié)(上)DWIN屏介紹開(kāi)發(fā)工具ICL生成CFG修改工程建立與下載工程建立文件下載總結(jié)DWIN
    發(fā)表于 12-02 14:21 ?12次下載
    DWIN屏<b class='flag-5'>使用方法</b><b class='flag-5'>總結(jié)</b>(上)

    DWIN屏使用方法總結(jié)(下)

    DWIN屏使用方法總結(jié)(下)DWIN屏使用方法總結(jié)(下)數(shù)據(jù)幀常用的系統(tǒng)指令常用控件基礎(chǔ)觸控按鍵返回?cái)?shù)據(jù)變量錄入圖標(biāo)變量數(shù)據(jù)變量顯示總結(jié)DW
    發(fā)表于 12-31 18:56 ?10次下載
    DWIN屏<b class='flag-5'>使用方法</b><b class='flag-5'>總結(jié)</b>(下)

    新型存儲(chǔ)技術(shù):新型SCM類(lèi)介質(zhì)的特性及使用方法總結(jié)和介紹

    Storage Class Memory (SCM)是非易失性?xún)?nèi)存,該類(lèi)介質(zhì)的存取速度略比內(nèi)存慢,但是遠(yuǎn)快于NAND類(lèi)介質(zhì)。本文對(duì)該類(lèi)介質(zhì)的特性及使用方法做了簡(jiǎn)單總結(jié)和介紹。
    發(fā)表于 01-15 15:07 ?2785次閱讀

    使用電容器降低噪聲-去耦電容的有效使用方法總結(jié)

    前面分三次對(duì)“去耦電容的有效使用方法”進(jìn)行了介紹。利用電容來(lái)降低噪聲是非常重要的,所以在這里總結(jié)一下。
    發(fā)表于 02-15 16:12 ?1181次閱讀

    內(nèi)網(wǎng)穿透工具的種類(lèi)、原理和使用方法

    本文以滲透的視角,總結(jié)幾種個(gè)人常用的內(nèi)網(wǎng)穿透,內(nèi)網(wǎng)代理工具,介紹其簡(jiǎn)單原理和使用方法。
    的頭像 發(fā)表于 08-25 10:35 ?3600次閱讀
    內(nèi)網(wǎng)穿透工具的種類(lèi)、原理和<b class='flag-5'>使用方法</b>

    電子發(fā)燒友

    中國(guó)電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會(huì)員交流學(xué)習(xí)
    • 獲取您個(gè)性化的科技前沿技術(shù)信息
    • 參加活動(dòng)獲取豐厚的禮品