/* If change this size, then change the size of NVNAME_LEN */ struct nvram_header { unsignedchar signature; unsignedchar checksum; unsignedshort length; /* Terminating null required only for names < 12 chars. */ char name[12];
};
/* * For capturing and compressing an oops or panic report...
* big_oops_buf[] holds the uncompressed text we're capturing. * * oops_buf[] holds the compressed text, preceded by a oops header. * oops header has u16 holding the version of oops header (to differentiate * between old and new format header) followed by u16 holding the length of * the compressed* text (*Or uncompressed, if compression fails.) and u64 * holding the timestamp. oops_buf[] gets written to NVRAM. * * oops_log_info points to the header. oops_data points to the compressed text. * * +- oops_buf * | +- oops_data * v v * +-----------+-----------+-----------+------------------------+ * | version | length | timestamp | text | * | (2 bytes) | (2 bytes) | (8 bytes) | (oops_data_sz bytes) | * +-----------+-----------+-----------+------------------------+ * ^ * +- oops_log_info * * We preallocate these buffers during init to avoid kmalloc during oops/panic.
*/ static size_t big_oops_buf_sz; staticchar *big_oops_buf, *oops_buf; staticchar *oops_data; static size_t oops_data_sz;
/* nvram_write_os_partition * * We need to buffer the error logs into nvram to ensure that we have * the failure information to decode. If we have a severe error there * is no way to guarantee that the OS or the machine is in a state to * get back to user land and write the error to disk. For example if * the SCSI device driver causes a Machine Check by writing to a bad * IO address, there is no way of guaranteeing that the device driver * is in any state that is would also be able to write the error data * captured to disk, thus we buffer it in NVRAM for analysis on the * next boot. * * In NVRAM the partition containing the error log buffer will looks like: * Header (in bytes): * +-----------+----------+--------+------------+------------------+ * | signature | checksum | length | name | data | * |0 |1 |2 3|4 15|16 length-1| * +-----------+----------+--------+------------+------------------+ * * The 'data' section would look like (in bytes): * +--------------+------------+-----------------------------------+ * | event_logged | sequence # | error log | * |0 3|4 7|8 error_log_size-1| * +--------------+------------+-----------------------------------+ * * event_logged: 0 if event has not been logged to syslog, 1 if it has * sequence #: The unique sequence # for each event. (until it wraps) * error log: The error log from event_scan
*/ int nvram_write_os_partition(struct nvram_os_partition *part, char *buff, int length, unsignedint err_type, unsignedint error_log_cnt)
{ int rc;
loff_t tmp_index; struct err_log_info info;
if (part->os_partition) {
*error_log_cnt = be32_to_cpu(info.seq_num);
*err_type = be32_to_cpu(info.error_type);
}
return 0;
}
/* nvram_init_os_partition * * This sets up a partition with an "OS" signature. * * The general strategy is the following: * 1.) If a partition with the indicated name already exists... * - If it's large enough, use it. * - Otherwise, recycle it and keep going. * 2.) Search for a free partition that is large enough. * 3.) If there's not a free partition large enough, recycle any obsolete * OS partitions and try again. * 4.) Will first try getting a chunk that will satisfy the requested size. * 5.) If a chunk of the requested size cannot be allocated, then try finding * a chunk that will satisfy the minum needed. * * Returns 0 on success, else -1.
*/ int __init nvram_init_os_partition(struct nvram_os_partition *part)
{
loff_t p; int size;
/* Look for ours */
p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
/* Found one but too small, remove it */ if (p && size < part->min_size) {
pr_info("nvram: Found too small %s partition," " removing it...\n", part->name);
nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
p = 0;
}
/* Create one if we didn't find */ if (!p) {
p = nvram_create_partition(part->name, NVRAM_SIG_OS,
part->req_size, part->min_size); if (p == -ENOSPC) {
pr_info("nvram: No room to create %s partition, " "deleting any obsolete OS partitions...\n",
part->name);
nvram_remove_partition(NULL, NVRAM_SIG_OS,
nvram_os_partitions);
p = nvram_create_partition(part->name, NVRAM_SIG_OS,
part->req_size, part->min_size);
}
}
if (p <= 0) {
pr_err("nvram: Failed to find or create %s" " partition, err %d\n", part->name, (int)p); return -1;
}
err = zlib_deflate(&stream, Z_FINISH); if (err != Z_STREAM_END) goto error;
err = zlib_deflateEnd(&stream); if (err != Z_OK) goto error;
if (stream.total_out >= stream.total_in) goto error;
ret = stream.total_out;
error: return ret;
}
/* Compress the text from big_oops_buf into oops_buf. */ staticint zip_oops(size_t text_len)
{ struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len,
oops_data_sz); if (zipped_len < 0) {
pr_err("nvram: compression failed; returned %d\n", zipped_len);
pr_err("nvram: logging uncompressed oops/panic report\n"); return -1;
}
oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION);
oops_hdr->report_length = cpu_to_be16(zipped_len);
oops_hdr->timestamp = cpu_to_be64(ktime_get_real_seconds()); return 0;
}
#ifdef CONFIG_PSTORE staticint nvram_pstore_open(struct pstore_info *psi)
{ /* Reset the iterator to start reading partitions again */
read_type = -1; return 0;
}
/** * nvram_pstore_write - pstore write callback for nvram * @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.
*/ staticint nvram_pstore_write(struct pstore_record *record)
{ int rc; unsignedint 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 (record->part > 1 || (record->type != PSTORE_TYPE_DMESG)) return -1;
/* * Reads the oops/panic report, rtas, of-config and common partition. * 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(struct pstore_record *record)
{ struct oops_log_info *oops_hdr; unsignedint err_type, id_no, size = 0; struct nvram_os_partition *part = NULL; char *buff = NULL; int sig = 0;
loff_t p;
read_type++;
switch (nvram_type_ids[read_type]) { case PSTORE_TYPE_DMESG:
part = &oops_log_partition;
record->type = PSTORE_TYPE_DMESG; break; case PSTORE_TYPE_PPC_COMMON:
sig = NVRAM_SIG_SYS;
part = &common_partition;
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;
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;
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;
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: return 0;
}
if (!part->os_partition) {
p = nvram_find_partition(part->name, sig, &size); if (p <= 0) {
pr_err("nvram: Failed to find partition %s, " "err %d\n", part->name, (int)p); return 0;
}
part->index = p;
part->size = size;
}
void __init nvram_init_oops_partition(int rtas_partition_exists)
{ int rc;
rc = nvram_init_os_partition(&oops_log_partition); if (rc != 0) { #ifdef CONFIG_PPC_PSERIES if (!rtas_partition_exists) {
pr_err("nvram: Failed to initialize oops partition!"); return;
}
pr_notice("nvram: Using %s partition to log both" " RTAS errors and oops/panic reports\n",
rtas_log_partition.name);
memcpy(&oops_log_partition, &rtas_log_partition, sizeof(rtas_log_partition)); #else
pr_err("nvram: Failed to initialize oops partition!"); return; #endif
}
oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); if (!oops_buf) {
pr_err("nvram: No memory for %s partition\n",
oops_log_partition.name); return;
}
oops_data = oops_buf + sizeof(struct oops_log_info);
oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info);
rc = nvram_pstore_init();
if (!rc) return;
/* * Figure compression (preceded by elimination of each line's <n> * severity prefix) will reduce the oops/panic report to at most * 45% of its original size.
*/
big_oops_buf_sz = (oops_data_sz * 100) / 45;
big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); if (big_oops_buf) {
stream.workspace = kmalloc(zlib_deflate_workspacesize(
WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); if (!stream.workspace) {
pr_err("nvram: No memory for compression workspace; " "skipping compression of %s partition data\n",
oops_log_partition.name);
kfree(big_oops_buf);
big_oops_buf = NULL;
}
} else {
pr_err("No memory for uncompressed %s data; " "skipping compression\n", oops_log_partition.name);
stream.workspace = NULL;
}
rc = kmsg_dump_register(&nvram_kmsg_dumper); if (rc != 0) {
pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
kfree(oops_buf);
kfree(big_oops_buf);
kfree(stream.workspace);
}
}
/* * This is our kmsg_dump callback, called after an oops or panic report * has been written to the printk buffer. We want to capture as much * of the printk buffer as possible. First, capture as much as we can * that we think will compress sufficiently to fit in the lnx,oops-log * partition. If that's too much, go back and capture uncompressed text.
*/ staticvoid oops_to_nvram(struct kmsg_dumper *dumper, struct kmsg_dump_detail *detail)
{ struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; staticunsignedint oops_count = 0; staticstruct kmsg_dump_iter iter; staticbool panicking = false; static DEFINE_SPINLOCK(lock); unsignedlong flags;
size_t text_len; unsignedint err_type = ERR_TYPE_KERNEL_PANIC_GZ; int rc = -1;
switch (detail->reason) { case KMSG_DUMP_SHUTDOWN: /* These are almost always orderly shutdowns. */ return; case KMSG_DUMP_OOPS: break; case KMSG_DUMP_PANIC:
panicking = true; break; case KMSG_DUMP_EMERG: if (panicking) /* Panic report already captured. */ return; break; default:
pr_err("%s: ignoring unrecognized KMSG_DUMP_* reason %d\n",
__func__, (int) detail->reason); return;
}
/* The sum may have spilled into the 3rd byte. Fold it back. */
c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff; /* The sum cannot exceed 2 bytes. Fold it into a checksum */
c_sum2 = (c_sum >> 8) + (c_sum << 8);
c_sum = ((c_sum + c_sum2) >> 8) & 0xff; return c_sum;
}
/* * Per the criteria passed via nvram_remove_partition(), should this * partition be removed? 1=remove, 0=keep
*/ staticint __init nvram_can_remove_partition(struct nvram_partition *part, constchar *name, int sig, constchar *exceptions[])
{ if (part->header.signature != sig) return 0; if (name) { if (strncmp(name, part->header.name, 12)) return 0;
} elseif (exceptions) { constchar **except; for (except = exceptions; *except; except++) { if (!strncmp(*except, part->header.name, 12)) return 0;
}
} return 1;
}
/** * nvram_remove_partition - Remove one or more partitions in nvram * @name: name of the partition to remove, or NULL for a * signature only match * @sig: signature of the partition(s) to remove * @exceptions: When removing all partitions with a matching signature, * leave these alone.
*/
int __init nvram_remove_partition(constchar *name, int sig, constchar *exceptions[])
{ struct nvram_partition *part, *prev, *tmp; int rc;
list_for_each_entry(part, &nvram_partitions, partition) { if (!nvram_can_remove_partition(part, name, sig, exceptions)) continue;
/** * nvram_create_partition - Create a partition in nvram * @name: name of the partition to create * @sig: signature of the partition to create * @req_size: size of data to allocate in bytes * @min_size: minimum acceptable size (0 means req_size) * * Returns a negative error code or a positive nvram index * of the beginning of the data area of the newly created * partition. If you provided a min_size smaller than req_size * you need to query for the actual size yourself after the * call using nvram_partition_get_size().
*/
loff_t __init nvram_create_partition(constchar *name, int sig, int req_size, int min_size)
{ struct nvram_partition *part; struct nvram_partition *new_part; struct nvram_partition *free_part = NULL; staticchar nv_init_vals[16];
loff_t tmp_index; long size = 0; int rc;
BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
/* Convert sizes from bytes to blocks */
req_size = ALIGN(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
min_size = ALIGN(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
/* If no minimum size specified, make it the same as the * requested size
*/ if (min_size == 0)
min_size = req_size; if (min_size > req_size) return -EINVAL;
/* Now add one block to each for the header */
req_size += 1;
min_size += 1;
/* Find a free partition that will give us the maximum needed size
If can't find one that will give us the minimum size needed */
list_for_each_entry(part, &nvram_partitions, partition) { if (part->header.signature != NVRAM_SIG_FREE) continue;
/** * nvram_get_partition_size - Get the data size of an nvram partition * @data_index: This is the offset of the start of the data of * the partition. The same value that is returned by * nvram_create_partition().
*/ int nvram_get_partition_size(loff_t data_index)
{ struct nvram_partition *part;
/** * nvram_find_partition - Find an nvram partition by signature and name * @name: Name of the partition or NULL for any name * @sig: Signature to test against * @out_size: if non-NULL, returns the size of the data part of the partition
*/
loff_t nvram_find_partition(constchar *name, int sig, int *out_size)
{ struct nvram_partition *p;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.