pstore读写接口分别是ramoops_pstore_read()ramoops_pstore_write_buf(),参考代码kernel4.9:

static struct ramoops_context oops_cxt = {
.pstore = {
.owner = THIS_MODULE,
.name = "ramoops",
.open = ramoops_pstore_open,
.read = ramoops_pstore_read,
.write_buf = ramoops_pstore_write_buf,
.write_buf_user = ramoops_pstore_write_buf_user,
.erase = ramoops_pstore_erase,
},
};

我们先看write,pstore是写了后才有读出。

ramoops_pstore_write_buf会call persistent_ram_write:

static int notrace ramoops_pstore_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)
{
struct ramoops_context *cxt = psi->data;
struct persistent_ram_zone *prz;
size_t hlen;

if (type == PSTORE_TYPE_CONSOLE) {
if (!cxt->cprz)
return -ENOMEM;
persistent_ram_write(cxt->cprz, buf, size);
return 0;

backend ramoops write:

int notrace persistent_ram_write(struct persistent_ram_zone *prz,
const void *s, unsigned int count)
{
int rem;
int c = count;
size_t start;

if (unlikely(c > prz->buffer_size)) {
s += c - prz->buffer_size;
c = prz->buffer_size;
}

buffer_size_add(prz, c);

start = buffer_start_add(prz, c);

rem = prz->buffer_size - start;
if (unlikely(rem < c)) {
persistent_ram_update(prz, s, start, rem);
s += rem;
c -= rem;
start = 0;
}
persistent_ram_update(prz, s, start, c);

persistent_ram_update_header_ecc(prz);

return count;
}

看样子是在确定buffer大小和地址后call persistent_ram_update来写buffer,我们先看下buffer_size_add:

/* increase the size counter until it hits the max size */
static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
{
size_t old;
size_t new;
unsigned long flags = 0;

if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_lock_irqsave(&prz->buffer_lock, flags);

old = atomic_read(&prz->buffer->size);
if (old == prz->buffer_size)
goto exit;

new = old + a;
if (new > prz->buffer_size)
new = prz->buffer_size;
atomic_set(&prz->buffer->size, new); //设置prz->buffer->size

再看下buffer_start_add:

/* increase and wrap the start pointer, returning the old value */
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{
int old;
int new;
unsigned long flags = 0;

if (!(prz->flags & PRZ_FLAG_NO_LOCK))
raw_spin_lock_irqsave(&prz->buffer_lock, flags);

old = atomic_read(&prz->buffer->start);
new = old + a;
while (unlikely(new >= prz->buffer_size))
new -= prz->buffer_size;
atomic_set(&prz->buffer->start, new); //设置prz->buffer->start
...
return old;

这里就有几个疑问了:

  1. prz->buffer->size->start初始值如何定义的?
  2. What is prz->buffer->start, prz->buffer->start, prz->buffer_size

问题1:prz init时会call persistent_ram_zap初始化为0:

void persistent_ram_zap(struct persistent_ram_zone *prz)
{
atomic_set(&prz->buffer->start, 0);
atomic_set(&prz->buffer->size, 0);

问题2:ram在分配时会设置prz->buffer_size:

static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
struct persistent_ram_zone *prz, int memtype)
{
prz->paddr = start;
prz->size = size;

if (pfn_valid(start >> PAGE_SHIFT))
prz->vaddr = persistent_ram_vmap(start, size, memtype);
else
prz->vaddr = persistent_ram_iomap(start, size, memtype);

if (!prz->vaddr) {
pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
(unsigned long long)size, (unsigned long long)start);
return -ENOMEM;
}

prz->buffer = prz->vaddr + offset_in_page(start);
prz->buffer_size = size - sizeof(struct persistent_ram_buffer); //tj: 这里设置

这里size就是每个zone配置的大小:

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

->buffer偏移了start,所以应该是zone的开头保留了一个persistent_ram_buffer大小:

struct persistent_ram_buffer {
uint32_t sig;
atomic_t start;
atomic_t size;
uint8_t data[0];
};

这个data[0]后面才是如console message等。回过头来再看persistent_ram_write就通了。

再来看read,read是在pstore文件系统准备好就开始:

ramoops_probe -> pstore_register -> pstore_get_records (if pstore is mounted) -> (psi->read)
static ssize_t ramoops_pstore_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)
{
...
/* Find the next valid persistent_ram_zone for DMESG */
while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) {
prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, id, type,
PSTORE_TYPE_DMESG, 1);
if (!prz_ok(prz))
continue;
header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz),
time, compressed);
/* Clear and skip this DMESG record if it has no valid header */
if (!header_length) {
persistent_ram_free_old(prz);
persistent_ram_zap(prz);
prz = NULL;
}
}

if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
1, id, type, PSTORE_TYPE_CONSOLE, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
1, id, type, PSTORE_TYPE_FTRACE, 0);
if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, id, type, PSTORE_TYPE_PMSG, 0);
if (!prz_ok(prz))
return 0; //tj: 一个也没有找到

扫描整个pstore ram区域,通过call ramoops_get_next_prz来找到一个有效的persist ram zone。

看下ramoops_get_next_pr:

static struct persistent_ram_zone *
ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
u64 *id,
enum pstore_type_id *typep, enum pstore_type_id type,
bool update)
{
struct persistent_ram_zone *prz;
int i = (*c)++;

if (i >= max)
return NULL;

prz = przs[i];
if (!prz)
return NULL;

/* Update old/shadowed buffer. */
if (update)
persistent_ram_save_old(prz);

if (!persistent_ram_old_size(prz))
return NULL;

*typep = type;
*id = i;

return prz;
}

console没有update,看下persistent_ram_old_size:

size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
{
return prz->old_log_size;
}

old_log_size要非0才能找到,在persistent_ram_save_old会保存:

void persistent_ram_save_old(struct persistent_ram_zone *prz)
{
struct persistent_ram_buffer *buffer = prz->buffer;
size_t size = buffer_size(prz);
size_t start = buffer_start(prz);

if (!size)
return; //tj: 只有写过这里才会pass

if (!prz->old_log) {
persistent_ram_ecc_old(prz);
prz->old_log = kmalloc(size, GFP_KERNEL);
}
if (!prz->old_log) {
pr_err("failed to allocate buffer\n");
return;
}

prz->old_log_size = size; //tj: 这里保存
memcpy_fromio(prz->old_log, &buffer->data[start], size - start);
memcpy_fromio(prz->old_log + size - start, &buffer->data[0], start);
}

可见,这里old_log_size就是由buffer_size而来。console在下面保留的:

static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
struct persistent_ram_ecc_info *ecc_info)
{
int ret;

ret = persistent_ram_init_ecc(prz, ecc_info);
if (ret)
return ret;

sig ^= PERSISTENT_RAM_SIG;

if (prz->buffer->sig == sig) {
if (buffer_size(prz) > prz->buffer_size ||
buffer_start(prz) > buffer_size(prz))
pr_info("found existing invalid buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
else {
pr_debug("found existing buffer, size %zu, start %zu\n",
buffer_size(prz), buffer_start(prz));
persistent_ram_save_old(prz); //tj: 找到就保存
return 0;

有些时候,我们需要在kernel起来查看bootloader log,这里提供linux修改部分(基于4.9)参考,我本地验证ok: