Merge linux-3.10.67 into dev-kernel-3.10
[linux-3.10.git] / fs / pstore / ram.c
index 42d5911..cf5da2a 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
  * Copyright (C) 2011 Kees Cook <keescook@chromium.org>
+ * Copyright (C) 2014 NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -51,6 +52,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
 module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
 MODULE_PARM_DESC(ftrace_size, "size of ftrace log");
 
+static ulong ramoops_rtrace_size = MIN_MEM_SIZE;
+module_param_named(rtrace_size, ramoops_rtrace_size, ulong, 0400);
+MODULE_PARM_DESC(rtrace_size, "size of rtrace log");
+
 static ulong mem_address;
 module_param(mem_address, ulong, 0400);
 MODULE_PARM_DESC(mem_address,
@@ -82,19 +87,23 @@ struct ramoops_context {
        struct persistent_ram_zone **przs;
        struct persistent_ram_zone *cprz;
        struct persistent_ram_zone *fprz;
+       struct persistent_ram_zone *rprz;
        phys_addr_t phys_addr;
        unsigned long size;
        unsigned int memtype;
        size_t record_size;
        size_t console_size;
        size_t ftrace_size;
+       size_t rtrace_size;
        int dump_oops;
        struct persistent_ram_ecc_info ecc_info;
        unsigned int max_dump_cnt;
        unsigned int dump_write_cnt;
+       /* _read_cnt need clear on ramoops_pstore_open */
        unsigned int dump_read_cnt;
        unsigned int console_read_cnt;
        unsigned int ftrace_read_cnt;
+       unsigned int rtrace_read_cnt;
        struct pstore_info pstore;
 };
 
@@ -107,6 +116,8 @@ static int ramoops_pstore_open(struct pstore_info *psi)
 
        cxt->dump_read_cnt = 0;
        cxt->console_read_cnt = 0;
+       cxt->ftrace_read_cnt = 0;
+       cxt->rtrace_read_cnt = 0;
        return 0;
 }
 
@@ -123,13 +134,15 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
                return NULL;
 
        prz = przs[i];
+       if (!prz)
+               return NULL;
 
-       if (update) {
-               /* Update old/shadowed buffer. */
+       /* Update old/shadowed buffer. */
+       if (update)
                persistent_ram_save_old(prz);
-               if (!persistent_ram_old_size(prz))
-                       return NULL;
-       }
+
+       if (!persistent_ram_old_size(prz))
+               return NULL;
 
        *typep = type;
        *id = i;
@@ -156,22 +169,29 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
                prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
                                           1, id, type, PSTORE_TYPE_FTRACE, 0);
        if (!prz)
+               prz = ramoops_get_next_prz(&cxt->rprz, &cxt->rtrace_read_cnt,
+                                          1, id, type, PSTORE_TYPE_RTRACE, 0);
+       if (!prz)
                return 0;
 
-       /* TODO(kees): Bogus time for the moment. */
-       time->tv_sec = 0;
-       time->tv_nsec = 0;
-
        size = persistent_ram_old_size(prz);
 
        /* ECC correction notice */
        ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
 
+       if (!(size + ecc_notice_size))
+               return 0;
+
        *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL);
        if (*buf == NULL)
                return -ENOMEM;
 
        memcpy(*buf, persistent_ram_old(prz), size);
+       if (sscanf(*buf, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
+                       &time->tv_sec, &time->tv_nsec) != 2) {
+               time->tv_sec = 0;
+               time->tv_nsec = 0;
+       }
        persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
 
        return size + ecc_notice_size;
@@ -218,6 +238,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
                        return -ENOMEM;
                persistent_ram_write(cxt->fprz, buf, size);
                return 0;
+       } else if (type == PSTORE_TYPE_RTRACE) {
+               if (!cxt->rprz)
+                       return -ENOMEM;
+               persistent_ram_write(cxt->rprz, buf, size);
+               return 0;
        }
 
        if (type != PSTORE_TYPE_DMESG)
@@ -265,16 +290,22 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
 
        switch (type) {
        case PSTORE_TYPE_DMESG:
+               return -EINVAL;
+               /* disable erase for the moment
                if (id >= cxt->max_dump_cnt)
                        return -EINVAL;
                prz = cxt->przs[id];
                break;
+               */
        case PSTORE_TYPE_CONSOLE:
                prz = cxt->cprz;
                break;
        case PSTORE_TYPE_FTRACE:
                prz = cxt->fprz;
                break;
+       case PSTORE_TYPE_RTRACE:
+               prz = cxt->rprz;
+               break;
        default:
                return -EINVAL;
        }
@@ -296,6 +327,23 @@ static struct ramoops_context oops_cxt = {
        },
 };
 
+static unsigned int
+ramoops_pstore_get_count(struct persistent_ram_zone *przs[],
+                                  enum pstore_type_id pstore_type,
+                                  unsigned int max)
+{
+       u64 id;
+       enum pstore_type_id type;
+       unsigned int count = 0;
+
+       while (ramoops_get_next_prz(przs, &count, max, &id, &type,
+                                   pstore_type, 0))
+               ;
+       if (count)
+               count--;
+       return count;
+}
+
 static void ramoops_free_przs(struct ramoops_context *cxt)
 {
        int i;
@@ -348,6 +396,10 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
                *paddr += sz;
        }
 
