成人免费无码不卡毛片,亚洲AⅤ无码精品一区二区三区,国产尤物精品视频,久久精品日本亚洲,欧美成人一区三区无码乱码A片,中文字日产幕码一区二区色哟哟,亞洲日韓中文字幕網AV

  • 正文
    • 什么是pstore
    • pstore工作原理
    • pstore使用方法
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

【調(diào)試】pstore原理和使用方法總結(jié)

2024/03/04
4581
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

什么是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>,其中typeenum 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_kmsgpstore_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

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
M24512-RDW6TP 1 STMicroelectronics 512 Kbit serial I<sup>2</sup>C bus EEPROM

ECAD模型

下載ECAD模型
$0.63 查看
HFBR-1531Z 1 Broadcom Limited Transmitter, Through Hole Mount, ROHS COMPLIANT PACKAGE

ECAD模型

下載ECAD模型
$17.74 查看
FTLF8519P3BTL 1 Finisar Corporation Transceiver, 840nm Min, 860nm Max, 2125Mbps(Tx), 2125Mbps(Rx), LC Connector, Panel Mount, ROHS COMPLIANT PACKAGE
$218.87 查看

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

作者就職于某500強公司,擔任BSP工程師。具有豐富的嵌入式開發(fā)經(jīng)驗。專欄主要分享計算機基礎(chǔ),操作系統(tǒng),Linux驅(qū)動開發(fā),Arm體系與架構(gòu),C/C++,數(shù)據(jù)結(jié)構(gòu)與算法等相關(guān)文章。歡迎關(guān)注我的公眾號【嵌入式與Linux那些事】,一起學習交流。

吴川市| 鄯善县| 濮阳县| 双峰县| 清徐县| 吉隆县| 淳化县| 泰兴市| 昆明市| 达拉特旗| 寿光市| 舟山市| 平阳县| 罗田县| 宝兴县| 石泉县| 南召县| 新和县| 南木林县| 茶陵县| 陵川县| 青神县| 灵台县| 和龙市| 北海市| 电白县| 南和县| 济宁市| 洪湖市| 连山| 衡东县| 岫岩| 茂名市| 平舆县| 甘德县| 金秀| 萝北县| 沁源县| 高邑县| 洪江市| 南宁市|