]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/kernel/fadump.c
ath6kl: fix fw capability parsing
[~andy/linux] / arch / powerpc / kernel / fadump.c
index a83bc9015c64edca35f30444aa31d5bb4c19dcb5..cfe7a38708c3724d6fc88cc6b110d2917d85b3e9 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/crash_dump.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
 
 #include <asm/page.h>
 #include <asm/prom.h>
@@ -984,6 +986,132 @@ static int fadump_unregister_dump(struct fadump_mem_struct *fdm)
        return 0;
 }
 
+static int fadump_invalidate_dump(struct fadump_mem_struct *fdm)
+{
+       int rc = 0;
+       unsigned int wait_time;
+
+       pr_debug("Invalidating firmware-assisted dump registration\n");
+
+       /* TODO: Add upper time limit for the delay */
+       do {
+               rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL,
+                       FADUMP_INVALIDATE, fdm,
+                       sizeof(struct fadump_mem_struct));
+
+               wait_time = rtas_busy_delay_time(rc);
+               if (wait_time)
+                       mdelay(wait_time);
+       } while (wait_time);
+
+       if (rc) {
+               printk(KERN_ERR "Failed to invalidate firmware-assisted dump "
+                       "rgistration. unexpected error(%d).\n", rc);
+               return rc;
+       }
+       fw_dump.dump_active = 0;
+       fdm_active = NULL;
+       return 0;
+}
+
+void fadump_cleanup(void)
+{
+       /* Invalidate the registration only if dump is active. */
+       if (fw_dump.dump_active) {
+               init_fadump_mem_struct(&fdm,
+                       fdm_active->cpu_state_data.destination_address);
+               fadump_invalidate_dump(&fdm);
+       }
+}
+
+/*
+ * Release the memory that was reserved in early boot to preserve the memory
+ * contents. The released memory will be available for general use.
+ */
+static void fadump_release_memory(unsigned long begin, unsigned long end)
+{
+       unsigned long addr;
+       unsigned long ra_start, ra_end;
+
+       ra_start = fw_dump.reserve_dump_area_start;
+       ra_end = ra_start + fw_dump.reserve_dump_area_size;
+
+       for (addr = begin; addr < end; addr += PAGE_SIZE) {
+               /*
+                * exclude the dump reserve area. Will reuse it for next
+                * fadump registration.
+                */
+               if (addr <= ra_end && ((addr + PAGE_SIZE) > ra_start))
+                       continue;
+
+               ClearPageReserved(pfn_to_page(addr >> PAGE_SHIFT));
+               init_page_count(pfn_to_page(addr >> PAGE_SHIFT));
+               free_page((unsigned long)__va(addr));
+               totalram_pages++;
+       }
+}
+
+static void fadump_invalidate_release_mem(void)
+{
+       unsigned long reserved_area_start, reserved_area_end;
+       unsigned long destination_address;
+
+       mutex_lock(&fadump_mutex);
+       if (!fw_dump.dump_active) {
+               mutex_unlock(&fadump_mutex);
+               return;
+       }
+
+       destination_address = fdm_active->cpu_state_data.destination_address;
+       fadump_cleanup();
+       mutex_unlock(&fadump_mutex);
+
+       /*
+        * Save the current reserved memory bounds we will require them
+        * later for releasing the memory for general use.
+        */
+       reserved_area_start = fw_dump.reserve_dump_area_start;
+       reserved_area_end = reserved_area_start +
+                       fw_dump.reserve_dump_area_size;
+       /*
+        * Setup reserve_dump_area_start and its size so that we can
+        * reuse this reserved memory for Re-registration.
+        */
+       fw_dump.reserve_dump_area_start = destination_address;
+       fw_dump.reserve_dump_area_size = get_fadump_area_size();
+
+       fadump_release_memory(reserved_area_start, reserved_area_end);
+       if (fw_dump.cpu_notes_buf) {
+               fadump_cpu_notes_buf_free(
+                               (unsigned long)__va(fw_dump.cpu_notes_buf),
+                               fw_dump.cpu_notes_buf_size);
+               fw_dump.cpu_notes_buf = 0;
+               fw_dump.cpu_notes_buf_size = 0;
+       }
+       /* Initialize the kernel dump memory structure for FAD registration. */
+       init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);
+}
+
+static ssize_t fadump_release_memory_store(struct kobject *kobj,
+                                       struct kobj_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       if (!fw_dump.dump_active)
+               return -EPERM;
+
+       if (buf[0] == '1') {
+               /*
+                * Take away the '/proc/vmcore'. We are releasing the dump
+                * memory, hence it will not be valid anymore.
+                */
+               vmcore_cleanup();
+               fadump_invalidate_release_mem();
+
+       } else
+               return -EINVAL;
+       return count;
+}
+
 static ssize_t fadump_enabled_show(struct kobject *kobj,
                                        struct kobj_attribute *attr,
                                        char *buf)
@@ -1043,10 +1171,13 @@ static int fadump_region_show(struct seq_file *m, void *private)
        if (!fw_dump.fadump_enabled)
                return 0;
 
+       mutex_lock(&fadump_mutex);
        if (fdm_active)
                fdm_ptr = fdm_active;
-       else
+       else {
+               mutex_unlock(&fadump_mutex);
                fdm_ptr = &fdm;
+       }
 
        seq_printf(m,
                        "CPU : [%#016llx-%#016llx] %#llx bytes, "
@@ -1076,7 +1207,7 @@ static int fadump_region_show(struct seq_file *m, void *private)
        if (!fdm_active ||
                (fw_dump.reserve_dump_area_start ==
                fdm_ptr->cpu_state_data.destination_address))
-               return 0;
+               goto out;
 
        /* Dump is active. Show reserved memory region. */
        seq_printf(m,
@@ -1088,9 +1219,15 @@ static int fadump_region_show(struct seq_file *m, void *private)
                        fw_dump.reserve_dump_area_start,
                        fdm_ptr->cpu_state_data.destination_address -
                        fw_dump.reserve_dump_area_start);
+out:
+       if (fdm_active)
+               mutex_unlock(&fadump_mutex);
        return 0;
 }
 
+static struct kobj_attribute fadump_release_attr = __ATTR(fadump_release_mem,
+                                               0200, NULL,
+                                               fadump_release_memory_store);
 static struct kobj_attribute fadump_attr = __ATTR(fadump_enabled,
                                                0444, fadump_enabled_show,
                                                NULL);
@@ -1131,6 +1268,13 @@ static void fadump_init_files(void)
        if (!debugfs_file)
                printk(KERN_ERR "fadump: unable to create debugfs file"
                                " fadump_region\n");
+
+       if (fw_dump.dump_active) {
+               rc = sysfs_create_file(kernel_kobj, &fadump_release_attr.attr);
+               if (rc)
+                       printk(KERN_ERR "fadump: unable to create sysfs file"
+                               " fadump_release_mem (%d)\n", rc);
+       }
        return;
 }
 
@@ -1153,8 +1297,14 @@ int __init setup_fadump(void)
         * If dump data is available then see if it is valid and prepare for
         * saving it to the disk.
         */
-       if (fw_dump.dump_active)
-               process_fadump(fdm_active);
+       if (fw_dump.dump_active) {
+               /*
+                * if dump process fails then invalidate the registration
+                * and release memory before proceeding for re-registration.
+                */
+               if (process_fadump(fdm_active) < 0)
+                       fadump_invalidate_release_mem();
+       }
        /* Initialize the kernel dump memory structure for FAD registration. */
        else if (fw_dump.reserve_dump_area_size)
                init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);