- 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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 Comment: Kees Cook <kees@outflux.net>
 
 iQIcBAABCgAGBQJZB3wzAAoJEIly9N/cbcAmVQAP/3+GzoxUcL43ypsDa1CmCsFN
 l2roQjzWLNGfHgq5qkS/mNtrUdEvBMUBd2oyhHcaiqM0DsuuO3rKTp6dZ8oczYjN
 6GoTmU8ZwIPze3VEadNPjCIdpsfTMNKtZvVJCTrWnsgXxTawDS89qqr7SCs3qhBS
 Dm1E8oX77YyhOKoGA6O3CJxpdm/Ge4+KpPR6Uwj90eVro04vYoiwnBjyLUzE7w1l
 JcXEGEh1t5NjUxHeMwW7HZwJYZfA3DQ6I3MOOzGhf9tsKp6J0LQTTV8PMSEo1mif
 mLZhDBy8BBlunL+b2Tp3+c4QItGSHkBCWASI2RLa2TM7xvL67oC+qm/WaUyoRovy
 hllEG96rsCs3Zx7fFFsfQCwURcTWfJQMrD+0d/fM+P2ylWvgp+KU6PeLTS9IHu6M
 3n6i5i6A6OY/QvmZr1tN/06kUBjtQmo8EgQ0jxoxAlWyNcJqi93hmJyaRW28KxjS
 tjFTNLZMrslj0UDmjiD6fIuaT6gsGDB+3wAMPVAf+iV/k/2GUlj3ZILe4RaABAe9
 8xaUu11tZ5sTniayZ+10bA+6+K5n7uTlgU8RfFgaUZoRAzHgtyijOmdo6N+HILfK
 klv59B1Fmf6JpDlq7L9vurOqE82FAWFn4DruFM2bAaky2meFUNbYFiNfwK4l6lPI
 pmAgpdgRRvNMBCEmbVfv
 =S14G
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2017-05-02 10:35:45 -07:00
commit 2575be8ad3
11 changed files with 600 additions and 454 deletions

View File

@ -389,51 +389,40 @@ static int nvram_pstore_open(struct pstore_info *psi)
/**
* nvram_pstore_write - pstore write callback for nvram
* @type: Type of message logged
* @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
* @record: pstore record to write, with @id to be set
*
* Called by pstore_dump() when an oops or panic report is logged in the
* printk buffer.
* Returns 0 on successful write.
*/
static int nvram_pstore_write(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 nvram_pstore_write(struct pstore_record *record)
{
int rc;
unsigned int err_type = ERR_TYPE_KERNEL_PANIC;
struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf;
/* 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;
if (clobbering_unread_rtas_event())
return -1;
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());
if (compressed)
if (record->compressed)
err_type = ERR_TYPE_KERNEL_PANIC_GZ;
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)
return rc;
*id = part;
record->id = record->part;
return 0;
}
@ -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 0 if we've been called before.
*/
static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi)
static ssize_t nvram_pstore_read(struct pstore_record *record)
{
struct oops_log_info *oops_hdr;
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,
switch (nvram_type_ids[read_type]) {
case PSTORE_TYPE_DMESG:
part = &oops_log_partition;
*type = PSTORE_TYPE_DMESG;
record->type = PSTORE_TYPE_DMESG;
break;
case PSTORE_TYPE_PPC_COMMON:
sig = NVRAM_SIG_SYS;
part = &common_partition;
*type = PSTORE_TYPE_PPC_COMMON;
*id = PSTORE_TYPE_PPC_COMMON;
time->tv_sec = 0;
time->tv_nsec = 0;
record->type = PSTORE_TYPE_PPC_COMMON;
record->id = PSTORE_TYPE_PPC_COMMON;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
break;
#ifdef CONFIG_PPC_PSERIES
case PSTORE_TYPE_PPC_RTAS:
part = &rtas_log_partition;
*type = PSTORE_TYPE_PPC_RTAS;
time->tv_sec = last_rtas_event;
time->tv_nsec = 0;
record->type = PSTORE_TYPE_PPC_RTAS;
record->time.tv_sec = last_rtas_event;
record->time.tv_nsec = 0;
break;
case PSTORE_TYPE_PPC_OF:
sig = NVRAM_SIG_OF;
part = &of_config_partition;
*type = PSTORE_TYPE_PPC_OF;
*id = PSTORE_TYPE_PPC_OF;
time->tv_sec = 0;
time->tv_nsec = 0;
record->type = PSTORE_TYPE_PPC_OF;
record->id = PSTORE_TYPE_PPC_OF;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
break;
#endif
#ifdef CONFIG_PPC_POWERNV
case PSTORE_TYPE_PPC_OPAL:
sig = NVRAM_SIG_FW;
part = &skiboot_partition;
*type = PSTORE_TYPE_PPC_OPAL;
*id = PSTORE_TYPE_PPC_OPAL;
time->tv_sec = 0;
time->tv_nsec = 0;
record->type = PSTORE_TYPE_PPC_OPAL;
record->id = PSTORE_TYPE_PPC_OPAL;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
break;
#endif
default:
@ -520,10 +506,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
return 0;
}
*count = 0;
record->count = 0;
if (part->os_partition)
*id = id_no;
record->id = id_no;
if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) {
size_t length, hdr_size;
@ -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 */
hdr_size = sizeof(u16);
length = be16_to_cpu(oops_hdr->version);
time->tv_sec = 0;
time->tv_nsec = 0;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
} else {
hdr_size = sizeof(*oops_hdr);
length = be16_to_cpu(oops_hdr->report_length);
time->tv_sec = be64_to_cpu(oops_hdr->timestamp);
time->tv_nsec = 0;
record->time.tv_sec = be64_to_cpu(oops_hdr->timestamp);
record->time.tv_nsec = 0;
}
*buf = kmemdup(buff + hdr_size, length, GFP_KERNEL);
record->buf = kmemdup(buff + hdr_size, length, GFP_KERNEL);
kfree(buff);
if (*buf == NULL)
if (record->buf == NULL)
return -ENOMEM;
*ecc_notice_size = 0;
record->ecc_notice_size = 0;
if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
*compressed = true;
record->compressed = true;
else
*compressed = false;
record->compressed = false;
return length;
}
*buf = buff;
record->buf = buff;
return part->size;
}
static struct pstore_info nvram_pstore_info = {
.owner = THIS_MODULE,
.name = "nvram",
.flags = PSTORE_FLAGS_DMESG,
.open = nvram_pstore_open,
.read = nvram_pstore_read,
.write = nvram_pstore_write,

View File

@ -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_close_pstore(struct pstore_info *psi);
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi);
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 ssize_t erst_reader(struct pstore_record *record);
static int erst_writer(struct pstore_record *record);
static int erst_clearer(struct pstore_record *record);
static struct pstore_info erst_info = {
.owner = THIS_MODULE,
@ -986,10 +980,7 @@ static int erst_close_pstore(struct pstore_info *psi)
return 0;
}
static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi)
static ssize_t erst_reader(struct pstore_record *record)
{
int rc;
ssize_t len = 0;
@ -1027,42 +1018,40 @@ skip:
if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
goto skip;
*buf = kmalloc(len, GFP_KERNEL);
if (*buf == NULL) {
record->buf = kmalloc(len, GFP_KERNEL);
if (record->buf == NULL) {
rc = -ENOMEM;
goto out;
}
memcpy(*buf, rcd->data, len - sizeof(*rcd));
*id = record_id;
*compressed = false;
*ecc_notice_size = 0;
memcpy(record->buf, rcd->data, len - sizeof(*rcd));
record->id = record_id;
record->compressed = false;
record->ecc_notice_size = 0;
if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG_Z) == 0) {
*type = PSTORE_TYPE_DMESG;
*compressed = true;
record->type = PSTORE_TYPE_DMESG;
record->compressed = true;
} else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG) == 0)
*type = PSTORE_TYPE_DMESG;
record->type = PSTORE_TYPE_DMESG;
else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_MCE) == 0)
*type = PSTORE_TYPE_MCE;
record->type = PSTORE_TYPE_MCE;
else
*type = PSTORE_TYPE_UNKNOWN;
record->type = PSTORE_TYPE_UNKNOWN;
if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
time->tv_sec = rcd->hdr.timestamp;
record->time.tv_sec = rcd->hdr.timestamp;
else
time->tv_sec = 0;
time->tv_nsec = 0;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
out:
kfree(rcd);
return (rc < 0) ? rc : (len - sizeof(*rcd));
}
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_writer(struct pstore_record *record)
{
struct cper_pstore_record *rcd = (struct cper_pstore_record *)
(erst_info.buf - sizeof(*rcd));
@ -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 */
rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
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.notification_type = CPER_NOTIFY_MCE;
rcd->hdr.record_id = cper_next_record_id();
rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
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;
/* fru_id and fru_text is invalid */
rcd->sec_hdr.validation_bits = 0;
rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
switch (type) {
switch (record->type) {
case PSTORE_TYPE_DMESG:
if (compressed)
if (record->compressed)
rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
else
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,
rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
ret = erst_write(&rcd->hdr);
*id = rcd->hdr.record_id;
record->id = rcd->hdr.record_id;
return ret;
}
static int erst_clearer(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi)
static int erst_clearer(struct pstore_record *record)
{
return erst_clear(id);
return erst_clear(record->id);
}
static int __init erst_init(void)

View File

@ -28,26 +28,16 @@ static int efi_pstore_close(struct pstore_info *psi)
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,
unsigned int part, int 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;
struct pstore_read_data *cb_data = data;
char name[DUMP_NAME_LEN], data_type;
int i;
int cnt;
@ -61,37 +51,37 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
name[i] = entry->var.VariableName[i];
if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
cb_data->type, &part, &cnt, &time, &data_type) == 5) {
*cb_data->id = generic_id(time, part, cnt);
*cb_data->count = cnt;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
&record->type, &part, &cnt, &time, &data_type) == 5) {
record->id = generic_id(time, part, cnt);
record->count = cnt;
record->time.tv_sec = time;
record->time.tv_nsec = 0;
if (data_type == 'C')
*cb_data->compressed = true;
record->compressed = true;
else
*cb_data->compressed = false;
*cb_data->ecc_notice_size = 0;
record->compressed = false;
record->ecc_notice_size = 0;
} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
cb_data->type, &part, &cnt, &time) == 4) {
*cb_data->id = generic_id(time, part, cnt);
*cb_data->count = cnt;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
*cb_data->compressed = false;
*cb_data->ecc_notice_size = 0;
&record->type, &part, &cnt, &time) == 4) {
record->id = generic_id(time, part, cnt);
record->count = cnt;
record->time.tv_sec = time;
record->time.tv_nsec = 0;
record->compressed = false;
record->ecc_notice_size = 0;
} 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,
* which doesn't support holding
* multiple logs, remains.
*/
*cb_data->id = generic_id(time, part, 0);
*cb_data->count = 0;
cb_data->timespec->tv_sec = time;
cb_data->timespec->tv_nsec = 0;
*cb_data->compressed = false;
*cb_data->ecc_notice_size = 0;
record->id = generic_id(time, part, 0);
record->count = 0;
record->time.tv_sec = time;
record->time.tv_nsec = 0;
record->compressed = false;
record->ecc_notice_size = 0;
} else
return 0;
@ -99,7 +89,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
__efivar_entry_get(entry, &entry->var.Attributes,
&entry->var.DataSize, entry->var.Data);
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));
return size;
@ -164,7 +154,7 @@ static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
/**
* 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
*
* 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,
* 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.
*/
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 list_head *head = &efivar_sysfs_list;
@ -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) {
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,
size < 0);
if (ret)
@ -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) {
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);
if (ret)
return ret;
@ -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(),
* and pstore will stop reading entry.
*/
static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *timespec,
char **buf, bool *compressed,
ssize_t *ecc_notice_size,
struct pstore_info *psi)
static ssize_t efi_pstore_read(struct pstore_record *record)
{
struct pstore_read_data data;
struct efivar_entry *entry = (struct efivar_entry *)record->psi->data;
ssize_t size;
data.id = id;
data.type = type;
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)
record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
if (!record->buf)
return -ENOMEM;
if (efivar_entry_iter_begin()) {
kfree(*data.buf);
return -EINTR;
size = -EINTR;
goto out;
}
size = efi_pstore_sysfs_entry_iter(&data,
(struct efivar_entry **)&psi->data);
size = efi_pstore_sysfs_entry_iter(record, &entry);
efivar_entry_iter_end();
if (size <= 0)
kfree(*data.buf);
out:
if (size <= 0) {
kfree(record->buf);
record->buf = NULL;
}
return size;
}
static int efi_pstore_write(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 efi_pstore_write(struct pstore_record *record)
{
char name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN];
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
int i, ret = 0;
sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count,
get_seconds(), compressed ? 'C' : 'D');
snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
record->type, record->part, record->count,
get_seconds(), record->compressed ? 'C' : 'D');
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
!pstore_cannot_block_path(reason),
size, psi->buf);
!pstore_cannot_block_path(record->reason),
record->size, record->psi->buf);
if (reason == KMSG_DUMP_OOPS)
if (record->reason == KMSG_DUMP_OOPS)
efivar_run_worker();
*id = part;
record->id = record->part;
return ret;
};
struct pstore_erase_data {
u64 id;
enum pstore_type_id type;
int count;
struct timespec time;
struct pstore_record *record;
efi_char16_t *name;
};
@ -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
* holding multiple logs, remains.
*/
sprintf(name_old, "dump-type%u-%u-%lu", ed->type,
(unsigned int)ed->id, ed->time.tv_sec);
snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu",
ed->record->type, (unsigned int)ed->record->id,
ed->record->time.tv_sec);
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name_old[i] = name_old[i];
@ -341,8 +319,7 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
return 1;
}
static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi)
static int efi_pstore_erase(struct pstore_record *record)
{
struct pstore_erase_data edata;
struct efivar_entry *entry = NULL;
@ -351,17 +328,16 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
int found, i;
unsigned int part;
do_div(id, 1000);
part = do_div(id, 100);
sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, time.tv_sec);
do_div(record->id, 1000);
part = do_div(record->id, 100);
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++)
efi_name[i] = name[i];
edata.id = part;
edata.type = type;
edata.count = count;
edata.time = time;
edata.record = record;
edata.name = efi_name;
if (efivar_entry_iter_begin())

View File

@ -37,6 +37,12 @@ static void notrace pstore_ftrace_call(unsigned long ip,
{
unsigned long flags;
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))
return;
@ -47,8 +53,7 @@ static void notrace pstore_ftrace_call(unsigned long ip,
rec.parent_ip = parent_ip;
pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++);
pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());
psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec,
0, sizeof(rec), psinfo);
psinfo->write(&record);
local_irq_restore(flags);
}
@ -117,7 +122,7 @@ void pstore_register_ftrace(void)
{
struct dentry *file;
if (!psinfo->write_buf)
if (!psinfo->write)
return;
pstore_ftrace_dir = debugfs_create_dir("pstore", NULL);

View File

@ -47,12 +47,8 @@ static LIST_HEAD(allpstore);
struct pstore_private {
struct list_head list;
struct pstore_info *psi;
enum pstore_type_id type;
u64 id;
int count;
ssize_t size;
char data[];
struct pstore_record *record;
size_t total_size;
};
struct pstore_ftrace_seq_data {
@ -63,6 +59,17 @@ struct pstore_ftrace_seq_data {
#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)
{
struct pstore_private *ps = s->private;
@ -72,9 +79,9 @@ static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
if (!data)
return NULL;
data->off = ps->size % REC_SIZE;
data->off = ps->total_size % REC_SIZE;
data->off += *pos * REC_SIZE;
if (data->off + REC_SIZE > ps->size) {
if (data->off + REC_SIZE > ps->total_size) {
kfree(data);
return NULL;
}
@ -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;
data->off += REC_SIZE;
if (data->off + REC_SIZE > ps->size)
if (data->off + REC_SIZE > ps->total_size)
return NULL;
(*pos)++;
@ -105,7 +112,9 @@ static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
{
struct pstore_private *ps = s->private;
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",
pstore_ftrace_decode_cpu(rec),
@ -125,7 +134,7 @@ static const struct seq_operations pstore_ftrace_seq_ops = {
static int pstore_check_syslog_permissions(struct pstore_private *ps)
{
switch (ps->type) {
switch (ps->record->type) {
case PSTORE_TYPE_DMESG:
case PSTORE_TYPE_CONSOLE:
return check_syslog_permissions(SYSLOG_ACTION_READ_ALL,
@ -141,9 +150,10 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
struct seq_file *sf = file->private_data;
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 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)
@ -157,7 +167,7 @@ static int pstore_file_open(struct inode *inode, struct file *file)
if (err)
return err;
if (ps->type == PSTORE_TYPE_FTRACE)
if (ps->record->type == PSTORE_TYPE_FTRACE)
sops = &pstore_ftrace_seq_ops;
err = seq_open(file, sops);
@ -193,20 +203,19 @@ static const struct file_operations pstore_file_operations = {
static int pstore_unlink(struct inode *dir, struct dentry *dentry)
{
struct pstore_private *p = d_inode(dentry)->i_private;
struct pstore_record *record = p->record;
int err;
err = pstore_check_syslog_permissions(p);
if (err)
return err;
if (p->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 {
if (!record->psi->erase)
return -EPERM;
}
mutex_lock(&record->psi->read_mutex);
record->psi->erase(record);
mutex_unlock(&record->psi->read_mutex);
return simple_unlink(dir, dentry);
}
@ -221,7 +230,7 @@ static void pstore_evict_inode(struct inode *inode)
spin_lock_irqsave(&allpstore_lock, flags);
list_del(&p->list);
spin_unlock_irqrestore(&allpstore_lock, flags);
kfree(p);
free_pstore_private(p);
}
}
@ -302,23 +311,23 @@ bool pstore_is_mounted(void)
* Load it up with "size" bytes of data from "buf".
* 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,
char *data, bool compressed, size_t size,
struct timespec time, struct pstore_info *psi)
int pstore_mkfile(struct dentry *root, struct pstore_record *record)
{
struct dentry *root = pstore_sb->s_root;
struct dentry *dentry;
struct inode *inode;
int rc = 0;
char name[PSTORE_NAMELEN];
struct pstore_private *private, *pos;
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);
list_for_each_entry(pos, &allpstore, list) {
if (pos->type == type &&
pos->id == id &&
pos->psi == psi) {
if (pos->record->type == record->type &&
pos->record->id == record->id &&
pos->record->psi == record->psi) {
rc = -EEXIST;
break;
}
@ -328,72 +337,74 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
return rc;
rc = -ENOMEM;
inode = pstore_get_inode(pstore_sb);
inode = pstore_get_inode(root->d_sb);
if (!inode)
goto fail;
inode->i_mode = S_IFREG | 0444;
inode->i_fop = &pstore_file_operations;
private = kmalloc(sizeof *private + size, GFP_KERNEL);
private = kzalloc(sizeof(*private), GFP_KERNEL);
if (!private)
goto fail_alloc;
private->type = type;
private->id = id;
private->count = count;
private->psi = psi;
private->record = record;
switch (type) {
switch (record->type) {
case PSTORE_TYPE_DMESG:
scnprintf(name, sizeof(name), "dmesg-%s-%lld%s",
psname, id, compressed ? ".enc.z" : "");
record->psi->name, record->id,
record->compressed ? ".enc.z" : "");
break;
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;
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;
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;
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;
case PSTORE_TYPE_PPC_OF:
scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld",
psname, id);
record->psi->name, record->id);
break;
case PSTORE_TYPE_PPC_COMMON:
scnprintf(name, sizeof(name), "powerpc-common-%s-%lld",
psname, id);
record->psi->name, record->id);
break;
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;
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;
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;
default:
scnprintf(name, sizeof(name), "type%d-%s-%lld",
type, psname, id);
record->type, record->psi->name, record->id);
break;
}
inode_lock(d_inode(root));
dentry = d_alloc_name(root, name);
if (!dentry)
goto fail_lockedalloc;
goto fail_private;
memcpy(private->data, data, size);
inode->i_size = private->size = size;
inode->i_size = private->total_size = size;
inode->i_private = private;
if (time.tv_sec)
inode->i_mtime = inode->i_ctime = time;
if (record->time.tv_sec)
inode->i_mtime = inode->i_ctime = record->time;
d_add(dentry, inode);
@ -401,13 +412,10 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
list_add(&private->list, &allpstore);
spin_unlock_irqrestore(&allpstore_lock, flags);
inode_unlock(d_inode(root));
return 0;
fail_lockedalloc:
inode_unlock(d_inode(root));
kfree(private);
fail_private:
free_pstore_private(private);
fail_alloc:
iput(inode);
@ -415,6 +423,27 @@ fail:
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)
{
struct inode *inode;

View File

@ -25,10 +25,10 @@ extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int);
extern void pstore_get_records(int);
extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
int count, char *data, bool compressed,
size_t size, struct timespec time,
struct pstore_info *psi);
extern void pstore_get_backend_records(struct pstore_info *psi,
struct dentry *root, int quiet);
extern int pstore_mkfile(struct dentry *root,
struct pstore_record *record);
extern bool pstore_is_mounted(void);
#endif

View File

@ -267,7 +267,7 @@ static void free_zlib(void)
big_oops_buf_sz = 0;
}
static struct pstore_zbackend backend_zlib = {
static const struct pstore_zbackend backend_zlib = {
.compress = compress_zlib,
.decompress = decompress_zlib,
.allocate = allocate_zlib,
@ -328,7 +328,7 @@ static void free_lzo(void)
big_oops_buf_sz = 0;
}
static struct pstore_zbackend backend_lzo = {
static const struct pstore_zbackend backend_lzo = {
.compress = compress_lzo,
.decompress = decompress_lzo,
.allocate = allocate_lzo,
@ -393,7 +393,7 @@ static void free_lz4(void)
big_oops_buf_sz = 0;
}
static struct pstore_zbackend backend_lz4 = {
static const struct pstore_zbackend backend_lz4 = {
.compress = compress_lz4,
.decompress = decompress_lz4,
.allocate = allocate_lz4,
@ -402,7 +402,7 @@ static struct pstore_zbackend backend_lz4 = {
};
#endif
static struct pstore_zbackend *zbackend =
static const struct pstore_zbackend *zbackend =
#if defined(CONFIG_PSTORE_ZLIB_COMPRESS)
&backend_zlib;
#elif defined(CONFIG_PSTORE_LZO_COMPRESS)
@ -484,7 +484,6 @@ static void pstore_dump(struct kmsg_dumper *dumper,
{
unsigned long total = 0;
const char *why;
u64 id;
unsigned int part = 1;
unsigned long flags = 0;
int is_locked;
@ -506,48 +505,59 @@ static void pstore_dump(struct kmsg_dumper *dumper,
oopscount++;
while (total < kmsg_bytes) {
char *dst;
unsigned long size;
int hsize;
size_t dst_size;
int header_size;
int zipped_len = -1;
size_t len;
bool compressed = false;
size_t total_len;
size_t dump_size;
struct pstore_record record = {
.type = PSTORE_TYPE_DMESG,
.count = oopscount,
.reason = reason,
.part = part,
.compressed = false,
.buf = psinfo->buf,
.psi = psinfo,
};
if (big_oops_buf && is_locked) {
dst = big_oops_buf;
size = big_oops_buf_sz;
dst_size = big_oops_buf_sz;
} else {
dst = psinfo->buf;
size = psinfo->bufsize;
dst_size = psinfo->bufsize;
}
hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part);
size -= hsize;
/* Write dump header. */
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,
size, &len))
/* Write dump contents. */
if (!kmsg_dump_get_buffer(dumper, true, dst + header_size,
dst_size, &dump_size))
break;
if (big_oops_buf && is_locked) {
zipped_len = pstore_compress(dst, psinfo->buf,
hsize + len, psinfo->bufsize);
header_size + dump_size,
psinfo->bufsize);
if (zipped_len > 0) {
compressed = true;
total_len = zipped_len;
record.compressed = true;
record.size = zipped_len;
} else {
total_len = copy_kmsg_to_buffer(hsize, len);
record.size = copy_kmsg_to_buffer(header_size,
dump_size);
}
} else {
total_len = hsize + len;
record.size = header_size + dump_size;
}
ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
oopscount, compressed, total_len, psinfo);
ret = psinfo->write(&record);
if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
pstore_new_entry = 1;
total += total_len;
total += record.size;
part++;
}
if (is_locked)
@ -577,8 +587,11 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
const char *e = s + c;
while (s < e) {
struct pstore_record record = {
.type = PSTORE_TYPE_CONSOLE,
.psi = psinfo,
};
unsigned long flags;
u64 id;
if (c > psinfo->bufsize)
c = psinfo->bufsize;
@ -589,8 +602,9 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
} else {
spin_lock_irqsave(&psinfo->buf_lock, flags);
}
psinfo->write_buf(PSTORE_TYPE_CONSOLE, 0, &id, 0,
s, 0, c, psinfo);
record.buf = (char *)s;
record.size = c;
psinfo->write(&record);
spin_unlock_irqrestore(&psinfo->buf_lock, flags);
s += c;
c = e - s;
@ -618,48 +632,30 @@ static void pstore_register_console(void) {}
static void pstore_unregister_console(void) {}
#endif
static int pstore_write_compat(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 pstore_write_user_compat(struct pstore_record *record,
const char __user *buf)
{
return psi->write_buf(type, reason, id, part, psinfo->buf, compressed,
size, psi);
}
int ret = 0;
static int pstore_write_buf_user_compat(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)
{
unsigned long flags = 0;
size_t i, bufsize = size;
long ret = 0;
if (record->buf)
return -EINVAL;
if (unlikely(!access_ok(VERIFY_READ, buf, size)))
return -EFAULT;
if (bufsize > psinfo->bufsize)
bufsize = psinfo->bufsize;
spin_lock_irqsave(&psinfo->buf_lock, flags);
for (i = 0; i < size; ) {
size_t c = min(size - i, bufsize);
record->buf = kmalloc(record->size, GFP_KERNEL);
if (!record->buf)
return -ENOMEM;
ret = __copy_from_user(psinfo->buf, buf + i, c);
if (unlikely(ret != 0)) {
ret = -EFAULT;
break;
}
ret = psi->write_buf(type, reason, id, part, psinfo->buf,
compressed, c, psi);
if (unlikely(ret < 0))
break;
i += c;
if (unlikely(copy_from_user(record->buf, buf, record->size))) {
ret = -EFAULT;
goto out;
}
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)
{
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;
}
/* 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)
psi->write = pstore_write_compat;
if (!psi->write_buf_user)
psi->write_buf_user = pstore_write_buf_user_compat;
if (!psi->write_user)
psi->write_user = pstore_write_user_compat;
psinfo = psi;
mutex_init(&psinfo->read_mutex);
spin_unlock(&pstore_lock);
@ -709,6 +721,7 @@ int pstore_register(struct pstore_info *psi)
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);
@ -721,16 +734,21 @@ int pstore_register(struct pstore_info *psi)
*/
backend = psi->name;
module_put(owner);
pr_info("Registered %s as persistent store backend\n", psi->name);
module_put(owner);
return 0;
}
EXPORT_SYMBOL_GPL(pstore_register);
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)
pstore_unregister_pmsg();
if (psi->flags & PSTORE_FLAGS_FTRACE)
@ -747,66 +765,99 @@ void pstore_unregister(struct pstore_info *psi)
}
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
* when we are re-scanning the backing store looking to add new
* 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;
ssize_t size;
u64 id;
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;
int failed = 0;
if (!psi)
if (!psi || !root)
return;
mutex_lock(&psi->read_mutex);
if (psi->open && psi->open(psi))
goto out;
while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
&ecc_notice_size, psi)) > 0) {
if (compressed && (type == PSTORE_TYPE_DMESG)) {
if (big_oops_buf)
unzipped_len = pstore_decompress(buf,
big_oops_buf, size,
big_oops_buf_sz);
/*
* Backend callback read() allocates record.buf. decompress_record()
* may reallocate record.buf. On success, pstore_mkfile() will keep
* the record.buf, so free it only on failure.
*/
for (;;) {
struct pstore_record *record;
int rc;
if (unzipped_len > 0) {
if (ecc_notice_size)
memcpy(big_oops_buf + unzipped_len,
buf + size, ecc_notice_size);
kfree(buf);
buf = big_oops_buf;
size = unzipped_len;
compressed = false;
} else {
pr_err("decompression failed;returned %d\n",
unzipped_len);
compressed = true;
}
record = kzalloc(sizeof(*record), GFP_KERNEL);
if (!record) {
pr_err("out of memory creating record\n");
break;
}
record->psi = psi;
record->size = psi->read(record);
/* No more records left in backend? */
if (record->size <= 0)
break;
decompress_record(record);
rc = pstore_mkfile(root, record);
if (rc) {
/* pstore_mkfile() did not take record, so free it. */
kfree(record->buf);
kfree(record);
if (rc != -EEXIST || !quiet)
failed++;
}
rc = pstore_mkfile(type, psi->name, id, count, buf,
compressed, size + ecc_notice_size,
time, psi);
if (unzipped_len < 0) {
/* Free buffer other than big oops */
kfree(buf);
buf = NULL;
} else
unzipped_len = -1;
if (rc && (rc != -EEXIST || !quiet))
failed++;
}
if (psi->close)
psi->close(psi);
@ -830,7 +881,9 @@ static void pstore_timefunc(unsigned long dummy)
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);

View File

@ -15,7 +15,6 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "internal.h"
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,
size_t count, loff_t *ppos)
{
u64 id;
struct pstore_record record = {
.type = PSTORE_TYPE_PMSG,
.size = count,
.psi = psinfo,
};
int ret;
if (!count)
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))
return -EFAULT;
mutex_lock(&pmsg_lock);
ret = psinfo->write_buf_user(PSTORE_TYPE_PMSG, 0, &id, 0, buf, 0, count,
psinfo);
ret = psinfo->write_user(&record, buf);
mutex_unlock(&pmsg_lock);
return ret ? ret : count;
}

View File

@ -235,35 +235,34 @@ static ssize_t ftrace_log_combine(struct persistent_ram_zone *dest,
return 0;
}
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time,
char **buf, bool *compressed,
ssize_t *ecc_notice_size,
struct pstore_info *psi)
static ssize_t ramoops_pstore_read(struct pstore_record *record)
{
ssize_t size = 0;
struct ramoops_context *cxt = psi->data;
struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz = NULL;
int header_length = 0;
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
* valid time stamps, so it is initialized to zero.
*/
time->tv_sec = 0;
time->tv_nsec = 0;
*compressed = false;
record->time.tv_sec = 0;
record->time.tv_nsec = 0;
record->compressed = false;
/* Find the next valid persistent_ram_zone for DMESG */
while (cxt->dump_read_cnt < cxt->max_dump_cnt && !prz) {
prz = ramoops_get_next_prz(cxt->dprzs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, id, type,
cxt->max_dump_cnt, &record->id,
&record->type,
PSTORE_TYPE_DMESG, 1);
if (!prz_ok(prz))
continue;
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 */
if (!header_length) {
persistent_ram_free_old(prz);
@ -274,18 +273,20 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (!prz_ok(prz))
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))
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. */
if (!prz_ok(prz)) {
if (!(cxt->flags & RAMOOPS_FLAG_FTRACE_PER_CPU)) {
prz = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt, 1, id, type,
PSTORE_TYPE_FTRACE, 0);
&cxt->ftrace_read_cnt, 1, &record->id,
&record->type, PSTORE_TYPE_FTRACE, 0);
} else {
/*
* 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,
while (cxt->ftrace_read_cnt < cxt->max_ftrace_cnt) {
prz_next = ramoops_get_next_prz(cxt->fprzs,
&cxt->ftrace_read_cnt,
cxt->max_ftrace_cnt, id,
type, PSTORE_TYPE_FTRACE, 0);
cxt->max_ftrace_cnt,
&record->id,
&record->type,
PSTORE_TYPE_FTRACE, 0);
if (!prz_ok(prz_next))
continue;
@ -316,7 +319,7 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
if (size)
goto out;
}
*id = 0;
record->id = 0;
prz = tmp_prz;
}
}
@ -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;
/* 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);
if (*buf == NULL) {
record->buf = kmalloc(size + record->ecc_notice_size + 1, GFP_KERNEL);
if (record->buf == NULL) {
size = -ENOMEM;
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:
if (free_prz) {
@ -373,23 +378,18 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
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)
static int notrace ramoops_pstore_write(struct pstore_record *record)
{
struct ramoops_context *cxt = psi->data;
struct ramoops_context *cxt = record->psi->data;
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)
return -ENOMEM;
persistent_ram_write(cxt->cprz, buf, size);
persistent_ram_write(cxt->cprz, record->buf, record->size);
return 0;
} else if (type == PSTORE_TYPE_FTRACE) {
} else if (record->type == PSTORE_TYPE_FTRACE) {
int zonenum;
if (!cxt->fprzs)
@ -402,33 +402,36 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
else
zonenum = 0;
persistent_ram_write(cxt->fprzs[zonenum], buf, size);
persistent_ram_write(cxt->fprzs[zonenum], record->buf,
record->size);
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__);
return -EINVAL;
}
if (type != PSTORE_TYPE_DMESG)
if (record->type != PSTORE_TYPE_DMESG)
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.
*/
if (reason != KMSG_DUMP_OOPS &&
reason != KMSG_DUMP_PANIC)
if (record->reason != KMSG_DUMP_OOPS &&
record->reason != KMSG_DUMP_PANIC)
return -EINVAL;
/* 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;
/* 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,
* and if our buffer is smaller than kmsg_bytes, we don't want the
* report split across multiple records.
*/
if (part != 1)
if (record->part != 1)
return -ENOSPC;
if (!cxt->dprzs)
@ -436,53 +439,50 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
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)
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;
return 0;
}
static int notrace ramoops_pstore_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)
static int notrace ramoops_pstore_write_user(struct pstore_record *record,
const char __user *buf)
{
if (type == PSTORE_TYPE_PMSG) {
struct ramoops_context *cxt = psi->data;
if (record->type == PSTORE_TYPE_PMSG) {
struct ramoops_context *cxt = record->psi->data;
if (!cxt->mprz)
return -ENOMEM;
return persistent_ram_write_user(cxt->mprz, buf, size);
return persistent_ram_write_user(cxt->mprz, buf, record->size);
}
return -EINVAL;
}
static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
struct timespec time, struct pstore_info *psi)
static int ramoops_pstore_erase(struct pstore_record *record)
{
struct ramoops_context *cxt = psi->data;
struct ramoops_context *cxt = record->psi->data;
struct persistent_ram_zone *prz;
switch (type) {
switch (record->type) {
case PSTORE_TYPE_DMESG:
if (id >= cxt->max_dump_cnt)
if (record->id >= cxt->max_dump_cnt)
return -EINVAL;
prz = cxt->dprzs[id];
prz = cxt->dprzs[record->id];
break;
case PSTORE_TYPE_CONSOLE:
prz = cxt->cprz;
break;
case PSTORE_TYPE_FTRACE:
if (id >= cxt->max_ftrace_cnt)
if (record->id >= cxt->max_ftrace_cnt)
return -EINVAL;
prz = cxt->fprzs[id];
prz = cxt->fprzs[record->id];
break;
case PSTORE_TYPE_PMSG:
prz = cxt->mprz;
@ -503,8 +503,8 @@ static struct ramoops_context oops_cxt = {
.name = "ramoops",
.open = ramoops_pstore_open,
.read = ramoops_pstore_read,
.write_buf = ramoops_pstore_write_buf,
.write_buf_user = ramoops_pstore_write_buf_user,
.write = ramoops_pstore_write,
.write_user = ramoops_pstore_write_user,
.erase = ramoops_pstore_erase,
},
};

View File

@ -532,7 +532,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
}
/* Initialize general buffer state. */
prz->buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(buffer_lock);
raw_spin_lock_init(&prz->buffer_lock);
prz->flags = flags;
ret = persistent_ram_buffer_map(start, size, prz, memtype);

View File

@ -30,7 +30,9 @@
#include <linux/time.h>
#include <linux/types.h>
/* types */
struct module;
/* pstore record types (see fs/pstore/inode.c for filename templates) */
enum pstore_type_id {
PSTORE_TYPE_DMESG = 0,
PSTORE_TYPE_MCE = 1,
@ -45,42 +47,146 @@ enum pstore_type_id {
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 module *owner;
char *name;
spinlock_t buf_lock; /* serialize access to 'buf' */
spinlock_t buf_lock;
char *buf;
size_t bufsize;
struct mutex read_mutex; /* serialize open/read/close */
struct mutex read_mutex;
int flags;
void *data;
int (*open)(struct pstore_info *psi);
int (*close)(struct pstore_info *psi);
ssize_t (*read)(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, char **buf,
bool *compressed, ssize_t *ecc_notice_size,
struct pstore_info *psi);
int (*write)(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);
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;
ssize_t (*read)(struct pstore_record *record);
int (*write)(struct pstore_record *record);
int (*write_user)(struct pstore_record *record,
const char __user *buf);
int (*erase)(struct pstore_record *record);
};
/* Supported frontends */
#define PSTORE_FLAGS_DMESG (1 << 0)
#define PSTORE_FLAGS_FRAGILE PSTORE_FLAGS_DMESG
#define PSTORE_FLAGS_CONSOLE (1 << 1)
#define PSTORE_FLAGS_FTRACE (1 << 2)
#define PSTORE_FLAGS_PMSG (1 << 3)