+       cxt->dump_write_cnt = ramoops_pstore_get_count(cxt->przs,
+                                       PSTORE_TYPE_DMESG, cxt->max_dump_cnt);
+       cxt->dump_write_cnt %= cxt->max_dump_cnt;
+
        return 0;
 fail_prz:
        ramoops_free_przs(cxt);
@@ -384,6 +436,12 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt,
        return 0;
 }
 
+void notrace ramoops_console_write_buf(const char *buf, size_t size)
+{
+       struct ramoops_context *cxt = &oops_cxt;
+       persistent_ram_write(cxt->cprz, buf, size);
+}
+
 static int ramoops_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -398,54 +456,79 @@ static int ramoops_probe(struct platform_device *pdev)
         */
        if (cxt->max_dump_cnt)
                goto fail_out;
-
-       if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
-                       !pdata->ftrace_size)) {
-               pr_err("The memory size and the record/console size must be "
-                       "non-zero\n");
+       if ((pdata->mem_size && pdata->record_size) == 0) {
+               pr_err("The memory size and the record size must be non-zero\n");
                goto fail_out;
        }
-
-       if (!is_power_of_2(pdata->mem_size))
+#ifdef CONFIG_PSTORE_CONSOLE
+       if (pdata->console_size == 0) {
+               pr_err("PSTORE_CONSOLE is enabled, console size must be non-zero\n");
+               goto fail_out;
+       }
+#endif
+#ifdef CONFIG_PSTORE_FTRACE
+       if (pdata->ftrace_size == 0) {
+               pr_err("PSTORE_FTRACE is enabled, ftrace size must be non-zero\n");
+               goto fail_out;
+       }
+#endif
+#ifdef CONFIG_PSTORE_RTRACE
+       if (pdata->rtrace_size == 0) {
+               pr_err("PSTORE_RTRACE is enabled, rtrace size must be non-zero\n");
+               goto fail_out;
+       }
+#endif
+       if (pdata->mem_size && !is_power_of_2(pdata->mem_size))
                pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
-       if (!is_power_of_2(pdata->record_size))
+       if (pdata->record_size && !is_power_of_2(pdata->record_size))
                pdata->record_size = rounddown_pow_of_two(pdata->record_size);
-       if (!is_power_of_2(pdata->console_size))
+       if (pdata->console_size && !is_power_of_2(pdata->console_size))
                pdata->console_size = rounddown_pow_of_two(pdata->console_size);
-       if (!is_power_of_2(pdata->ftrace_size))
+       if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
                pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
+       if (pdata->rtrace_size && !is_power_of_2(pdata->rtrace_size))
+               pdata->rtrace_size = rounddown_pow_of_two(pdata->rtrace_size);
 
-       cxt->dump_read_cnt = 0;
        cxt->size = pdata->mem_size;
        cxt->phys_addr = pdata->mem_address;
        cxt->memtype = pdata->mem_type;
        cxt->record_size = pdata->record_size;
        cxt->console_size = pdata->console_size;
        cxt->ftrace_size = pdata->ftrace_size;
+       cxt->rtrace_size = pdata->rtrace_size;
        cxt->dump_oops = pdata->dump_oops;
        cxt->ecc_info = pdata->ecc_info;
 
        paddr = cxt->phys_addr;
 
-       dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size;
+       dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size -
+                       cxt->rtrace_size;
        err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
        if (err)
                goto fail_out;
 
+#ifdef CONFIG_PSTORE_CONSOLE
        err = ramoops_init_prz(dev, cxt, &cxt->cprz, &paddr,
                               cxt->console_size, 0);
        if (err)
                goto fail_init_cprz;
-
+#endif
+#ifdef CONFIG_PSTORE_FTRACE
        err = ramoops_init_prz(dev, cxt, &cxt->fprz, &paddr, cxt->ftrace_size,
                               LINUX_VERSION_CODE);
        if (err)
                goto fail_init_fprz;
-
-       if (!cxt->przs && !cxt->cprz && !cxt->fprz) {
+#endif
+#ifdef CONFIG_PSTORE_RTRACE
+       err = ramoops_init_prz(dev, cxt, &cxt->rprz, &paddr,
+                              cxt->rtrace_size, 0);
+       if (err)
+               goto fail_init_rprz;
+#endif
+       if (!cxt->przs && !cxt->cprz && !cxt->fprz && !cxt->rprz) {
                pr_err("memory size too small, minimum is %zu\n",
                        cxt->console_size + cxt->record_size +
-                       cxt->ftrace_size);
+                       cxt->ftrace_size + cxt->rtrace_size);
                err = -EINVAL;
                goto fail_cnt;
        }
@@ -495,6 +578,8 @@ fail_clear:
        cxt->pstore.bufsize = 0;
        cxt->max_dump_cnt = 0;
 fail_cnt:
+       kfree(cxt->rprz);
+fail_init_rprz:
        kfree(cxt->fprz);
 fail_init_fprz:
        kfree(cxt->cprz);
@@ -553,6 +638,7 @@ static void ramoops_register_dummy(void)
        dummy_data->record_size = record_size;
        dummy_data->console_size = ramoops_console_size;
        dummy_data->ftrace_size = ramoops_ftrace_size;
+       dummy_data->rtrace_size = ramoops_rtrace_size;
        dummy_data->dump_oops = dump_oops;
        /*
         * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC