cpu-hotplug: CPUx should be active before it is marked online
[linux-2.6.git] / kernel / kexec.c
index 4838995..296fbc8 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/hardirq.h>
 #include <linux/elf.h>
 #include <linux/elfcore.h>
-#include <linux/utsrelease.h>
+#include <generated/utsrelease.h>
 #include <linux/utsname.h>
 #include <linux/numa.h>
 #include <linux/suspend.h>
@@ -31,6 +31,9 @@
 #include <linux/cpu.h>
 #include <linux/console.h>
 #include <linux/vmalloc.h>
+#include <linux/swap.h>
+#include <linux/kmsg_dump.h>
+#include <linux/syscore_ops.h>
 
 #include <asm/page.h>
 #include <asm/uaccess.h>
 #include <asm/sections.h>
 
 /* Per cpu memory for storing cpu states in case of system crash. */
-note_buf_t* crash_notes;
+note_buf_t __percpu *crash_notes;
 
 /* vmcoreinfo stuff */
-unsigned char vmcoreinfo_data[VMCOREINFO_BYTES];
+static unsigned char vmcoreinfo_data[VMCOREINFO_BYTES];
 u32 vmcoreinfo_note[VMCOREINFO_NOTE_SIZE/4];
 size_t vmcoreinfo_size;
 size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data);
@@ -142,15 +145,17 @@ static int do_kimage_alloc(struct kimage **rimage, unsigned long entry,
        /* Initialize the list of destination pages */
        INIT_LIST_HEAD(&image->dest_pages);
 
-       /* Initialize the list of unuseable pages */
+       /* Initialize the list of unusable pages */
        INIT_LIST_HEAD(&image->unuseable_pages);
 
        /* Read in the segments */
        image->nr_segments = nr_segments;
        segment_bytes = nr_segments * sizeof(*segments);
        result = copy_from_user(image->segment, segments, segment_bytes);
-       if (result)
+       if (result) {
+               result = -EFAULT;
                goto out;
+       }
 
        /*
         * Verify we have good destination addresses.  The caller is
@@ -159,7 +164,7 @@ static int do_kimage_alloc(struct kimage **rimage, unsigned long entry,
         * just verifies it is an address we can use.
         *
         * Since the kernel does everything in page size chunks ensure
-        * the destination addreses are page aligned.  Too many
+        * the destination addresses are page aligned.  Too many
         * special cases crop of when we don't do this.  The most
         * insidious is getting overlapping destination addresses
         * simply because addresses are changed to page size
@@ -450,7 +455,7 @@ static struct page *kimage_alloc_normal_control_pages(struct kimage *image,
        /* Deal with the destination pages I have inadvertently allocated.
         *
         * Ideally I would convert multi-page allocations into single
-        * page allocations, and add everyting to image->dest_pages.
+        * page allocations, and add everything to image->dest_pages.
         *
         * For now it is simpler to just free the pages.
         */
@@ -598,7 +603,7 @@ static void kimage_free_extra_pages(struct kimage *image)
        /* Walk through and free any extra destination pages I may have */
        kimage_free_page_list(&image->dest_pages);
 
-       /* Walk through and free any unuseable pages I have cached */
+       /* Walk through and free any unusable pages I have cached */
        kimage_free_page_list(&image->unuseable_pages);
 
 }
@@ -812,7 +817,7 @@ static int kimage_load_normal_segment(struct kimage *image,
 
                ptr = kmap(page);
                /* Start with a clear page */
-               memset(ptr, 0, PAGE_SIZE);
+               clear_page(ptr);
                ptr += maddr & ~PAGE_MASK;
                mchunk = PAGE_SIZE - (maddr & ~PAGE_MASK);
                if (mchunk > mbytes)
@@ -825,7 +830,7 @@ static int kimage_load_normal_segment(struct kimage *image,
                result = copy_from_user(ptr, buf, uchunk);
                kunmap(page);
                if (result) {
-                       result = (result < 0) ? result : -EIO;
+                       result = -EFAULT;
                        goto out;
                }
                ubytes -= uchunk;
@@ -880,7 +885,7 @@ static int kimage_load_crash_segment(struct kimage *image,
                kexec_flush_icache_page(page);
                kunmap(page);
                if (result) {
-                       result = (result < 0) ? result : -EIO;
+                       result = -EFAULT;
                        goto out;
                }
                ubytes -= uchunk;
@@ -1073,6 +1078,9 @@ void crash_kexec(struct pt_regs *regs)
        if (mutex_trylock(&kexec_mutex)) {
                if (kexec_crash_image) {
                        struct pt_regs fixed_regs;
+
+                       kmsg_dump(KMSG_DUMP_KEXEC);
+
                        crash_setup_regs(&fixed_regs, regs);
                        crash_save_vmcoreinfo();
                        machine_crash_shutdown(&fixed_regs);
@@ -1082,6 +1090,64 @@ void crash_kexec(struct pt_regs *regs)
        }
 }
 
+size_t crash_get_memory_size(void)
+{
+       size_t size = 0;
+       mutex_lock(&kexec_mutex);
+       if (crashk_res.end != crashk_res.start)
+               size = resource_size(&crashk_res);
+       mutex_unlock(&kexec_mutex);
+       return size;
+}
+
+void __weak crash_free_reserved_phys_range(unsigned long begin,
+                                          unsigned long end)
+{
+       unsigned long addr;
+
+       for (addr = begin; addr < end; addr += PAGE_SIZE) {
+               ClearPageReserved(pfn_to_page(addr >> PAGE_SHIFT));
+               init_page_count(pfn_to_page(addr >> PAGE_SHIFT));
+               free_page((unsigned long)__va(addr));
+               totalram_pages++;
+       }
+}
+
+int crash_shrink_memory(unsigned long new_size)
+{
+       int ret = 0;
+       unsigned long start, end;
+
+       mutex_lock(&kexec_mutex);
+
+       if (kexec_crash_image) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+       start = crashk_res.start;
+       end = crashk_res.end;
+
+       if (new_size >= end - start + 1) {
+               ret = -EINVAL;
+               if (new_size == end - start + 1)
+                       ret = 0;
+               goto unlock;
+       }
+
+       start = roundup(start, PAGE_SIZE);
+       end = roundup(start + new_size, PAGE_SIZE);
+
+       crash_free_reserved_phys_range(end, crashk_res.end);
+
+       if ((start == end) && (crashk_res.parent != NULL))
+               release_resource(&crashk_res);
+       crashk_res.end = end - 1;
+
+unlock:
+       mutex_unlock(&kexec_mutex);
+       return ret;
+}
+
 static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data,
                            size_t data_len)
 {
@@ -1130,7 +1196,7 @@ void crash_save_cpu(struct pt_regs *regs, int cpu)
                return;
        memset(&prstatus, 0, sizeof(prstatus));
        prstatus.pr_pid = current->pid;
-       elf_core_copy_regs(&prstatus.pr_reg, regs);
+       elf_core_copy_kernel_regs(&prstatus.pr_reg, regs);
        buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS,
                              &prstatus, sizeof(prstatus));
        final_note(buf);
@@ -1228,7 +1294,7 @@ static int __init parse_crashkernel_mem(char                      *cmdline,
        } while (*cur++ == ',');
 
        if (*crash_size > 0) {
-               while (*cur != ' ' && *cur != '@')
+               while (*cur && *cur != ' ' && *cur != '@')
                        cur++;
                if (*cur == '@') {
                        cur++;
@@ -1409,6 +1475,7 @@ static int __init crash_save_vmcoreinfo_init(void)
        VMCOREINFO_OFFSET(list_head, prev);
        VMCOREINFO_OFFSET(vm_struct, addr);
        VMCOREINFO_LENGTH(zone.free_area, MAX_ORDER);
+       log_buf_kexec_setup();
        VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
        VMCOREINFO_NUMBER(NR_FREE_PAGES);
        VMCOREINFO_NUMBER(PG_lru);
@@ -1447,29 +1514,26 @@ int kernel_kexec(void)
                        goto Restore_console;
                }
                suspend_console();
-               error = device_suspend(PMSG_FREEZE);
+               error = dpm_suspend_start(PMSG_FREEZE);
                if (error)
                        goto Resume_console;
-               error = disable_nonboot_cpus();
-               if (error)
-                       goto Resume_devices;
-               device_pm_lock();
-               local_irq_disable();
-               /* At this point, device_suspend() has been called,
-                * but *not* device_power_down(). We *must*
-                * device_power_down() now.  Otherwise, drivers for
+               /* At this point, dpm_suspend_start() has been called,
+                * but *not* dpm_suspend_noirq(). We *must* call
+                * dpm_suspend_noirq() now.  Otherwise, drivers for
                 * some devices (e.g. interrupt controllers) become
                 * desynchronized with the actual state of the
                 * hardware at resume time, and evil weirdness ensues.
                 */
-               error = device_power_down(PMSG_FREEZE);
+               error = dpm_suspend_noirq(PMSG_FREEZE);
                if (error)
-                       goto Enable_irqs;
-
-               /* Suspend system devices */
-               error = sysdev_suspend(PMSG_FREEZE);
+                       goto Resume_devices;
+               error = disable_nonboot_cpus();
+               if (error)
+                       goto Enable_cpus;
+               local_irq_disable();
+               error = syscore_suspend();
                if (error)
-                       goto Power_up_devices;
+                       goto Enable_irqs;
        } else
 #endif
        {
@@ -1482,15 +1546,14 @@ int kernel_kexec(void)
 
 #ifdef CONFIG_KEXEC_JUMP
        if (kexec_image->preserve_context) {
-               sysdev_resume();
- Power_up_devices:
-               device_power_up(PMSG_RESTORE);
+               syscore_resume();
  Enable_irqs:
                local_irq_enable();
-               device_pm_unlock();
+ Enable_cpus:
                enable_nonboot_cpus();
+               dpm_resume_noirq(PMSG_RESTORE);
  Resume_devices:
-               device_resume(PMSG_RESTORE);
+               dpm_resume_end(PMSG_RESTORE);
  Resume_console:
                resume_console();
                thaw_processes();