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
:
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);
再看下buffer_start_add
:
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); ... return old;
这里就有几个疑问了:
prz->buffer
的->size
和->start
初始值如何定义的?
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);
这里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) { ... 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); 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 ;
扫描整个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 ; 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 ; 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; 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); return 0 ;
有些时候,我们需要在kernel起来查看bootloader log,这里提供linux修改部分(基于4.9)参考,我本地验证ok:
版权声明: 本站所有文章均采用 CC BY-NC-SA 4.0 CN 许可协议。转载请注明原文链接!