- 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:
commit
2575be8ad3
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
130
fs/pstore/ram.c
130
fs/pstore/ram.c
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue