|
@@ -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);
|