/* * 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. * * Register with kmsg_dump to save last part of console log on panic. */ int pstore_register(struct pstore_info *psi) { struct module *owner = psi->owner; spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; } if (backend && strcmp(backend, psi->name)) { spin_unlock(&pstore_lock); return -EINVAL; } psinfo = psi; mutex_init(&psinfo->read_mutex); spin_unlock(&pstore_lock); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } if (pstore_is_mounted()) pstore_get_records(0); kmsg_dump_register(&pstore_dumper); pstore_timer.expires = jiffies + PSTORE_INTERVAL; add_timer(&pstore_timer); return 0; }
/* * 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. * * Register with kmsg_dump to save last part of console log on panic. */ int pstore_register(struct pstore_info *psi) { struct module *owner = psi->owner; spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; } if (backend && strcmp(backend, psi->name)) { spin_unlock(&pstore_lock); return -EINVAL; } psinfo = psi; spin_unlock(&pstore_lock); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } if (pstore_is_mounted()) pstore_get_records(); kmsg_dump_register(&pstore_dumper); return 0; }
/* * 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, const char *s1, unsigned long l1, const char *s2, unsigned long l2) { unsigned long s1_start, s2_start; unsigned long l1_cpy, l2_cpy; unsigned long size, total = 0; char *dst; const char *why; u64 id; int hsize, ret; unsigned int part = 1; unsigned long flags = 0; int is_locked = 0; why = get_reason_str(reason); if (pstore_cannot_block_path(reason)) { is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags); if (!is_locked) { pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" , in_nmi() ? "NMI" : why); } } else spin_lock_irqsave(&psinfo->buf_lock, flags); oopscount++; while (total < kmsg_bytes) { dst = psinfo->buf; hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); size = psinfo->bufsize - hsize; dst += hsize; l2_cpy = min(l2, size); l1_cpy = min(l1, size - l2_cpy); if (l1_cpy + l2_cpy == 0) break; s2_start = l2 - l2_cpy; s1_start = l1 - l1_cpy; memcpy(dst, s1 + s1_start, l1_cpy); memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); ret = psinfo->write(PSTORE_TYPE_DMESG, &id, part, hsize + l1_cpy + l2_cpy, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; l1 -= l1_cpy; l2 -= l2_cpy; total += l1_cpy + l2_cpy; part++; } if (pstore_cannot_block_path(reason)) { if (is_locked) spin_unlock_irqrestore(&psinfo->buf_lock, flags); } else spin_unlock_irqrestore(&psinfo->buf_lock, flags); }
/* * 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); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } allocate_buf_for_compression(); if (pstore_is_mounted()) pstore_get_records(0); 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(); if (pstore_update_ms >= 0) { pstore_timer.expires = jiffies + msecs_to_jiffies(pstore_update_ms); add_timer(&pstore_timer); } /* * Update the module parameter backend, so it is visible * through /sys/module/pstore/parameters/backend */ backend = psi->name; module_put(owner); pr_info("Registered %s as persistent store backend\n", psi->name); return 0; }
/* * 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, const char *s1, unsigned long l1, const char *s2, unsigned long l2) { unsigned long s1_start, s2_start; unsigned long l1_cpy, l2_cpy; unsigned long size, total = 0; char *dst, *why; u64 id; int hsize; unsigned int part = 1; if (reason < ARRAY_SIZE(reason_str)) why = reason_str[reason]; else why = "Unknown"; mutex_lock(&psinfo->buf_mutex); oopscount++; while (total < kmsg_bytes) { dst = psinfo->buf; hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); size = psinfo->bufsize - hsize; dst += hsize; l2_cpy = min(l2, size); l1_cpy = min(l1, size - l2_cpy); if (l1_cpy + l2_cpy == 0) break; s2_start = l2 - l2_cpy; s1_start = l1 - l1_cpy; memcpy(dst, s1 + s1_start, l1_cpy); memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); id = psinfo->write(PSTORE_TYPE_DMESG, part, hsize + l1_cpy + l2_cpy, psinfo); if (reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, hsize + l1_cpy + l2_cpy, CURRENT_TIME, psinfo); l1 -= l1_cpy; l2 -= l2_cpy; total += l1_cpy + l2_cpy; part++; } mutex_unlock(&psinfo->buf_mutex); }
/* * 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) { unsigned long total = 0; const char *why; u64 id; unsigned int part = 1; unsigned long flags = 0; int is_locked = 0; int ret; why = get_reason_str(reason); if (pstore_cannot_block_path(reason)) { is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags); if (!is_locked) { pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" , in_nmi() ? "NMI" : why); } } else spin_lock_irqsave(&psinfo->buf_lock, flags); oopscount++; while (total < kmsg_bytes) { char *dst; unsigned long size; int hsize; size_t len; dst = psinfo->buf; hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); size = psinfo->bufsize - hsize; dst += hsize; if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len)) break; ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, oopscount, hsize + len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; total += hsize + len; part++; } if (pstore_cannot_block_path(reason)) { if (is_locked) spin_unlock_irqrestore(&psinfo->buf_lock, flags); } else spin_unlock_irqrestore(&psinfo->buf_lock, flags); }
/* * 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. * * Register with kmsg_dump to save last part of console log on panic. */ int pstore_register(struct pstore_info *psi) { struct module *owner = psi->owner; spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; } if (backend && strcmp(backend, psi->name)) { spin_unlock(&pstore_lock); return -EINVAL; } if (!psi->write) psi->write = pstore_write_compat; psinfo = psi; mutex_init(&psinfo->read_mutex); spin_unlock(&pstore_lock); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } if (pstore_is_mounted()) pstore_get_records(0); kmsg_dump_register(&pstore_dumper); pstore_register_console(); pstore_register_ftrace(); pstore_register_pmsg(); if (pstore_update_ms >= 0) { pstore_timer.expires = jiffies + msecs_to_jiffies(pstore_update_ms); add_timer(&pstore_timer); } pr_info("psi registered\n"); return 0; }
/* * Call platform driver to write a record to the * persistent store. */ int pstore_write(enum pstore_type_id type, char *buf, size_t size) { u64 id; if (!psinfo) return -ENODEV; if (size > psinfo->bufsize) return -EFBIG; mutex_lock(&psinfo->buf_mutex); memcpy(psinfo->buf, buf, size); id = psinfo->write(type, 0, size, psinfo); if (pstore_is_mounted()) pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, size, CURRENT_TIME, psinfo); mutex_unlock(&psinfo->buf_mutex); return 0; }
/* * Call platform driver to write a record to the * persistent store. */ int pstore_write(enum pstore_type_id type, char *buf, size_t size) { u64 id; int ret; unsigned long flags; if (!psinfo) return -ENODEV; if (size > psinfo->bufsize) return -EFBIG; spin_lock_irqsave(&psinfo->buf_lock, flags); memcpy(psinfo->buf, buf, size); ret = psinfo->write(type, &id, 0, size, psinfo); if (ret == 0 && pstore_is_mounted()) pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf, size, CURRENT_TIME, psinfo); spin_unlock_irqrestore(&psinfo->buf_lock, flags); return 0; }
/* * 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)) { pr_warn("ignoring unexpected backend '%s'\n", psi->name); return -EPERM; } /* Sanity check flags. */ if (!psi->flags) { pr_warn("backend '%s' must support at least one frontend\n", psi->name); return -EINVAL; } /* Check for required functions. */ if (!psi->read || !psi->write) { pr_warn("backend '%s' must implement read() and write()\n", psi->name); return -EINVAL; } spin_lock(&pstore_lock); if (psinfo) { pr_warn("backend '%s' already loaded: ignoring '%s'\n", psinfo->name, psi->name); spin_unlock(&pstore_lock); return -EBUSY; } if (!psi->write_user) psi->write_user = pstore_write_user_compat; psinfo = psi; mutex_init(&psinfo->read_mutex); sema_init(&psinfo->buf_lock, 1); spin_unlock(&pstore_lock); if (owner && !try_module_get(owner)) { psinfo = NULL; return -EINVAL; } allocate_buf_for_compression(); if (pstore_is_mounted()) pstore_get_records(0); 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(); /* Start watching for new records, if desired. */ if (pstore_update_ms >= 0) { pstore_timer.expires = jiffies + msecs_to_jiffies(pstore_update_ms); add_timer(&pstore_timer); } /* * Update the module parameter backend, so it is visible * through /sys/module/pstore/parameters/backend */ backend = psi->name; pr_info("Registered %s as persistent store backend\n", psi->name); module_put(owner); return 0; }
/* * callback from kmsg_dump. Save as much as we can (up to kmsg_bytes) from the * end of the buffer. */ static void pstore_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) { unsigned long total = 0; const char *why; unsigned int part = 1; int ret; why = get_reason_str(reason); if (down_trylock(&psinfo->buf_lock)) { /* Failed to acquire lock: give up if we cannot wait. */ if (pstore_cannot_wait(reason)) { pr_err("dump skipped in %s path: may corrupt error record\n", in_nmi() ? "NMI" : why); return; } if (down_interruptible(&psinfo->buf_lock)) { pr_err("could not grab semaphore?!\n"); return; } } oopscount++; while (total < kmsg_bytes) { char *dst; size_t dst_size; int header_size; int zipped_len = -1; size_t dump_size; struct pstore_record record; pstore_record_init(&record, psinfo); record.type = PSTORE_TYPE_DMESG; record.count = oopscount; record.reason = reason; record.part = part; record.buf = psinfo->buf; if (big_oops_buf) { dst = big_oops_buf; dst_size = big_oops_buf_sz; } else { dst = psinfo->buf; dst_size = psinfo->bufsize; } /* Write dump header. */ header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, oopscount, part); dst_size -= header_size; /* Write dump contents. */ if (!kmsg_dump_get_buffer(dumper, true, dst + header_size, dst_size, &dump_size)) break; if (big_oops_buf) { zipped_len = pstore_compress(dst, psinfo->buf, header_size + dump_size, psinfo->bufsize); if (zipped_len > 0) { record.compressed = true; record.size = zipped_len; } else { record.size = copy_kmsg_to_buffer(header_size, dump_size); } } else { record.size = header_size + dump_size; } ret = psinfo->write(&record); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; total += record.size; part++; } up(&psinfo->buf_lock); }
/* * 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) { unsigned long total = 0; const char *why; u64 id; unsigned int part = 1; unsigned long flags = 0; int is_locked; int ret; why = get_reason_str(reason); if (pstore_cannot_block_path(reason)) { is_locked = spin_trylock_irqsave(&psinfo->buf_lock, flags); if (!is_locked) { pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" , in_nmi() ? "NMI" : why); } } else { spin_lock_irqsave(&psinfo->buf_lock, flags); is_locked = 1; } oopscount++; while (total < kmsg_bytes) { char *dst; unsigned long size; int hsize; int zipped_len = -1; size_t len; bool compressed = false; size_t total_len; if (big_oops_buf && is_locked) { dst = big_oops_buf; size = big_oops_buf_sz; } else { dst = psinfo->buf; size = psinfo->bufsize; } hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); size -= hsize; if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, size, &len)) break; if (big_oops_buf && is_locked) { zipped_len = pstore_compress(dst, psinfo->buf, hsize + len, psinfo->bufsize); if (zipped_len > 0) { compressed = true; total_len = zipped_len; } else { total_len = copy_kmsg_to_buffer(hsize, len); } } else { total_len = hsize + len; } ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, oopscount, compressed, total_len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; total += total_len; part++; } if (is_locked) spin_unlock_irqrestore(&psinfo->buf_lock, flags); }