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, size_t hsize, 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; } else if (type == PSTORE_TYPE_FTRACE) { if (!cxt->fprz) return -ENOMEM; persistent_ram_write(cxt->fprz, buf, size); return 0; } if (type != PSTORE_TYPE_DMESG) return -EINVAL; /* Out of the various dmesg dump types, ramoops is currently designed * to only store crash logs, rather than storing general kernel logs. */ if (reason != KMSG_DUMP_OOPS && reason != KMSG_DUMP_PANIC) return -EINVAL; /* Skip Oopes when configured to do so. */ if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) return -EINVAL; /* Explicitly only take the first part of any new crash. * If our buffer is larger than kmsg_bytes, this can never happen, * and if our buffer is smaller than kmsg_bytes, we don't want the * report split across multiple records. */ if (part != 1) return -ENOSPC; if (!cxt->przs) return -ENOSPC; prz = cxt->przs[cxt->dump_write_cnt]; hlen = ramoops_write_kmsg_hdr(prz); if (size + hlen > prz->buffer_size) size = prz->buffer_size - hlen; persistent_ram_write(prz, buf, size); cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; return 0; }
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, 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; } else if (type == PSTORE_TYPE_FTRACE) { if (!cxt->fprz) return -ENOMEM; persistent_ram_write(cxt->fprz, buf, size); return 0; } if (type != PSTORE_TYPE_DMESG) return -EINVAL; if (reason != KMSG_DUMP_OOPS && reason != KMSG_DUMP_PANIC) return -EINVAL; if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) return -EINVAL; if (part != 1) return -ENOSPC; if (!cxt->przs) return -ENOSPC; prz = cxt->przs[cxt->dump_write_cnt]; hlen = ramoops_write_kmsg_hdr(prz); if (size + hlen > prz->buffer_size) size = prz->buffer_size - hlen; persistent_ram_write(prz, buf, size); cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; return 0; }
static void persistent_trace_call(unsigned long ip, unsigned long parent_ip) { struct trace_array *tr = persistent_trace_array; struct trace_array_cpu *data; long disabled; struct persistent_trace_record rec; unsigned long flags; int cpu; smp_rmb(); if (unlikely(!persistent_trace_enabled)) return; if (unlikely(oops_in_progress)) return; /* * Need to use raw, since this must be called before the * recursive protection is performed. */ local_irq_save(flags); cpu = raw_smp_processor_id(); data = tr->data[cpu]; disabled = atomic_inc_return(&data->disabled); if (likely(disabled == 1)) { rec.ip = ip; rec.parent_ip = parent_ip; rec.ip |= cpu; persistent_ram_write(persistent_trace, &rec, sizeof(rec)); } atomic_dec(&data->disabled); local_irq_restore(flags); }
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) { char *hdr; struct timeval timestamp; size_t len; do_gettimeofday(×tamp); hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); WARN_ON_ONCE(!hdr); len = hdr ? strlen(hdr) : 0; persistent_ram_write(prz, hdr, len); kfree(hdr); return len; }
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, struct pstore_record *record) { char *hdr; size_t len; hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lld.%06lu-%c\n", (time64_t)record->time.tv_sec, record->time.tv_nsec / 1000, record->compressed ? 'C' : 'D'); WARN_ON_ONCE(!hdr); len = hdr ? strlen(hdr) : 0; persistent_ram_write(prz, hdr, len); kfree(hdr); return len; }
static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) { char *hdr; struct timespec timestamp; size_t len; /* Report zeroed timestamp if called before timekeeping has resumed. */ if (__getnstimeofday(×tamp)) { timestamp.tv_sec = 0; timestamp.tv_nsec = 0; } hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n", (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000)); WARN_ON_ONCE(!hdr); len = hdr ? strlen(hdr) : 0; persistent_ram_write(prz, hdr, len); kfree(hdr); return len; }
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; } else if (type == PSTORE_TYPE_FTRACE) { int zonenum; if (!cxt->fprzs) return -ENOMEM; /* * Choose zone by if we're using per-cpu buffers. */ if (cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU) zonenum = smp_processor_id(); else zonenum = 0; persistent_ram_write(cxt->fprzs[zonenum], buf, size); return 0; } else if (type == PSTORE_TYPE_PMSG) { pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__); return -EINVAL; } if (type != PSTORE_TYPE_DMESG) return -EINVAL; /* Out of the various dmesg dump types, ramoops is currently designed * to only store crash logs, rather than storing general kernel logs. */ if (reason != KMSG_DUMP_OOPS && reason != KMSG_DUMP_PANIC) return -EINVAL; /* Skip Oopes when configured to do so. */ if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) return -EINVAL; /* Explicitly only take the first part of any new crash. * If our buffer is larger than kmsg_bytes, this can never happen, * and if our buffer is smaller than kmsg_bytes, we don't want the * report split across multiple records. */ if (part != 1) return -ENOSPC; if (!cxt->dprzs) return -ENOSPC; prz = cxt->dprzs[cxt->dump_write_cnt]; hlen = ramoops_write_kmsg_hdr(prz, compressed); if (size + hlen > prz->buffer_size) size = prz->buffer_size - hlen; persistent_ram_write(prz, buf, size); cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; return 0; }
static void ram_console_write(struct console *console, const char *s, unsigned int count) { struct persistent_ram_zone *prz = console->data; persistent_ram_write(prz, s, count); }
void notrace ramoops_console_write_buf(const char *buf, size_t size) { struct ramoops_context *cxt = &oops_cxt; persistent_ram_write(cxt->cprz, buf, size); }