Commit 0f4ac132 authored by Jim Keniston's avatar Jim Keniston Committed by Benjamin Herrenschmidt

powerpc/nvram: Generalize code for OS partitions in NVRAM

Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.
Signed-off-by: default avatarJim Keniston <jkenisto@us.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 6dd22700
...@@ -51,7 +51,8 @@ static inline int mmio_nvram_init(void) ...@@ -51,7 +51,8 @@ static inline int mmio_nvram_init(void)
extern int __init nvram_scan_partitions(void); extern int __init nvram_scan_partitions(void);
extern loff_t nvram_create_partition(const char *name, int sig, extern loff_t nvram_create_partition(const char *name, int sig,
int req_size, int min_size); int req_size, int min_size);
extern int nvram_remove_partition(const char *name, int sig); extern int nvram_remove_partition(const char *name, int sig,
const char *exceptions[]);
extern int nvram_get_partition_size(loff_t data_index); extern int nvram_get_partition_size(loff_t data_index);
extern loff_t nvram_find_partition(const char *name, int sig, int *out_size); extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
......
...@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p) ...@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
return c_sum; return c_sum;
} }
/*
* Per the criteria passed via nvram_remove_partition(), should this
* partition be removed? 1=remove, 0=keep
*/
static int nvram_can_remove_partition(struct nvram_partition *part,
const char *name, int sig, const char *exceptions[])
{
if (part->header.signature != sig)
return 0;
if (name) {
if (strncmp(name, part->header.name, 12))
return 0;
} else if (exceptions) {
const char **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 * nvram_remove_partition - Remove one or more partitions in nvram
* @name: name of the partition to remove, or NULL for a * @name: name of the partition to remove, or NULL for a
* signature only match * signature only match
* @sig: signature of the partition(s) to remove * @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(const char *name, int sig) int __init nvram_remove_partition(const char *name, int sig,
const char *exceptions[])
{ {
struct nvram_partition *part, *prev, *tmp; struct nvram_partition *part, *prev, *tmp;
int rc; int rc;
list_for_each_entry(part, &nvram_partitions, partition) { list_for_each_entry(part, &nvram_partitions, partition) {
if (part->header.signature != sig) if (!nvram_can_remove_partition(part, name, sig, exceptions))
continue;
if (name && strncmp(name, part->header.name, 12))
continue; continue;
/* Make partition a free partition */ /* Make partition a free partition */
......
...@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store; ...@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */
static DEFINE_SPINLOCK(nvram_lock); static DEFINE_SPINLOCK(nvram_lock);
static long nvram_error_log_index = -1;
static long nvram_error_log_size = 0;
struct err_log_info { struct err_log_info {
int error_type; int error_type;
unsigned int seq_num; unsigned int seq_num;
}; };
#define NVRAM_MAX_REQ 2079
#define NVRAM_MIN_REQ 1055
#define NVRAM_LOG_PART_NAME "ibm,rtas-log" struct nvram_os_partition {
const char *name;
int req_size; /* desired size, in bytes */
int min_size; /* minimum acceptable size (0 means req_size) */
long size; /* size of data portion of partition */
long index; /* offset of data portion of partition */
};
static struct nvram_os_partition rtas_log_partition = {
.name = "ibm,rtas-log",
.req_size = 2079,
.min_size = 1055,
.index = -1
};
static const char *pseries_nvram_os_partitions[] = {
"ibm,rtas-log",
NULL
};
static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
{ {
...@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void) ...@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
} }
/* nvram_write_error_log /* nvram_write_os_partition, nvram_write_error_log
* *
* We need to buffer the error logs into nvram to ensure that we have * 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 * the failure information to decode. If we have a severe error there
...@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void) ...@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
* The 'data' section would look like (in bytes): * The 'data' section would look like (in bytes):
* +--------------+------------+-----------------------------------+ * +--------------+------------+-----------------------------------+
* | event_logged | sequence # | error log | * | event_logged | sequence # | error log |
* |0 3|4 7|8 nvram_error_log_size-1| * |0 3|4 7|8 error_log_size-1|
* +--------------+------------+-----------------------------------+ * +--------------+------------+-----------------------------------+
* *
* event_logged: 0 if event has not been logged to syslog, 1 if it has * 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) * sequence #: The unique sequence # for each event. (until it wraps)
* error log: The error log from event_scan * error log: The error log from event_scan
*/ */
int nvram_write_error_log(char * buff, int length, int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
unsigned int err_type, unsigned int error_log_cnt) int length, unsigned int err_type, unsigned int error_log_cnt)
{ {
int rc; int rc;
loff_t tmp_index; loff_t tmp_index;
struct err_log_info info; struct err_log_info info;
if (nvram_error_log_index == -1) { if (part->index == -1) {
return -ESPIPE; return -ESPIPE;
} }
if (length > nvram_error_log_size) { if (length > part->size) {
length = nvram_error_log_size; length = part->size;
} }
info.error_type = err_type; info.error_type = err_type;
info.seq_num = error_log_cnt; info.seq_num = error_log_cnt;
tmp_index = nvram_error_log_index; tmp_index = part->index;
rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
if (rc <= 0) { if (rc <= 0) {
printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
return rc; return rc;
} }
rc = ppc_md.nvram_write(buff, length, &tmp_index); rc = ppc_md.nvram_write(buff, length, &tmp_index);
if (rc <= 0) { if (rc <= 0) {
printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
return rc; return rc;
} }
return 0; return 0;
} }
int nvram_write_error_log(char * buff, int length,
unsigned int err_type, unsigned int error_log_cnt)
{
return nvram_write_os_partition(&rtas_log_partition, buff, length,
err_type, error_log_cnt);
}
/* nvram_read_error_log /* nvram_read_error_log
* *
* Reads nvram for error log for at most 'length' * Reads nvram for error log for at most 'length'
...@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length, ...@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
loff_t tmp_index; loff_t tmp_index;
struct err_log_info info; struct err_log_info info;
if (nvram_error_log_index == -1) if (rtas_log_partition.index == -1)
return -1; return -1;
if (length > nvram_error_log_size) if (length > rtas_log_partition.size)
length = nvram_error_log_size; length = rtas_log_partition.size;
tmp_index = nvram_error_log_index; tmp_index = rtas_log_partition.index;
rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index); rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
if (rc <= 0) { if (rc <= 0) {
...@@ -244,10 +264,10 @@ int nvram_clear_error_log(void) ...@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
int clear_word = ERR_FLAG_ALREADY_LOGGED; int clear_word = ERR_FLAG_ALREADY_LOGGED;
int rc; int rc;
if (nvram_error_log_index == -1) if (rtas_log_partition.index == -1)
return -1; return -1;
tmp_index = nvram_error_log_index; tmp_index = rtas_log_partition.index;
rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
if (rc <= 0) { if (rc <= 0) {
...@@ -258,23 +278,25 @@ int nvram_clear_error_log(void) ...@@ -258,23 +278,25 @@ int nvram_clear_error_log(void)
return 0; return 0;
} }
/* pseries_nvram_init_log_partition /* pseries_nvram_init_os_partition
* *
* This will setup the partition we need for buffering the * This sets up a partition with an "OS" signature.
* error logs and cleanup partitions if needed.
* *
* The general strategy is the following: * The general strategy is the following:
* 1.) If there is log partition large enough then use it. * 1.) If a partition with the indicated name already exists...
* 2.) If there is none large enough, search * - If it's large enough, use it.
* for a free partition that is large enough. * - Otherwise, recycle it and keep going.
* 3.) If there is not a free partition large enough remove * 2.) Search for a free partition that is large enough.
* _all_ OS partitions and consolidate the space. * 3.) If there's not a free partition large enough, recycle any obsolete
* 4.) Will first try getting a chunk that will satisfy the maximum * OS partitions and try again.
* error log size (NVRAM_MAX_REQ). * 4.) Will first try getting a chunk that will satisfy the requested size.
* 5.) If the max chunk cannot be allocated then try finding a chunk * 5.) If a chunk of the requested size cannot be allocated, then try finding
* that will satisfy the minum needed (NVRAM_MIN_REQ). * a chunk that will satisfy the minum needed.
*
* Returns 0 on success, else -1.
*/ */
static int __init pseries_nvram_init_log_partition(void) static int __init pseries_nvram_init_os_partition(struct nvram_os_partition
*part)
{ {
loff_t p; loff_t p;
int size; int size;
...@@ -282,47 +304,50 @@ static int __init pseries_nvram_init_log_partition(void) ...@@ -282,47 +304,50 @@ static int __init pseries_nvram_init_log_partition(void)
/* Scan nvram for partitions */ /* Scan nvram for partitions */
nvram_scan_partitions(); nvram_scan_partitions();
/* Lookg for ours */ /* Look for ours */
p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size); p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
/* Found one but too small, remove it */ /* Found one but too small, remove it */
if (p && size < NVRAM_MIN_REQ) { if (p && size < part->min_size) {
pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition" pr_info("nvram: Found too small %s partition,"
",removing it..."); " removing it...\n", part->name);
nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS); nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
p = 0; p = 0;
} }
/* Create one if we didn't find */ /* Create one if we didn't find */
if (!p) { if (!p) {
p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, p = nvram_create_partition(part->name, NVRAM_SIG_OS,
NVRAM_MAX_REQ, NVRAM_MIN_REQ); part->req_size, part->min_size);
/* No room for it, try to get rid of any OS partition
* and try again
*/
if (p == -ENOSPC) { if (p == -ENOSPC) {
pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME pr_info("nvram: No room to create %s partition, "
" partition, deleting all OS partitions..."); "deleting any obsolete OS partitions...\n",
nvram_remove_partition(NULL, NVRAM_SIG_OS); part->name);
p = nvram_create_partition(NVRAM_LOG_PART_NAME, nvram_remove_partition(NULL, NVRAM_SIG_OS,
NVRAM_SIG_OS, NVRAM_MAX_REQ, pseries_nvram_os_partitions);
NVRAM_MIN_REQ); p = nvram_create_partition(part->name, NVRAM_SIG_OS,
part->req_size, part->min_size);
} }
} }
if (p <= 0) { if (p <= 0) {
pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME pr_err("nvram: Failed to find or create %s"
" partition, err %d\n", (int)p); " partition, err %d\n", part->name, (int)p);
return 0; return -1;
} }
nvram_error_log_index = p; part->index = p;
nvram_error_log_size = nvram_get_partition_size(p) - part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
sizeof(struct err_log_info);
return 0; return 0;
} }
machine_arch_initcall(pseries, pseries_nvram_init_log_partition);
static int __init pseries_nvram_init_log_partitions(void)
{
(void) pseries_nvram_init_os_partition(&rtas_log_partition);
return 0;
}
machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);
int __init pSeries_nvram_init(void) int __init pSeries_nvram_init(void)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment