powerpc/nvram: Generalize code for OS partitions in NVRAM
authorJim Keniston <jkenisto@linux.vnet.ibm.com>
Wed, 9 Feb 2011 12:43:13 +0000 (12:43 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 4 Mar 2011 07:19:04 +0000 (18:19 +1100)
Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/nvram.h
arch/powerpc/kernel/nvram_64.c
arch/powerpc/platforms/pseries/nvram.c

index 92efe67..9d1aafe 100644 (file)
@@ -51,7 +51,8 @@ static inline int mmio_nvram_init(void)
 extern int __init nvram_scan_partitions(void);
 extern loff_t nvram_create_partition(const char *name, int sig,
                                     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 loff_t nvram_find_partition(const char *name, int sig, int *out_size);
 
index bb12b32..bec1e93 100644 (file)
@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
        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
  * @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(const char *name, int sig)
+int __init nvram_remove_partition(const char *name, int sig,
+                                               const char *exceptions[])
 {
        struct nvram_partition *part, *prev, *tmp;
        int rc;
 
        list_for_each_entry(part, &nvram_partitions, partition) {
-               if (part->header.signature != sig)
-                       continue;
-               if (name && strncmp(name, part->header.name, 12))
+               if (!nvram_can_remove_partition(part, name, sig, exceptions))
                        continue;
 
                /* Make partition a free partition */
index 7e828ba..befb41b 100644 (file)
@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
 static char nvram_buf[NVRW_CNT];       /* assume this is in the first 4GB */
 static DEFINE_SPINLOCK(nvram_lock);
 
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
-
 struct err_log_info {
        int error_type;
        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)
 {
@@ -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
  * the failure information to decode.  If we have a severe error there
@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
  * The 'data' section would look like (in bytes):
  * +--------------+------------+-----------------------------------+
  * | 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
  * sequence #: The unique sequence # for each event. (until it wraps)
  * error log: The error log from event_scan
  */
-int nvram_write_error_log(char * buff, int length,
-                          unsigned int err_type, unsigned int error_log_cnt)
+int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
+               int length, unsigned int err_type, unsigned int error_log_cnt)
 {
        int rc;
        loff_t tmp_index;
        struct err_log_info info;
        
-       if (nvram_error_log_index == -1) {
+       if (part->index == -1) {
                return -ESPIPE;
        }
 
-       if (length > nvram_error_log_size) {
-               length = nvram_error_log_size;
+       if (length > part->size) {
+               length = part->size;
        }
 
        info.error_type = err_type;
        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);
        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;
        }
 
        rc = ppc_md.nvram_write(buff, length, &tmp_index);
        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 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
  *
  * Reads nvram for error log for at most 'length'
@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
        loff_t tmp_index;
        struct err_log_info info;
        
-       if (nvram_error_log_index == -1)
+       if (rtas_log_partition.index == -1)
                return -1;
 
-       if (length > nvram_error_log_size)
-               length = nvram_error_log_size;
+       if (length > rtas_log_partition.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);
        if (rc <= 0) {
@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
        int clear_word = ERR_FLAG_ALREADY_LOGGED;
        int rc;
 
-       if (nvram_error_log_index == -1)
+       if (rtas_log_partition.index == -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);
        if (rc <= 0) {
@@ -258,23 +278,25 @@ int nvram_clear_error_log(void)
        return 0;
 }
 
-/* pseries_nvram_init_log_partition
+/* pseries_nvram_init_os_partition
  *
- * This will setup the partition we need for buffering the
- * error logs and cleanup partitions if needed.
+ * This sets up a partition with an "OS" signature.
  *
  * The general strategy is the following:
- * 1.) If there is log partition large enough then use it.
- * 2.) If there is none large enough, search
- * for a free partition that is large enough.
- * 3.) If there is not a free partition large enough remove 
- * _all_ OS partitions and consolidate the space.
- * 4.) Will first try getting a chunk that will satisfy the maximum
- * error log size (NVRAM_MAX_REQ).
- * 5.) If the max chunk cannot be allocated then try finding a chunk
- * that will satisfy the minum needed (NVRAM_MIN_REQ).
+ * 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.
  */
-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;
        int size;
@@ -282,47 +304,50 @@ static int __init pseries_nvram_init_log_partition(void)
        /* Scan nvram for partitions */
        nvram_scan_partitions();
 
-       /* Lookg for ours */
-       p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
+       /* Look for ours */
+       p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
 
        /* Found one but too small, remove it */
-       if (p && size < NVRAM_MIN_REQ) {
-               pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
-                       ",removing it...");
-               nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
+       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(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
-                                          NVRAM_MAX_REQ, NVRAM_MIN_REQ);
-               /* No room for it, try to get rid of any OS partition
-                * and try again
-                */
+               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 "NVRAM_LOG_PART_NAME
-                               " partition, deleting all OS partitions...");
-                       nvram_remove_partition(NULL, NVRAM_SIG_OS);
-                       p = nvram_create_partition(NVRAM_LOG_PART_NAME,
-                                                  NVRAM_SIG_OS, NVRAM_MAX_REQ,
-                                                  NVRAM_MIN_REQ);
+                       pr_info("nvram: No room to create %s partition, "
+                               "deleting any obsolete OS partitions...\n",
+                               part->name);
+                       nvram_remove_partition(NULL, NVRAM_SIG_OS,
+                                               pseries_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 "NVRAM_LOG_PART_NAME
-                      " partition, err %d\n", (int)p);
-               return 0;
+               pr_err("nvram: Failed to find or create %s"
+                      " partition, err %d\n", part->name, (int)p);
+               return -1;
        }
 
-       nvram_error_log_index = p;
-       nvram_error_log_size = nvram_get_partition_size(p) -
-               sizeof(struct err_log_info);
+       part->index = p;
+       part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
        
        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)
 {