Commit 2575be8a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pstore-v4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull pstore updates from Kees Cook:
 "This has a large internal refactoring along with several smaller
  fixes.

   - constify compression structures; Bhumika Goyal

   - restore powerpc dumping; Ankit Kumar

   - fix more bugs in the rarely exercises module unloading logic

   - reorganize filesystem locking to fix problems noticed by lockdep

   - refactor internal pstore APIs to make development and review
     easier:
      - improve error reporting
      - add kernel-doc structure and function comments
      - avoid insane argument passing by using a common record
        structure"

* tag 'pstore-v4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: (23 commits)
  pstore: Solve lockdep warning by moving inode locks
  pstore: Fix flags to enable dumps on powerpc
  pstore: Remove unused vmalloc.h in pmsg
  pstore: simplify write_user_compat()
  pstore: Remove write_buf() callback
  pstore: Replace arguments for write_buf_user() API
  pstore: Replace arguments for write_buf() API
  pstore: Replace arguments for erase() API
  pstore: Do not duplicate record metadata
  pstore: Allocate records on heap instead of stack
  pstore: Pass record contents instead of copying
  pstore: Always allocate buffer for decompression
  pstore: Replace arguments for write() API
  pstore: Replace arguments for read() API
  pstore: Switch pstore_mkfile to pass record
  pstore: Move record decompression to function
  pstore: Extract common arguments into structure
  pstore: Add kernel-doc for struct pstore_info
  pstore: Improve register_pstore() error reporting
  pstore: Avoid race in module unloading
  ...
parents c58d4055 3a7d2fd1
...@@ -389,51 +389,40 @@ static int nvram_pstore_open(struct pstore_info *psi) ...@@ -389,51 +389,40 @@ static int nvram_pstore_open(struct pstore_info *psi)
/** /**
* nvram_pstore_write - pstore write callback for nvram * nvram_pstore_write - pstore write callback for nvram
* @type: Type of message logged * @record: pstore record to write, with @id to be set
* @reason: reason behind dump (oops/panic)
* @id: identifier to indicate the write performed
* @part: pstore writes data to registered buffer in parts,
* part number will indicate the same.
* @count: Indicates oops count
* @compressed: Flag to indicate the log is compressed
* @size: number of bytes written to the registered buffer
* @psi: registered pstore_info structure
* *
* Called by pstore_dump() when an oops or panic report is logged in the * Called by pstore_dump() when an oops or panic report is logged in the
* printk buffer. * printk buffer.
* Returns 0 on successful write. * Returns 0 on successful write.
*/ */
static int nvram_pstore_write(enum pstore_type_id type, static int nvram_pstore_write(struct pstore_record *record)
enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count,
bool compressed, size_t size,
struct pstore_info *psi)
{ {
int rc; int rc;
unsigned int err_type = ERR_TYPE_KERNEL_PANIC; unsigned int err_type = ERR_TYPE_KERNEL_PANIC;
struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf;
/* part 1 has the recent messages from printk buffer */ /* part 1 has the recent messages from printk buffer */
if (part > 1 || (type != PSTORE_TYPE_DMESG)) if (record->part > 1 || (record->type != PSTORE_TYPE_DMESG))
return -1; return -1;
if (clobbering_unread_rtas_event()) if (clobbering_unread_rtas_event())
return -1; return -1;
oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION); oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION);
oops_hdr->report_length = cpu_to_be16(size); oops_hdr->report_length = cpu_to_be16(record->size);
oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds());
if (compressed) if (record->compressed)
err_type = ERR_TYPE_KERNEL_PANIC_GZ; err_type = ERR_TYPE_KERNEL_PANIC_GZ;
rc = nvram_write_os_partition(&oops_log_partition, oops_buf, rc = nvram_write_os_partition(&oops_log_partition, oops_buf,
(int) (sizeof(*oops_hdr) + size), err_type, count); (int) (sizeof(*oops_hdr) + record->size), err_type,
record->count);
if (rc != 0) if (rc != 0)
return rc; return rc;
*id = part; record->id = record->part;
return 0; return 0;
} }
...@@ -442,10 +431,7 @@ static int nvram_pstore_write(enum pstore_type_id type, ...@@ -442,10 +431,7 @@ static int nvram_pstore_write(enum pstore_type_id type,
* Returns the length of the data we read from each partition. * Returns the length of the data we read from each partition.
* Returns 0 if we've been called before. * Returns 0 if we've been called before.
*/ */
static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t nvram_pstore_read(struct pstore_record *record)
int *count, struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi)
{ {
struct oops_log_info *oops_hdr; struct oops_log_info *oops_hdr;
unsigned int err_type, id_no, size = 0; unsigned int err_type, id_no, size = 0;
...@@ -459,40 +445,40 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -459,40 +445,40 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
switch (nvram_type_ids[read_type]) { switch (nvram_type_ids[read_type]) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
part = &oops_log_partition; part = &oops_log_partition;
*type = PSTORE_TYPE_DMESG; record->type = PSTORE_TYPE_DMESG;
break; break;
case PSTORE_TYPE_PPC_COMMON: case PSTORE_TYPE_PPC_COMMON:
sig = NVRAM_SIG_SYS; sig = NVRAM_SIG_SYS;
part = &common_partition; part = &common_partition;
*type = PSTORE_TYPE_PPC_COMMON; record->type = PSTORE_TYPE_PPC_COMMON;
*id = PSTORE_TYPE_PPC_COMMON; record->id = PSTORE_TYPE_PPC_COMMON;
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
break; break;
#ifdef CONFIG_PPC_PSERIES #ifdef CONFIG_PPC_PSERIES
case PSTORE_TYPE_PPC_RTAS: case PSTORE_TYPE_PPC_RTAS:
part = &rtas_log_partition; part = &rtas_log_partition;
*type = PSTORE_TYPE_PPC_RTAS; record->type = PSTORE_TYPE_PPC_RTAS;
time->tv_sec = last_rtas_event; record->time.tv_sec = last_rtas_event;
time->tv_nsec = 0; record->time.tv_nsec = 0;
break; break;
case PSTORE_TYPE_PPC_OF: case PSTORE_TYPE_PPC_OF:
sig = NVRAM_SIG_OF; sig = NVRAM_SIG_OF;
part = &of_config_partition; part = &of_config_partition;
*type = PSTORE_TYPE_PPC_OF; record->type = PSTORE_TYPE_PPC_OF;
*id = PSTORE_TYPE_PPC_OF; record->id = PSTORE_TYPE_PPC_OF;
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
break; break;
#endif #endif
#ifdef CONFIG_PPC_POWERNV #ifdef CONFIG_PPC_POWERNV
case PSTORE_TYPE_PPC_OPAL: case PSTORE_TYPE_PPC_OPAL:
sig = NVRAM_SIG_FW; sig = NVRAM_SIG_FW;
part = &skiboot_partition; part = &skiboot_partition;
*type = PSTORE_TYPE_PPC_OPAL; record->type = PSTORE_TYPE_PPC_OPAL;
*id = PSTORE_TYPE_PPC_OPAL; record->id = PSTORE_TYPE_PPC_OPAL;
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
break; break;
#endif #endif
default: default:
...@@ -520,10 +506,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -520,10 +506,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
return 0; return 0;
} }
*count = 0; record->count = 0;
if (part->os_partition) if (part->os_partition)
*id = id_no; record->id = id_no;
if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) {
size_t length, hdr_size; size_t length, hdr_size;
...@@ -533,34 +519,35 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -533,34 +519,35 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
/* Old format oops header had 2-byte record size */ /* Old format oops header had 2-byte record size */
hdr_size = sizeof(u16); hdr_size = sizeof(u16);
length = be16_to_cpu(oops_hdr->version); length = be16_to_cpu(oops_hdr->version);
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
} else { } else {
hdr_size = sizeof(*oops_hdr); hdr_size = sizeof(*oops_hdr);
length = be16_to_cpu(oops_hdr->report_length); length = be16_to_cpu(oops_hdr->report_length);
time->tv_sec = be64_to_cpu(oops_hdr->timestamp); record->time.tv_sec = be64_to_cpu(oops_hdr->timestamp);
time->tv_nsec = 0; record->time.tv_nsec = 0;
} }
*buf = kmemdup(buff + hdr_size, length, GFP_KERNEL); record->buf = kmemdup(buff + hdr_size, length, GFP_KERNEL);
kfree(buff); kfree(buff);
if (*buf == NULL) if (record->buf == NULL)
return -ENOMEM; return -ENOMEM;
*ecc_notice_size = 0; record->ecc_notice_size = 0;
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
*compressed = true; record->compressed = true;
else else
*compressed = false; record->compressed = false;
return length; return length;
} }
*buf = buff; record->buf = buff;
return part->size; return part->size;
} }
static struct pstore_info nvram_pstore_info = { static struct pstore_info nvram_pstore_info = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "nvram", .name = "nvram",
.flags = PSTORE_FLAGS_DMESG,
.open = nvram_pstore_open, .open = nvram_pstore_open,
.read = nvram_pstore_read, .read = nvram_pstore_read,
.write = nvram_pstore_write, .write = nvram_pstore_write,
......
...@@ -925,15 +925,9 @@ static int erst_check_table(struct acpi_table_erst *erst_tab) ...@@ -925,15 +925,9 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
static int erst_open_pstore(struct pstore_info *psi); static int erst_open_pstore(struct pstore_info *psi);
static int erst_close_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, static ssize_t erst_reader(struct pstore_record *record);
struct timespec *time, char **buf, static int erst_writer(struct pstore_record *record);
bool *compressed, ssize_t *ecc_notice_size, static int erst_clearer(struct pstore_record *record);
struct pstore_info *psi);
static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
u64 *id, unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi);
static int erst_clearer(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi);
static struct pstore_info erst_info = { static struct pstore_info erst_info = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -986,10 +980,7 @@ static int erst_close_pstore(struct pstore_info *psi) ...@@ -986,10 +980,7 @@ static int erst_close_pstore(struct pstore_info *psi)
return 0; return 0;
} }
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, static ssize_t erst_reader(struct pstore_record *record)
struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi)
{ {
int rc; int rc;
ssize_t len = 0; ssize_t len = 0;
...@@ -1027,42 +1018,40 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, ...@@ -1027,42 +1018,40 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
goto skip; goto skip;
*buf = kmalloc(len, GFP_KERNEL); record->buf = kmalloc(len, GFP_KERNEL);
if (*buf == NULL) { if (record->buf == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
} }
memcpy(*buf, rcd->data, len - sizeof(*rcd)); memcpy(record->buf, rcd->data, len - sizeof(*rcd));
*id = record_id; record->id = record_id;
*compressed = false; record->compressed = false;
*ecc_notice_size = 0; record->ecc_notice_size = 0;
if (uuid_le_cmp(rcd->sec_hdr.section_type, if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG_Z) == 0) { CPER_SECTION_TYPE_DMESG_Z) == 0) {
*type = PSTORE_TYPE_DMESG; record->type = PSTORE_TYPE_DMESG;
*compressed = true; record->compressed = true;
} else if (uuid_le_cmp(rcd->sec_hdr.section_type, } else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG) == 0) CPER_SECTION_TYPE_DMESG) == 0)
*type = PSTORE_TYPE_DMESG; record->type = PSTORE_TYPE_DMESG;
else if (uuid_le_cmp(rcd->sec_hdr.section_type, else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_MCE) == 0) CPER_SECTION_TYPE_MCE) == 0)
*type = PSTORE_TYPE_MCE; record->type = PSTORE_TYPE_MCE;
else else
*type = PSTORE_TYPE_UNKNOWN; record->type = PSTORE_TYPE_UNKNOWN;
if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
time->tv_sec = rcd->hdr.timestamp; record->time.tv_sec = rcd->hdr.timestamp;
else else
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
out: out:
kfree(rcd); kfree(rcd);
return (rc < 0) ? rc : (len - sizeof(*rcd)); return (rc < 0) ? rc : (len - sizeof(*rcd));
} }
static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, static int erst_writer(struct pstore_record *record)
u64 *id, unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi)
{ {
struct cper_pstore_record *rcd = (struct cper_pstore_record *) struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd)); (erst_info.buf - sizeof(*rcd));
...@@ -1077,21 +1066,21 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, ...@@ -1077,21 +1066,21 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
/* timestamp valid. platform_id, partition_id are invalid */ /* timestamp valid. platform_id, partition_id are invalid */
rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
rcd->hdr.timestamp = get_seconds(); rcd->hdr.timestamp = get_seconds();
rcd->hdr.record_length = sizeof(*rcd) + size; rcd->hdr.record_length = sizeof(*rcd) + record->size;
rcd->hdr.creator_id = CPER_CREATOR_PSTORE; rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
rcd->hdr.notification_type = CPER_NOTIFY_MCE; rcd->hdr.notification_type = CPER_NOTIFY_MCE;
rcd->hdr.record_id = cper_next_record_id(); rcd->hdr.record_id = cper_next_record_id();
rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
rcd->sec_hdr.section_offset = sizeof(*rcd); rcd->sec_hdr.section_offset = sizeof(*rcd);
rcd->sec_hdr.section_length = size; rcd->sec_hdr.section_length = record->size;
rcd->sec_hdr.revision = CPER_SEC_REV; rcd->sec_hdr.revision = CPER_SEC_REV;
/* fru_id and fru_text is invalid */ /* fru_id and fru_text is invalid */
rcd->sec_hdr.validation_bits = 0; rcd->sec_hdr.validation_bits = 0;
rcd->sec_hdr.flags = CPER_SEC_PRIMARY; rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
switch (type) { switch (record->type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
if (compressed) if (record->compressed)
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
else else
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
...@@ -1105,15 +1094,14 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, ...@@ -1105,15 +1094,14 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
rcd->sec_hdr.section_severity = CPER_SEV_FATAL; rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
ret = erst_write(&rcd->hdr); ret = erst_write(&rcd->hdr);
*id = rcd->hdr.record_id; record->id = rcd->hdr.record_id;
return ret; return ret;
} }
static int erst_clearer(enum pstore_type_id type, u64 id, int count, static int erst_clearer(struct pstore_record *record)
struct timespec time, struct pstore_info *psi)
{ {
return erst_clear(id); return erst_clear(record->id);
} }
static int __init erst_init(void) static int __init erst_init(void)
......
...@@ -28,26 +28,16 @@ static int efi_pstore_close(struct pstore_info *psi) ...@@ -28,26 +28,16 @@ static int efi_pstore_close(struct pstore_info *psi)
return 0; return 0;
} }
struct pstore_read_data {
u64 *id;
enum pstore_type_id *type;
int *count;
struct timespec *timespec;
bool *compressed;
ssize_t *ecc_notice_size;
char **buf;
};
static inline u64 generic_id(unsigned long timestamp, static inline u64 generic_id(unsigned long timestamp,
unsigned int part, int count) unsigned int part, int count)
{ {
return ((u64) timestamp * 100 + part) * 1000 + count; return ((u64) timestamp * 100 + part) * 1000 + count;
} }
static int efi_pstore_read_func(struct efivar_entry *entry, void *data) static int efi_pstore_read_func(struct efivar_entry *entry,
struct pstore_record *record)
{ {
efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
struct pstore_read_data *cb_data = data;
char name[DUMP_NAME_LEN], data_type; char name[DUMP_NAME_LEN], data_type;
int i; int i;
int cnt; int cnt;
...@@ -61,37 +51,37 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -61,37 +51,37 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
name[i] = entry->var.VariableName[i]; name[i] = entry->var.VariableName[i];
if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
cb_data->type, &part, &cnt, &time, &data_type) == 5) { &record->type, &part, &cnt, &time, &data_type) == 5) {
*cb_data->id = generic_id(time, part, cnt); record->id = generic_id(time, part, cnt);
*cb_data->count = cnt; record->count = cnt;
cb_data->timespec->tv_sec = time; record->time.tv_sec = time;
cb_data->timespec->tv_nsec = 0; record->time.tv_nsec = 0;
if (data_type == 'C') if (data_type == 'C')
*cb_data->compressed = true; record->compressed = true;
else else
*cb_data->compressed = false; record->compressed = false;
*cb_data->ecc_notice_size = 0; record->ecc_notice_size = 0;
} else if (sscanf(name, "dump-type%u-%u-%d-%lu", } else if (sscanf(name, "dump-type%u-%u-%d-%lu",
cb_data->type, &part, &cnt, &time) == 4) { &record->type, &part, &cnt, &time) == 4) {
*cb_data->id = generic_id(time, part, cnt); record->id = generic_id(time, part, cnt);
*cb_data->count = cnt; record->count = cnt;
cb_data->timespec->tv_sec = time; record->time.tv_sec = time;
cb_data->timespec->tv_nsec = 0; record->time.tv_nsec = 0;
*cb_data->compressed = false; record->compressed = false;
*cb_data->ecc_notice_size = 0; record->ecc_notice_size = 0;
} else if (sscanf(name, "dump-type%u-%u-%lu", } else if (sscanf(name, "dump-type%u-%u-%lu",
cb_data->type, &part, &time) == 3) { &record->type, &part, &time) == 3) {
/* /*
* Check if an old format, * Check if an old format,
* which doesn't support holding * which doesn't support holding
* multiple logs, remains. * multiple logs, remains.
*/ */
*cb_data->id = generic_id(time, part, 0); record->id = generic_id(time, part, 0);
*cb_data->count = 0; record->count = 0;
cb_data->timespec->tv_sec = time; record->time.tv_sec = time;
cb_data->timespec->tv_nsec = 0; record->time.tv_nsec = 0;
*cb_data->compressed = false; record->compressed = false;
*cb_data->ecc_notice_size = 0; record->ecc_notice_size = 0;
} else } else
return 0; return 0;
...@@ -99,7 +89,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) ...@@ -99,7 +89,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
__efivar_entry_get(entry, &entry->var.Attributes, __efivar_entry_get(entry, &entry->var.Attributes,
&entry->var.DataSize, entry->var.Data); &entry->var.DataSize, entry->var.Data);
size = entry->var.DataSize; size = entry->var.DataSize;
memcpy(*cb_data->buf, entry->var.Data, memcpy(record->buf, entry->var.Data,
(size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size)); (size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size));
return size; return size;
...@@ -164,7 +154,7 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, ...@@ -164,7 +154,7 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
/** /**
* efi_pstore_sysfs_entry_iter * efi_pstore_sysfs_entry_iter
* *
* @data: function-specific data to pass to callback * @record: pstore record to pass to callback
* @pos: entry to begin iterating from * @pos: entry to begin iterating from
* *
* You MUST call efivar_enter_iter_begin() before this function, and * You MUST call efivar_enter_iter_begin() before this function, and
...@@ -175,7 +165,8 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos, ...@@ -175,7 +165,8 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
* the next entry of the last one passed to efi_pstore_read_func(). * the next entry of the last one passed to efi_pstore_read_func().
* To begin iterating from the beginning of the list @pos must be %NULL. * To begin iterating from the beginning of the list @pos must be %NULL.
*/ */
static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) static int efi_pstore_sysfs_entry_iter(struct pstore_record *record,
struct efivar_entry **pos)
{ {
struct efivar_entry *entry, *n; struct efivar_entry *entry, *n;
struct list_head *head = &efivar_sysfs_list; struct list_head *head = &efivar_sysfs_list;
...@@ -186,7 +177,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) ...@@ -186,7 +177,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
list_for_each_entry_safe(entry, n, head, list) { list_for_each_entry_safe(entry, n, head, list) {
efi_pstore_scan_sysfs_enter(entry, n, head); efi_pstore_scan_sysfs_enter(entry, n, head);
size = efi_pstore_read_func(entry, data); size = efi_pstore_read_func(entry, record);
ret = efi_pstore_scan_sysfs_exit(entry, n, head, ret = efi_pstore_scan_sysfs_exit(entry, n, head,
size < 0); size < 0);
if (ret) if (ret)
...@@ -201,7 +192,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) ...@@ -201,7 +192,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
list_for_each_entry_safe_from((*pos), n, head, list) { list_for_each_entry_safe_from((*pos), n, head, list) {
efi_pstore_scan_sysfs_enter((*pos), n, head); efi_pstore_scan_sysfs_enter((*pos), n, head);
size = efi_pstore_read_func((*pos), data); size = efi_pstore_read_func((*pos), record);
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0); ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
if (ret) if (ret)
return ret; return ret;
...@@ -225,71 +216,57 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) ...@@ -225,71 +216,57 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
* size < 0: Failed to get data of entry logging via efi_pstore_write(), * size < 0: Failed to get data of entry logging via efi_pstore_write(),
* and pstore will stop reading entry. * and pstore will stop reading entry.
*/ */
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t efi_pstore_read(struct pstore_record *record)
int *count, struct timespec *timespec,
char **buf, bool *compressed,
ssize_t *ecc_notice_size,
struct pstore_info *psi)
{ {
struct pstore_read_data data; struct efivar_entry *entry = (struct efivar_entry *)record->psi->data;
ssize_t size; ssize_t size;
data.id = id; record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
data.type = type; if (!record->buf)
data.count = count;
data.timespec = timespec;
data.compressed = compressed;
data.ecc_notice_size = ecc_notice_size;
data.buf = buf;
*data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
if (!*data.buf)
return -ENOMEM; return -ENOMEM;
if (efivar_entry_iter_begin()) { if (efivar_entry_iter_begin()) {
kfree(*data.buf); size = -EINTR;
return -EINTR; goto out;
} }
size = efi_pstore_sysfs_entry_iter(&data, size = efi_pstore_sysfs_entry_iter(record, &entry);
(struct efivar_entry **)&psi->data);
efivar_entry_iter_end(); efivar_entry_iter_end();
if (size <= 0)
kfree(*data.buf); out:
if (size <= 0) {
kfree(record->buf);
record->buf = NULL;
}
return size; return size;
} }
static int efi_pstore_write(enum pstore_type_id type, static int efi_pstore_write(struct pstore_record *record)
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, bool compressed, size_t size,
struct pstore_info *psi)
{ {
char name[DUMP_NAME_LEN]; char name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN]; efi_char16_t efi_name[DUMP_NAME_LEN];
efi_guid_t vendor = LINUX_EFI_CRASH_GUID; efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
int i, ret = 0; int i, ret = 0;
sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count, snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
get_seconds(), compressed ? 'C' : 'D'); record->type, record->part, record->count,
get_seconds(), record->compressed ? 'C' : 'D');
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i]; efi_name[i] = name[i];
ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
!pstore_cannot_block_path(reason), !pstore_cannot_block_path(record->reason),
size, psi->buf); record->size, record->psi->buf);
if (reason == KMSG_DUMP_OOPS) if (record->reason == KMSG_DUMP_OOPS)
efivar_run_worker(); efivar_run_worker();
*id = part; record->id = record->part;
return ret; return ret;
}; };
struct pstore_erase_data { struct pstore_erase_data {
u64 id; struct pstore_record *record;
enum pstore_type_id type;
int count;
struct timespec time;
efi_char16_t *name; efi_char16_t *name;
}; };
...@@ -315,8 +292,9 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) ...@@ -315,8 +292,9 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
* Check if an old format, which doesn't support * Check if an old format, which doesn't support
* holding multiple logs, remains. * holding multiple logs, remains.
*/ */
sprintf(name_old, "dump-type%u-%u-%lu", ed->type, snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu",
(unsigned int)ed->id, ed->time.tv_sec); ed->record->type, (unsigned int)ed->record->id,
ed->record->time.tv_sec);
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name_old[i] = name_old[i]; efi_name_old[i] = name_old[i];
...@@ -341,8 +319,7 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data) ...@@ -341,8 +319,7 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
return 1; return 1;
} }
static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, static int efi_pstore_erase(struct pstore_record *record)
struct timespec time, struct pstore_info *psi)
{ {
struct pstore_erase_data edata; struct pstore_erase_data edata;
struct efivar_entry *entry = NULL; struct efivar_entry *entry = NULL;
...@@ -351,17 +328,16 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -351,17 +328,16 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
int found, i; int found, i;
unsigned int part; unsigned int part;
do_div(id, 1000); do_div(record->id, 1000);
part = do_div(id, 100); part = do_div(record->id, 100);
sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, time.tv_sec); snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu",
record->type, record->part, record->count,
record->time.tv_sec);
for (i = 0; i < DUMP_NAME_LEN; i++) for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i]; efi_name[i] = name[i];
edata.id = part; edata.record = record;
edata.type = type;
edata.count = count;
edata.time = time;
edata.name = efi_name; edata.name = efi_name;
if (efivar_entry_iter_begin()) if (efivar_entry_iter_begin())
......
...@@ -37,6 +37,12 @@ static void notrace pstore_ftrace_call(unsigned long ip, ...@@ -37,6 +37,12 @@ static void notrace pstore_ftrace_call(unsigned long ip,
{ {
unsigned long flags; unsigned long flags;
struct pstore_ftrace_record rec = {}; struct pstore_ftrace_record rec = {};
struct pstore_record record = {
.type = PSTORE_TYPE_FTRACE,
.buf = (char *)&rec,
.size = sizeof(rec),
.psi = psinfo,
};
if (unlikely(oops_in_progress)) if (unlikely(oops_in_progress))
return; return;
...@@ -47,8 +53,7 @@ static void notrace pstore_ftrace_call(unsigned long ip, ...@@ -47,8 +53,7 @@ static void notrace pstore_ftrace_call(unsigned long ip,
rec.parent_ip = parent_ip; rec.parent_ip = parent_ip;
pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++); pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++);
pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());
psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec, psinfo->write(&record);
0, sizeof(rec), psinfo);
local_irq_restore(flags); local_irq_restore(flags);
} }
...@@ -117,7 +122,7 @@ void pstore_register_ftrace(void) ...@@ -117,7 +122,7 @@ void pstore_register_ftrace(void)
{ {
struct dentry *file; struct dentry *file;
if (!psinfo->write_buf) if (!psinfo->write)
return; return;
pstore_ftrace_dir = debugfs_create_dir("pstore", NULL); pstore_ftrace_dir = debugfs_create_dir("pstore", NULL);
......
...@@ -47,12 +47,8 @@ static LIST_HEAD(allpstore); ...@@ -47,12 +47,8 @@ static LIST_HEAD(allpstore);
struct pstore_private { struct pstore_private {
struct list_head list; struct list_head list;
struct pstore_info *psi; struct pstore_record *record;
enum pstore_type_id type; size_t total_size;
u64 id;
int count;
ssize_t size;
char data[];
}; };
struct pstore_ftrace_seq_data { struct pstore_ftrace_seq_data {
...@@ -63,6 +59,17 @@ struct pstore_ftrace_seq_data { ...@@ -63,6 +59,17 @@ struct pstore_ftrace_seq_data {
#define REC_SIZE sizeof(struct pstore_ftrace_record) #define REC_SIZE sizeof(struct pstore_ftrace_record)
static void free_pstore_private(struct pstore_private *private)
{
if (!private)
return;
if (private->record) {
kfree(private->record->buf);
kfree(private->record);
}
kfree(private);
}
static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos) static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
{ {
struct pstore_private *ps = s->private; struct pstore_private *ps = s->private;
...@@ -72,9 +79,9 @@ static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos) ...@@ -72,9 +79,9 @@ static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
if (!data) if (!data)
return NULL; return NULL;
data->off = ps->size % REC_SIZE; data->off = ps->total_size % REC_SIZE;
data->off += *pos * REC_SIZE; data->off += *pos * REC_SIZE;
if (data->off + REC_SIZE > ps->size) { if (data->off + REC_SIZE > ps->total_size) {
kfree(data); kfree(data);
return NULL; return NULL;
} }
...@@ -94,7 +101,7 @@ static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos) ...@@ -94,7 +101,7 @@ static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos)
struct pstore_ftrace_seq_data *data = v; struct pstore_ftrace_seq_data *data = v;
data->off += REC_SIZE; data->off += REC_SIZE;
if (data->off + REC_SIZE > ps->size) if (data->off + REC_SIZE > ps->total_size)
return NULL; return NULL;
(*pos)++; (*pos)++;
...@@ -105,7 +112,9 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v) ...@@ -105,7 +112,9 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
{ {
struct pstore_private *ps = s->private; struct pstore_private *ps = s->private;
struct pstore_ftrace_seq_data *data = v; struct pstore_ftrace_seq_data *data = v;
struct pstore_ftrace_record *rec = (void *)(ps->data + data->off); struct pstore_ftrace_record *rec;
rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off);
seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n", seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %pf <- %pF\n",
pstore_ftrace_decode_cpu(rec), pstore_ftrace_decode_cpu(rec),
...@@ -125,7 +134,7 @@ static const struct seq_operations pstore_ftrace_seq_ops = { ...@@ -125,7 +134,7 @@ static const struct seq_operations pstore_ftrace_seq_ops = {
static int pstore_check_syslog_permissions(struct pstore_private *ps) static int pstore_check_syslog_permissions(struct pstore_private *ps)
{ {
switch (ps->type) { switch (ps->record->type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
case PSTORE_TYPE_CONSOLE: case PSTORE_TYPE_CONSOLE:
return check_syslog_permissions(SYSLOG_ACTION_READ_ALL, return check_syslog_permissions(SYSLOG_ACTION_READ_ALL,
...@@ -141,9 +150,10 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf, ...@@ -141,9 +150,10 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
struct seq_file *sf = file->private_data; struct seq_file *sf = file->private_data;
struct pstore_private *ps = sf->private; struct pstore_private *ps = sf->private;
if (ps->type == PSTORE_TYPE_FTRACE) if (ps->record->type == PSTORE_TYPE_FTRACE)
return seq_read(file, userbuf, count, ppos); return seq_read(file, userbuf, count, ppos);
return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size); return simple_read_from_buffer(userbuf, count, ppos,
ps->record->buf, ps->total_size);
} }
static int pstore_file_open(struct inode *inode, struct file *file) static int pstore_file_open(struct inode *inode, struct file *file)
...@@ -157,7 +167,7 @@ static int pstore_file_open(struct inode *inode, struct file *file) ...@@ -157,7 +167,7 @@ static int pstore_file_open(struct inode *inode, struct file *file)
if (err) if (err)
return err; return err;
if (ps->type == PSTORE_TYPE_FTRACE) if (ps->record->type == PSTORE_TYPE_FTRACE)
sops = &pstore_ftrace_seq_ops; sops = &pstore_ftrace_seq_ops;
err = seq_open(file, sops); err = seq_open(file, sops);
...@@ -193,20 +203,19 @@ static const struct file_operations pstore_file_operations = { ...@@ -193,20 +203,19 @@ static const struct file_operations pstore_file_operations = {
static int pstore_unlink(struct inode *dir, struct dentry *dentry) static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct pstore_private *p = d_inode(dentry)->i_private; struct pstore_private *p = d_inode(dentry)->i_private;
struct pstore_record *record = p->record;
int err; int err;
err = pstore_check_syslog_permissions(p); err = pstore_check_syslog_permissions(p);
if (err) if (err)
return err; return err;
if (p->psi->erase) { if (!record->psi->erase)
mutex_lock(&p->psi->read_mutex);
p->psi->erase(p->type, p->id, p->count,
d_inode(dentry)->i_ctime, p->psi);
mutex_unlock(&p->psi->read_mutex);
} else {
return -EPERM; return -EPERM;
}
mutex_lock(&record->psi->read_mutex);
record->psi->erase(record);
mutex_unlock(&record->psi->read_mutex);
return simple_unlink(dir, dentry); return simple_unlink(dir, dentry);
} }
...@@ -221,7 +230,7 @@ static void pstore_evict_inode(struct inode *inode) ...@@ -221,7 +230,7 @@ static void pstore_evict_inode(struct inode *inode)
spin_lock_irqsave(&allpstore_lock, flags); spin_lock_irqsave(&allpstore_lock, flags);
list_del(&p->list); list_del(&p->list);
spin_unlock_irqrestore(&allpstore_lock, flags); spin_unlock_irqrestore(&allpstore_lock, flags);
kfree(p); free_pstore_private(p);
} }
} }
...@@ -302,23 +311,23 @@ bool pstore_is_mounted(void) ...@@ -302,23 +311,23 @@ bool pstore_is_mounted(void)
* Load it up with "size" bytes of data from "buf". * Load it up with "size" bytes of data from "buf".
* Set the mtime & ctime to the date that this record was originally stored. * Set the mtime & ctime to the date that this record was originally stored.
*/ */
int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, int pstore_mkfile(struct dentry *root, struct pstore_record *record)
char *data, bool compressed, size_t size,
struct timespec time, struct pstore_info *psi)
{ {
struct dentry *root = pstore_sb->s_root;
struct dentry *dentry; struct dentry *dentry;
struct inode *inode; struct inode *inode;
int rc = 0; int rc = 0;
char name[PSTORE_NAMELEN]; char name[PSTORE_NAMELEN];
struct pstore_private *private, *pos; struct pstore_private *private, *pos;
unsigned long flags; unsigned long flags;
size_t size = record->size + record->ecc_notice_size;
WARN_ON(!inode_is_locked(d_inode(root)));
spin_lock_irqsave(&allpstore_lock, flags); spin_lock_irqsave(&allpstore_lock, flags);
list_for_each_entry(pos, &allpstore, list) { list_for_each_entry(pos, &allpstore, list) {
if (pos->type == type && if (pos->record->type == record->type &&
pos->id == id && pos->record->id == record->id &&
pos->psi == psi) { pos->record->psi == record->psi) {
rc = -EEXIST; rc = -EEXIST;
break; break;
} }
...@@ -328,72 +337,74 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -328,72 +337,74 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
return rc; return rc;
rc = -ENOMEM; rc = -ENOMEM;
inode = pstore_get_inode(pstore_sb); inode = pstore_get_inode(root->d_sb);
if (!inode) if (!inode)
goto fail; goto fail;
inode->i_mode = S_IFREG | 0444; inode->i_mode = S_IFREG | 0444;
inode->i_fop = &pstore_file_operations; inode->i_fop = &pstore_file_operations;
private = kmalloc(sizeof *private + size, GFP_KERNEL); private = kzalloc(sizeof(*private), GFP_KERNEL);
if (!private) if (!private)
goto fail_alloc; goto fail_alloc;
private->type = type; private->record = record;
private->id = id;
private->count = count;
private->psi = psi;
switch (type) { switch (record->type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
scnprintf(name, sizeof(name), "dmesg-%s-%lld%s", scnprintf(name, sizeof(name), "dmesg-%s-%lld%s",
psname, id, compressed ? ".enc.z" : ""); record->psi->name, record->id,
record->compressed ? ".enc.z" : "");
break; break;
case PSTORE_TYPE_CONSOLE: case PSTORE_TYPE_CONSOLE:
scnprintf(name, sizeof(name), "console-%s-%lld", psname, id); scnprintf(name, sizeof(name), "console-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_FTRACE: case PSTORE_TYPE_FTRACE:
scnprintf(name, sizeof(name), "ftrace-%s-%lld", psname, id); scnprintf(name, sizeof(name), "ftrace-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_MCE: case PSTORE_TYPE_MCE:
scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id); scnprintf(name, sizeof(name), "mce-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_PPC_RTAS: case PSTORE_TYPE_PPC_RTAS:
scnprintf(name, sizeof(name), "rtas-%s-%lld", psname, id); scnprintf(name, sizeof(name), "rtas-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_PPC_OF: case PSTORE_TYPE_PPC_OF:
scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld", scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld",
psname, id); record->psi->name, record->id);
break; break;
case PSTORE_TYPE_PPC_COMMON: case PSTORE_TYPE_PPC_COMMON:
scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", scnprintf(name, sizeof(name), "powerpc-common-%s-%lld",
psname, id); record->psi->name, record->id);
break; break;
case PSTORE_TYPE_PMSG: case PSTORE_TYPE_PMSG:
scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); scnprintf(name, sizeof(name), "pmsg-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_PPC_OPAL: case PSTORE_TYPE_PPC_OPAL:
sprintf(name, "powerpc-opal-%s-%lld", psname, id); scnprintf(name, sizeof(name), "powerpc-opal-%s-%lld",
record->psi->name, record->id);
break; break;
case PSTORE_TYPE_UNKNOWN: case PSTORE_TYPE_UNKNOWN:
scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); scnprintf(name, sizeof(name), "unknown-%s-%lld",
record->psi->name, record->id);
break; break;
default: default:
scnprintf(name, sizeof(name), "type%d-%s-%lld", scnprintf(name, sizeof(name), "type%d-%s-%lld",
type, psname, id); record->type, record->psi->name, record->id);
break; break;
} }
inode_lock(d_inode(root));
dentry = d_alloc_name(root, name); dentry = d_alloc_name(root, name);
if (!dentry) if (!dentry)
goto fail_lockedalloc; goto fail_private;
memcpy(private->data, data, size); inode->i_size = private->total_size = size;
inode->i_size = private->size = size;
inode->i_private = private; inode->i_private = private;
if (time.tv_sec) if (record->time.tv_sec)
inode->i_mtime = inode->i_ctime = time; inode->i_mtime = inode->i_ctime = record->time;
d_add(dentry, inode); d_add(dentry, inode);
...@@ -401,13 +412,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -401,13 +412,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
list_add(&private->list, &allpstore); list_add(&private->list, &allpstore);
spin_unlock_irqrestore(&allpstore_lock, flags); spin_unlock_irqrestore(&allpstore_lock, flags);
inode_unlock(d_inode(root));
return 0; return 0;
fail_lockedalloc: fail_private:
inode_unlock(d_inode(root)); free_pstore_private(private);
kfree(private);
fail_alloc: fail_alloc:
iput(inode); iput(inode);
...@@ -415,6 +423,27 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -415,6 +423,27 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
return rc; return rc;
} }
/*
* Read all the records from the persistent store. Create
* files in our filesystem. Don't warn about -EEXIST errors
* when we are re-scanning the backing store looking to add new
* error records.
*/
void pstore_get_records(int quiet)
{
struct pstore_info *psi = psinfo;
struct dentry *root;
if (!psi || !pstore_sb)
return;
root = pstore_sb->s_root;
inode_lock(d_inode(root));
pstore_get_backend_records(psi, root, quiet);
inode_unlock(d_inode(root));
}
static int pstore_fill_super(struct super_block *sb, void *data, int silent) static int pstore_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct inode *inode; struct inode *inode;
......
...@@ -25,10 +25,10 @@ extern struct pstore_info *psinfo; ...@@ -25,10 +25,10 @@ extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int); extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int); extern void pstore_get_records(int);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, extern void pstore_get_backend_records(struct pstore_info *psi,
int count, char *data, bool compressed, struct dentry *root, int quiet);
size_t size, struct timespec time, extern int pstore_mkfile(struct dentry *root,
struct pstore_info *psi); struct pstore_record *record);
extern bool pstore_is_mounted(void); extern bool pstore_is_mounted(void);
#endif #endif
...@@ -267,7 +267,7 @@ static void free_zlib(void) ...@@ -267,7 +267,7 @@ static void free_zlib(void)
big_oops_buf_sz = 0; big_oops_buf_sz = 0;
} }
static struct pstore_zbackend backend_zlib = { static const struct pstore_zbackend backend_zlib = {
.compress = compress_zlib, .compress = compress_zlib,
.decompress = decompress_zlib, .decompress = decompress_zlib,
.allocate = allocate_zlib, .allocate = allocate_zlib,
...@@ -328,7 +328,7 @@ static void free_lzo(void) ...@@ -328,7 +328,7 @@ static void free_lzo(void)
big_oops_buf_sz = 0; big_oops_buf_sz = 0;
} }
static struct pstore_zbackend backend_lzo = { static const struct pstore_zbackend backend_lzo = {
.compress = compress_lzo, .compress = compress_lzo,
.decompress = decompress_lzo, .decompress = decompress_lzo,
.allocate = allocate_lzo, .allocate = allocate_lzo,
...@@ -393,7 +393,7 @@ static void free_lz4(void) ...@@ -393,7 +393,7 @@ static void free_lz4(void)
big_oops_buf_sz = 0; big_oops_buf_sz = 0;
} }
static struct pstore_zbackend backend_lz4 = { static const struct pstore_zbackend backend_lz4 = {
.compress = compress_lz4, .compress = compress_lz4,
.decompress = decompress_lz4, .decompress = decompress_lz4,
.allocate = allocate_lz4, .allocate = allocate_lz4,
...@@ -402,7 +402,7 @@ static struct pstore_zbackend backend_lz4 = { ...@@ -402,7 +402,7 @@ static struct pstore_zbackend backend_lz4 = {
}; };
#endif #endif
static struct pstore_zbackend *zbackend = static const struct pstore_zbackend *zbackend =
#if defined(CONFIG_PSTORE_ZLIB_COMPRESS) #if defined(CONFIG_PSTORE_ZLIB_COMPRESS)
&backend_zlib; &backend_zlib;
#elif defined(CONFIG_PSTORE_LZO_COMPRESS) #elif defined(CONFIG_PSTORE_LZO_COMPRESS)
...@@ -484,7 +484,6 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -484,7 +484,6 @@ static void pstore_dump(struct kmsg_dumper *dumper,
{ {
unsigned long total = 0; unsigned long total = 0;
const char *why; const char *why;
u64 id;
unsigned int part = 1; unsigned int part = 1;
unsigned long flags = 0; unsigned long flags = 0;
int is_locked; int is_locked;
...@@ -506,48 +505,59 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -506,48 +505,59 @@ static void pstore_dump(struct kmsg_dumper *dumper,
oopscount++; oopscount++;
while (total < kmsg_bytes) { while (total < kmsg_bytes) {
char *dst; char *dst;
unsigned long size; size_t dst_size;
int hsize; int header_size;
int zipped_len = -1; int zipped_len = -1;
size_t len; size_t dump_size;
bool compressed = false; struct pstore_record record = {
size_t total_len; .type = PSTORE_TYPE_DMESG,
.count = oopscount,
.reason = reason,
.part = part,
.compressed = false,
.buf = psinfo->buf,
.psi = psinfo,
};
if (big_oops_buf && is_locked) { if (big_oops_buf && is_locked) {
dst = big_oops_buf; dst = big_oops_buf;
size = big_oops_buf_sz; dst_size = big_oops_buf_sz;
} else { } else {
dst = psinfo->buf; dst = psinfo->buf;
size = psinfo->bufsize; dst_size = psinfo->bufsize;
} }
hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); /* Write dump header. */
size -= hsize; header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
oopscount, part);
dst_size -= header_size;
if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, /* Write dump contents. */
size, &len)) if (!kmsg_dump_get_buffer(dumper, true, dst + header_size,
dst_size, &dump_size))
break; break;
if (big_oops_buf && is_locked) { if (big_oops_buf && is_locked) {
zipped_len = pstore_compress(dst, psinfo->buf, zipped_len = pstore_compress(dst, psinfo->buf,
hsize + len, psinfo->bufsize); header_size + dump_size,
psinfo->bufsize);
if (zipped_len > 0) { if (zipped_len > 0) {
compressed = true; record.compressed = true;
total_len = zipped_len; record.size = zipped_len;
} else { } else {
total_len = copy_kmsg_to_buffer(hsize, len); record.size = copy_kmsg_to_buffer(header_size,
dump_size);
} }
} else { } else {
total_len = hsize + len; record.size = header_size + dump_size;
} }
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, ret = psinfo->write(&record);
oopscount, compressed, total_len, psinfo);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1; pstore_new_entry = 1;
total += total_len; total += record.size;
part++; part++;
} }
if (is_locked) if (is_locked)
...@@ -577,8 +587,11 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) ...@@ -577,8 +587,11 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
const char *e = s + c; const char *e = s + c;
while (s < e) { while (s < e) {
struct pstore_record record = {
.type = PSTORE_TYPE_CONSOLE,
.psi = psinfo,
};
unsigned long flags; unsigned long flags;
u64 id;
if (c > psinfo->bufsize) if (c > psinfo->bufsize)
c = psinfo->bufsize; c = psinfo->bufsize;
...@@ -589,8 +602,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) ...@@ -589,8 +602,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
} else { } else {
spin_lock_irqsave(&psinfo->buf_lock, flags); spin_lock_irqsave(&psinfo->buf_lock, flags);
} }
psinfo->write_buf(PSTORE_TYPE_CONSOLE, 0, &id, 0, record.buf = (char *)s;
s, 0, c, psinfo); record.size = c;
psinfo->write(&record);
spin_unlock_irqrestore(&psinfo->buf_lock, flags); spin_unlock_irqrestore(&psinfo->buf_lock, flags);
s += c; s += c;
c = e - s; c = e - s;
...@@ -618,48 +632,30 @@ static void pstore_register_console(void) {} ...@@ -618,48 +632,30 @@ static void pstore_register_console(void) {}
static void pstore_unregister_console(void) {} static void pstore_unregister_console(void) {}
#endif #endif
static int pstore_write_compat(enum pstore_type_id type, static int pstore_write_user_compat(struct pstore_record *record,
enum kmsg_dump_reason reason, const char __user *buf)
u64 *id, unsigned int part, int count,
bool compressed, size_t size,
struct pstore_info *psi)
{ {
return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, int ret = 0;
size, psi);
}
static int pstore_write_buf_user_compat(enum pstore_type_id type, if (record->buf)
enum kmsg_dump_reason reason, return -EINVAL;
u64 *id, unsigned int part,
const char __user *buf,
bool compressed, size_t size,
struct pstore_info *psi)
{
unsigned long flags = 0;
size_t i, bufsize = size;
long ret = 0;
if (unlikely(!access_ok(VERIFY_READ, buf, size))) record->buf = kmalloc(record->size, GFP_KERNEL);
return -EFAULT; if (!record->buf)
if (bufsize > psinfo->bufsize) return -ENOMEM;
bufsize = psinfo->bufsize;
spin_lock_irqsave(&psinfo->buf_lock, flags);
for (i = 0; i < size; ) {
size_t c = min(size - i, bufsize);
ret = __copy_from_user(psinfo->buf, buf + i, c); if (unlikely(copy_from_user(record->buf, buf, record->size))) {
if (unlikely(ret != 0)) {
ret = -EFAULT; ret = -EFAULT;
break; goto out;
}
ret = psi->write_buf(type, reason, id, part, psinfo->buf,
compressed, c, psi);
if (unlikely(ret < 0))
break;
i += c;
} }
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
return unlikely(ret < 0) ? ret : size; ret = record->psi->write(record);
out:
kfree(record->buf);
record->buf = NULL;
return unlikely(ret < 0) ? ret : record->size;
} }
/* /*
...@@ -673,19 +669,35 @@ int pstore_register(struct pstore_info *psi) ...@@ -673,19 +669,35 @@ int pstore_register(struct pstore_info *psi)
{ {
struct module *owner = psi->owner; struct module *owner = psi->owner;
if (backend && strcmp(backend, psi->name)) if (backend && strcmp(backend, psi->name)) {
pr_warn("ignoring unexpected backend '%s'\n", psi->name);
return -EPERM; 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); spin_lock(&pstore_lock);
if (psinfo) { if (psinfo) {
pr_warn("backend '%s' already loaded: ignoring '%s'\n",
psinfo->name, psi->name);
spin_unlock(&pstore_lock); spin_unlock(&pstore_lock);
return -EBUSY; return -EBUSY;
} }
if (!psi->write) if (!psi->write_user)
psi->write = pstore_write_compat; psi->write_user = pstore_write_user_compat;
if (!psi->write_buf_user)
psi->write_buf_user = pstore_write_buf_user_compat;
psinfo = psi; psinfo = psi;
mutex_init(&psinfo->read_mutex); mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock); spin_unlock(&pstore_lock);
...@@ -709,6 +721,7 @@ int pstore_register(struct pstore_info *psi) ...@@ -709,6 +721,7 @@ int pstore_register(struct pstore_info *psi)
if (psi->flags & PSTORE_FLAGS_PMSG) if (psi->flags & PSTORE_FLAGS_PMSG)
pstore_register_pmsg(); pstore_register_pmsg();
/* Start watching for new records, if desired. */
if (pstore_update_ms >= 0) { if (pstore_update_ms >= 0) {
pstore_timer.expires = jiffies + pstore_timer.expires = jiffies +
msecs_to_jiffies(pstore_update_ms); msecs_to_jiffies(pstore_update_ms);
...@@ -721,16 +734,21 @@ int pstore_register(struct pstore_info *psi) ...@@ -721,16 +734,21 @@ int pstore_register(struct pstore_info *psi)
*/ */
backend = psi->name; backend = psi->name;
module_put(owner);
pr_info("Registered %s as persistent store backend\n", psi->name); pr_info("Registered %s as persistent store backend\n", psi->name);
module_put(owner);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(pstore_register); EXPORT_SYMBOL_GPL(pstore_register);
void pstore_unregister(struct pstore_info *psi) void pstore_unregister(struct pstore_info *psi)
{ {
/* Stop timer and make sure all work has finished. */
pstore_update_ms = -1;
del_timer_sync(&pstore_timer);
flush_work(&pstore_work);
if (psi->flags & PSTORE_FLAGS_PMSG) if (psi->flags & PSTORE_FLAGS_PMSG)
pstore_unregister_pmsg(); pstore_unregister_pmsg();
if (psi->flags & PSTORE_FLAGS_FTRACE) if (psi->flags & PSTORE_FLAGS_FTRACE)
...@@ -747,67 +765,100 @@ void pstore_unregister(struct pstore_info *psi) ...@@ -747,67 +765,100 @@ void pstore_unregister(struct pstore_info *psi)
} }
EXPORT_SYMBOL_GPL(pstore_unregister); EXPORT_SYMBOL_GPL(pstore_unregister);
static void decompress_record(struct pstore_record *record)
{
int unzipped_len;
char *decompressed;
/* Only PSTORE_TYPE_DMESG support compression. */
if (!record->compressed || record->type != PSTORE_TYPE_DMESG) {
pr_warn("ignored compressed record type %d\n", record->type);
return;
}
/* No compression method has created the common buffer. */
if (!big_oops_buf) {
pr_warn("no decompression buffer allocated\n");
return;
}
unzipped_len = pstore_decompress(record->buf, big_oops_buf,
record->size, big_oops_buf_sz);
if (unzipped_len <= 0) {
pr_err("decompression failed: %d\n", unzipped_len);
return;
}
/* Build new buffer for decompressed contents. */
decompressed = kmalloc(unzipped_len + record->ecc_notice_size,
GFP_KERNEL);
if (!decompressed) {
pr_err("decompression ran out of memory\n");
return;
}
memcpy(decompressed, big_oops_buf, unzipped_len);
/* Append ECC notice to decompressed buffer. */
memcpy(decompressed + unzipped_len, record->buf + record->size,
record->ecc_notice_size);
/* Swap out compresed contents with decompressed contents. */
kfree(record->buf);
record->buf = decompressed;
record->size = unzipped_len;
record->compressed = false;
}
/* /*
* Read all the records from the persistent store. Create * Read all the records from one persistent store backend. Create
* files in our filesystem. Don't warn about -EEXIST errors * files in our filesystem. Don't warn about -EEXIST errors
* when we are re-scanning the backing store looking to add new * when we are re-scanning the backing store looking to add new
* error records. * error records.
*/ */
void pstore_get_records(int quiet) void pstore_get_backend_records(struct pstore_info *psi,
{ struct dentry *root, int quiet)
struct pstore_info *psi = psinfo; {
char *buf = NULL; int failed = 0;
ssize_t size;
u64 id; if (!psi || !root)
int count;
enum pstore_type_id type;
struct timespec time;
int failed = 0, rc;
bool compressed;
int unzipped_len = -1;
ssize_t ecc_notice_size = 0;
if (!psi)
return; return;
mutex_lock(&psi->read_mutex); mutex_lock(&psi->read_mutex);
if (psi->open && psi->open(psi)) if (psi->open && psi->open(psi))
goto out; goto out;
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, /*
&ecc_notice_size, psi)) > 0) { * Backend callback read() allocates record.buf. decompress_record()
if (compressed && (type == PSTORE_TYPE_DMESG)) { * may reallocate record.buf. On success, pstore_mkfile() will keep
if (big_oops_buf) * the record.buf, so free it only on failure.
unzipped_len = pstore_decompress(buf, */
big_oops_buf, size, for (;;) {
big_oops_buf_sz); struct pstore_record *record;
int rc;
if (unzipped_len > 0) {
if (ecc_notice_size) record = kzalloc(sizeof(*record), GFP_KERNEL);
memcpy(big_oops_buf + unzipped_len, if (!record) {
buf + size, ecc_notice_size); pr_err("out of memory creating record\n");
kfree(buf); break;
buf = big_oops_buf; }
size = unzipped_len; record->psi = psi;
compressed = false;
} else { record->size = psi->read(record);
pr_err("decompression failed;returned %d\n",
unzipped_len); /* No more records left in backend? */
compressed = true; if (record->size <= 0)
} break;
}
rc = pstore_mkfile(type, psi->name, id, count, buf, decompress_record(record);
compressed, size + ecc_notice_size, rc = pstore_mkfile(root, record);
time, psi); if (rc) {
if (unzipped_len < 0) { /* pstore_mkfile() did not take record, so free it. */
/* Free buffer other than big oops */ kfree(record->buf);
kfree(buf); kfree(record);
buf = NULL; if (rc != -EEXIST || !quiet)
} else
unzipped_len = -1;
if (rc && (rc != -EEXIST || !quiet))
failed++; failed++;
} }
}
if (psi->close) if (psi->close)
psi->close(psi); psi->close(psi);
out: out:
...@@ -830,7 +881,9 @@ static void pstore_timefunc(unsigned long dummy) ...@@ -830,7 +881,9 @@ static void pstore_timefunc(unsigned long dummy)
schedule_work(&pstore_work); schedule_work(&pstore_work);
} }
mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); if (pstore_update_ms >= 0)
mod_timer(&pstore_timer,
jiffies + msecs_to_jiffies(pstore_update_ms));
} }
module_param(backend, charp, 0444); module_param(backend, charp, 0444);
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "internal.h" #include "internal.h"
static DEFINE_MUTEX(pmsg_lock); static DEFINE_MUTEX(pmsg_lock);
...@@ -23,19 +22,22 @@ static DEFINE_MUTEX(pmsg_lock); ...@@ -23,19 +22,22 @@ static DEFINE_MUTEX(pmsg_lock);
static ssize_t write_pmsg(struct file *file, const char __user *buf, static ssize_t write_pmsg(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
u64 id; struct pstore_record record = {
.type = PSTORE_TYPE_PMSG,
.size = count,
.psi = psinfo,
};
int ret; int ret;
if (!count) if (!count)
return 0; return 0;
/* check outside lock, page in any data. write_buf_user also checks */ /* check outside lock, page in any data. write_user also checks */
if (!access_ok(VERIFY_READ, buf, count)) if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT; return -EFAULT;
mutex_lock(&pmsg_lock); mutex_lock(&pmsg_lock);
ret = psinfo->write_buf_user(PSTORE_TYPE_PMSG, 0, &id, 0, buf, 0, count, ret = psinfo->write_user(&record, buf);
psinfo);
mutex_unlock(&pmsg_lock); mutex_unlock(&pmsg_lock);
return ret ? ret : count; return ret ? ret : count;
} }
......
...@@ -235,35 +235,34 @@ static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest, ...@@ -235,35 +235,34 @@ static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
return 0; return 0;
} }
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t ramoops_pstore_read(struct pstore_record *record)
int *count, struct timespec *time,
char **buf, bool *compressed,
ssize_t *ecc_notice_size,
struct pstore_info *psi)
{ {
ssize_t size = 0; ssize_t size = 0;
struct ramoops_context *cxt = psi->data; struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz = NULL; struct persistent_ram_zone *prz = NULL;
int header_length = 0; int header_length = 0;
bool free_prz = false; bool free_prz = false;
/* Ramoops headers provide time stamps for PSTORE_TYPE_DMESG, but /*
* Ramoops headers provide time stamps for PSTORE_TYPE_DMESG, but
* PSTORE_TYPE_CONSOLE and PSTORE_TYPE_FTRACE don't currently have * PSTORE_TYPE_CONSOLE and PSTORE_TYPE_FTRACE don't currently have
* valid time stamps, so it is initialized to zero. * valid time stamps, so it is initialized to zero.
*/ */
time->tv_sec = 0; record->time.tv_sec = 0;
time->tv_nsec = 0; record->time.tv_nsec = 0;
*compressed = false; record->compressed = false;
/* Find the next valid persistent_ram_zone for DMESG */ /* Find the next valid persistent_ram_zone for DMESG */
while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) { while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) {
prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt, prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, id, type, cxt->max_dump_cnt, &record->id,
&record->type,
PSTORE_TYPE_DMESG, 1); PSTORE_TYPE_DMESG, 1);
if (!prz_ok(prz)) if (!prz_ok(prz))
continue; continue;
header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz), header_length = ramoops_read_kmsg_hdr(persistent_ram_old(prz),
time, compressed); &record->time,
&record->compressed);
/* Clear and skip this DMESG record if it has no valid header */ /* Clear and skip this DMESG record if it has no valid header */
if (!header_length) { if (!header_length) {
persistent_ram_free_old(prz); persistent_ram_free_old(prz);
...@@ -274,18 +273,20 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -274,18 +273,20 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (!prz_ok(prz)) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
1, id, type, PSTORE_TYPE_CONSOLE, 0); 1, &record->id, &record->type,
PSTORE_TYPE_CONSOLE, 0);
if (!prz_ok(prz)) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, id, type, PSTORE_TYPE_PMSG, 0); 1, &record->id, &record->type,
PSTORE_TYPE_PMSG, 0);
/* ftrace is last since it may want to dynamically allocate memory. */ /* ftrace is last since it may want to dynamically allocate memory. */
if (!prz_ok(prz)) { if (!prz_ok(prz)) {
if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) { if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) {
prz = ramoops_get_next_prz(cxt->fprzs, prz = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt, 1, id, type, &cxt->ftrace_read_cnt, 1, &record->id,
PSTORE_TYPE_FTRACE, 0); &record->type, PSTORE_TYPE_FTRACE, 0);
} else { } else {
/* /*
* Build a new dummy record which combines all the * Build a new dummy record which combines all the
...@@ -302,8 +303,10 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -302,8 +303,10 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) { while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) {
prz_next = ramoops_get_next_prz(cxt->fprzs, prz_next = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt, &cxt->ftrace_read_cnt,
cxt->max_ftrace_cnt, id, cxt->max_ftrace_cnt,
type, PSTORE_TYPE_FTRACE, 0); &record->id,
&record->type,
PSTORE_TYPE_FTRACE, 0);
if (!prz_ok(prz_next)) if (!prz_ok(prz_next))
continue; continue;
...@@ -316,7 +319,7 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -316,7 +319,7 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (size) if (size)
goto out; goto out;
} }
*id = 0; record->id = 0;
prz = tmp_prz; prz = tmp_prz;
} }
} }
...@@ -329,17 +332,19 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -329,17 +332,19 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
size = persistent_ram_old_size(prz) - header_length; size = persistent_ram_old_size(prz) - header_length;
/* ECC correction notice */ /* ECC correction notice */
*ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); record->ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
*buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL); record->buf = kmalloc(size + record->ecc_notice_size + 1, GFP_KERNEL);
if (*buf == NULL) { if (record->buf == NULL) {
size = -ENOMEM; size = -ENOMEM;
goto out; goto out;
} }
memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size); memcpy(record->buf, (char *)persistent_ram_old(prz) + header_length,
size);
persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1); persistent_ram_ecc_string(prz, record->buf + size,
record->ecc_notice_size + 1);
out: out:
if (free_prz) { if (free_prz) {
...@@ -373,23 +378,18 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, ...@@ -373,23 +378,18 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
return len; return len;
} }
static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, static int notrace ramoops_pstore_write(struct pstore_record *record)
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 ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz; struct persistent_ram_zone *prz;
size_t hlen; size_t size, hlen;
if (type == PSTORE_TYPE_CONSOLE) { if (record->type == PSTORE_TYPE_CONSOLE) {
if (!cxt->cprz) if (!cxt->cprz)
return -ENOMEM; return -ENOMEM;
persistent_ram_write(cxt->cprz, buf, size); persistent_ram_write(cxt->cprz, record->buf, record->size);
return 0; return 0;
} else if (type == PSTORE_TYPE_FTRACE) { } else if (record->type == PSTORE_TYPE_FTRACE) {
int zonenum; int zonenum;
if (!cxt->fprzs) if (!cxt->fprzs)
...@@ -402,33 +402,36 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, ...@@ -402,33 +402,36 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
else else
zonenum = 0; zonenum = 0;
persistent_ram_write(cxt->fprzs[zonenum], buf, size); persistent_ram_write(cxt->fprzs[zonenum], record->buf,
record->size);
return 0; return 0;
} else if (type == PSTORE_TYPE_PMSG) { } else if (record->type == PSTORE_TYPE_PMSG) {
pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__); pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (type != PSTORE_TYPE_DMESG) if (record->type != PSTORE_TYPE_DMESG)
return -EINVAL; return -EINVAL;
/* Out of the various dmesg dump types, ramoops is currently designed /*
* Out of the various dmesg dump types, ramoops is currently designed
* to only store crash logs, rather than storing general kernel logs. * to only store crash logs, rather than storing general kernel logs.
*/ */
if (reason != KMSG_DUMP_OOPS && if (record->reason != KMSG_DUMP_OOPS &&
reason != KMSG_DUMP_PANIC) record->reason != KMSG_DUMP_PANIC)
return -EINVAL; return -EINVAL;
/* Skip Oopes when configured to do so. */ /* Skip Oopes when configured to do so. */
if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops) if (record->reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
return -EINVAL; return -EINVAL;
/* Explicitly only take the first part of any new crash. /*
* Explicitly only take the first part of any new crash.
* If our buffer is larger than kmsg_bytes, this can never happen, * 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 * and if our buffer is smaller than kmsg_bytes, we don't want the
* report split across multiple records. * report split across multiple records.
*/ */
if (part != 1) if (record->part != 1)
return -ENOSPC; return -ENOSPC;
if (!cxt->dprzs) if (!cxt->dprzs)
...@@ -436,53 +439,50 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, ...@@ -436,53 +439,50 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
prz = cxt->dprzs[cxt->dump_write_cnt]; prz = cxt->dprzs[cxt->dump_write_cnt];
hlen = ramoops_write_kmsg_hdr(prz, compressed); /* Build header and append record contents. */
hlen = ramoops_write_kmsg_hdr(prz, record->compressed);
size = record->size;
if (size + hlen > prz->buffer_size) if (size + hlen > prz->buffer_size)
size = prz->buffer_size - hlen; size = prz->buffer_size - hlen;
persistent_ram_write(prz, buf, size); persistent_ram_write(prz, record->buf, size);
cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt;
return 0; return 0;
} }
static int notrace ramoops_pstore_write_buf_user(enum pstore_type_id type, static int notrace ramoops_pstore_write_user(struct pstore_record *record,
enum kmsg_dump_reason reason, const char __user *buf)
u64 *id, unsigned int part,
const char __user *buf,
bool compressed, size_t size,
struct pstore_info *psi)
{ {
if (type == PSTORE_TYPE_PMSG) { if (record->type == PSTORE_TYPE_PMSG) {
struct ramoops_context *cxt = psi->data; struct ramoops_context *cxt = record->psi->data;
if (!cxt->mprz) if (!cxt->mprz)
return -ENOMEM; return -ENOMEM;
return persistent_ram_write_user(cxt->mprz, buf, size); return persistent_ram_write_user(cxt->mprz, buf, record->size);
} }
return -EINVAL; return -EINVAL;
} }
static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, static int ramoops_pstore_erase(struct pstore_record *record)
struct timespec time, struct pstore_info *psi)
{ {
struct ramoops_context *cxt = psi->data; struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz; struct persistent_ram_zone *prz;
switch (type) { switch (record->type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
if (id >= cxt->max_dump_cnt) if (record->id >= cxt->max_dump_cnt)
return -EINVAL; return -EINVAL;
prz = cxt->dprzs[id]; prz = cxt->dprzs[record->id];
break; break;
case PSTORE_TYPE_CONSOLE: case PSTORE_TYPE_CONSOLE:
prz = cxt->cprz; prz = cxt->cprz;
break; break;
case PSTORE_TYPE_FTRACE: case PSTORE_TYPE_FTRACE:
if (id >= cxt->max_ftrace_cnt) if (record->id >= cxt->max_ftrace_cnt)
return -EINVAL; return -EINVAL;
prz = cxt->fprzs[id]; prz = cxt->fprzs[record->id];
break; break;
case PSTORE_TYPE_PMSG: case PSTORE_TYPE_PMSG:
prz = cxt->mprz; prz = cxt->mprz;
...@@ -503,8 +503,8 @@ static struct ramoops_context oops_cxt = { ...@@ -503,8 +503,8 @@ static struct ramoops_context oops_cxt = {
.name = "ramoops", .name = "ramoops",
.open = ramoops_pstore_open, .open = ramoops_pstore_open,
.read = ramoops_pstore_read, .read = ramoops_pstore_read,
.write_buf = ramoops_pstore_write_buf, .write = ramoops_pstore_write,
.write_buf_user = ramoops_pstore_write_buf_user, .write_user = ramoops_pstore_write_user,
.erase = ramoops_pstore_erase, .erase = ramoops_pstore_erase,
}, },
}; };
......
...@@ -532,7 +532,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, ...@@ -532,7 +532,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
} }
/* Initialize general buffer state. */ /* Initialize general buffer state. */
prz->buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(buffer_lock); raw_spin_lock_init(&prz->buffer_lock);
prz->flags = flags; prz->flags = flags;
ret = persistent_ram_buffer_map(start, size, prz, memtype); ret = persistent_ram_buffer_map(start, size, prz, memtype);
......
...@@ -30,7 +30,9 @@ ...@@ -30,7 +30,9 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/types.h> #include <linux/types.h>
/* types */ struct module;
/* pstore record types (see fs/pstore/inode.c for filename templates) */
enum pstore_type_id { enum pstore_type_id {
PSTORE_TYPE_DMESG = 0, PSTORE_TYPE_DMESG = 0,
PSTORE_TYPE_MCE = 1, PSTORE_TYPE_MCE = 1,
...@@ -45,42 +47,146 @@ enum pstore_type_id { ...@@ -45,42 +47,146 @@ enum pstore_type_id {
PSTORE_TYPE_UNKNOWN = 255 PSTORE_TYPE_UNKNOWN = 255
}; };
struct module; struct pstore_info;
/**
* struct pstore_record - details of a pstore record entry
* @psi: pstore backend driver information
* @type: pstore record type
* @id: per-type unique identifier for record
* @time: timestamp of the record
* @buf: pointer to record contents
* @size: size of @buf
* @ecc_notice_size:
* ECC information for @buf
*
* Valid for PSTORE_TYPE_DMESG @type:
*
* @count: Oops count since boot
* @reason: kdump reason for notification
* @part: position in a multipart record
* @compressed: whether the buffer is compressed
*
*/
struct pstore_record {
struct pstore_info *psi;
enum pstore_type_id type;
u64 id;
struct timespec time;
char *buf;
ssize_t size;
ssize_t ecc_notice_size;
int count;
enum kmsg_dump_reason reason;
unsigned int part;
bool compressed;
};
/**
* struct pstore_info - backend pstore driver structure
*
* @owner: module which is repsonsible for this backend driver
* @name: name of the backend driver
*
* @buf_lock: spinlock to serialize access to @buf
* @buf: preallocated crash dump buffer
* @bufsize: size of @buf available for crash dump writes
*
* @read_mutex: serializes @open, @read, @close, and @erase callbacks
* @flags: bitfield of frontends the backend can accept writes for
* @data: backend-private pointer passed back during callbacks
*
* Callbacks:
*
* @open:
* Notify backend that pstore is starting a full read of backend
* records. Followed by one or more @read calls, and a final @close.
*
* @psi: in: pointer to the struct pstore_info for the backend
*
* Returns 0 on success, and non-zero on error.
*
* @close:
* Notify backend that pstore has finished a full read of backend
* records. Always preceded by an @open call and one or more @read
* calls.
*
* @psi: in: pointer to the struct pstore_info for the backend
*
* Returns 0 on success, and non-zero on error. (Though pstore will
* ignore the error.)
*
* @read:
* Read next available backend record. Called after a successful
* @open.
*
* @record:
* pointer to record to populate. @buf should be allocated
* by the backend and filled. At least @type and @id should
* be populated, since these are used when creating pstorefs
* file names.
*
* Returns record size on success, zero when no more records are
* available, or negative on error.
*
* @write:
* A newly generated record needs to be written to backend storage.
*
* @record:
* pointer to record metadata. When @type is PSTORE_TYPE_DMESG,
* @buf will be pointing to the preallocated @psi.buf, since
* memory allocation may be broken during an Oops. Regardless,
* @buf must be proccesed or copied before returning. The
* backend is also expected to write @id with something that
8 can help identify this record to a future @erase callback.
*
* Returns 0 on success, and non-zero on error.
*
* @write_user:
* Perform a frontend write to a backend record, using a specified
* buffer that is coming directly from userspace, instead of the
* @record @buf.
*
* @record: pointer to record metadata.
* @buf: pointer to userspace contents to write to backend
*
* Returns 0 on success, and non-zero on error.
*
* @erase:
* Delete a record from backend storage. Different backends
* identify records differently, so entire original record is
* passed back to assist in identification of what the backend
* should remove from storage.
*
* @record: pointer to record metadata.
*
* Returns 0 on success, and non-zero on error.
*
*/
struct pstore_info { struct pstore_info {
struct module *owner; struct module *owner;
char *name; char *name;
spinlock_t buf_lock; /* serialize access to 'buf' */
spinlock_t buf_lock;
char *buf; char *buf;
size_t bufsize; size_t bufsize;
struct mutex read_mutex; /* serialize open/read/close */
struct mutex read_mutex;
int flags; int flags;
void *data;
int (*open)(struct pstore_info *psi); int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi); int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type, ssize_t (*read)(struct pstore_record *record);
int *count, struct timespec *time, char **buf, int (*write)(struct pstore_record *record);
bool *compressed, ssize_t *ecc_notice_size, int (*write_user)(struct pstore_record *record,
struct pstore_info *psi); const char __user *buf);
int (*write)(enum pstore_type_id type, int (*erase)(struct pstore_record *record);
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, int count, bool compressed,
size_t size, struct pstore_info *psi);
int (*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);
int (*write_buf_user)(enum pstore_type_id type,
enum kmsg_dump_reason reason, u64 *id,
unsigned int part, const char __user *buf,
bool compressed, size_t size, struct pstore_info *psi);
int (*erase)(enum pstore_type_id type, u64 id,
int count, struct timespec time,
struct pstore_info *psi);
void *data;
}; };
/* Supported frontends */
#define PSTORE_FLAGS_DMESG (1 << 0) #define PSTORE_FLAGS_DMESG (1 << 0)
#define PSTORE_FLAGS_FRAGILE PSTORE_FLAGS_DMESG
#define PSTORE_FLAGS_CONSOLE (1 << 1) #define PSTORE_FLAGS_CONSOLE (1 << 1)
#define PSTORE_FLAGS_FTRACE (1 << 2) #define PSTORE_FLAGS_FTRACE (1 << 2)
#define PSTORE_FLAGS_PMSG (1 << 3) #define PSTORE_FLAGS_PMSG (1 << 3)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment