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,这里提供个基于4.9的修改可以参考,我本地验证ok:

剩余内容付款8.00元后3天内可查看