mmc: s3c6410: enable ADMA feature in 6410 sdhci controller
[safe/jmp/linux-2.6] / kernel / kexec.c
index 1c5fcac..474a847 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/kexec.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/list.h>
 #include <linux/highmem.h>
 #include <linux/syscalls.h>
 #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>
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/pm.h>
+#include <linux/cpu.h>
+#include <linux/console.h>
+#include <linux/vmalloc.h>
+#include <linux/swap.h>
+#include <linux/kmsg_dump.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_tcrash_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);
@@ -71,7 +80,7 @@ int kexec_should_crash(struct task_struct *p)
  *
  * The code for the transition from the current kernel to the
  * the new kernel is placed in the control_code_buffer, whose size
- * is given by KEXEC_CONTROL_CODE_SIZE.  In the best case only a single
+ * is given by KEXEC_CONTROL_PAGE_SIZE.  In the best case only a single
  * page of memory is necessary, but some architectures require more.
  * Because this memory must be identity mapped in the transition from
  * virtual to physical addresses it must live in the range
@@ -236,12 +245,18 @@ static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry,
         */
        result = -ENOMEM;
        image->control_code_page = kimage_alloc_control_pages(image,
-                                          get_order(KEXEC_CONTROL_CODE_SIZE));
+                                          get_order(KEXEC_CONTROL_PAGE_SIZE));
        if (!image->control_code_page) {
                printk(KERN_ERR "Could not allocate control_code_buffer\n");
                goto out;
        }
 
+       image->swap_page = kimage_alloc_control_pages(image, 0);
+       if (!image->swap_page) {
+               printk(KERN_ERR "Could not allocate swap buffer\n");
+               goto out;
+       }
+
        result = 0;
  out:
        if (result == 0)
@@ -305,7 +320,7 @@ static int kimage_crash_alloc(struct kimage **rimage, unsigned long entry,
         */
        result = -ENOMEM;
        image->control_code_page = kimage_alloc_control_pages(image,
-                                          get_order(KEXEC_CONTROL_CODE_SIZE));
+                                          get_order(KEXEC_CONTROL_PAGE_SIZE));
        if (!image->control_code_page) {
                printk(KERN_ERR "Could not allocate control_code_buffer\n");
                goto out;
@@ -589,14 +604,12 @@ static void kimage_free_extra_pages(struct kimage *image)
        kimage_free_page_list(&image->unuseable_pages);
 
 }
-static int kimage_terminate(struct kimage *image)
+static void kimage_terminate(struct kimage *image)
 {
        if (*image->entry != 0)
                image->entry++;
 
        *image->entry = IND_DONE;
-
-       return 0;
 }
 
 #define for_each_kimage_entry(image, ptr, entry) \
@@ -743,8 +756,14 @@ static struct page *kimage_alloc_page(struct kimage *image,
                        *old = addr | (*old & ~PAGE_MASK);
 
                        /* The old page I have found cannot be a
-                        * destination page, so return it.
+                        * destination page, so return it if it's
+                        * gfp_flags honor the ones passed in.
                         */
+                       if (!(gfp_mask & __GFP_HIGHMEM) &&
+                           PageHighMem(old_page)) {
+                               kimage_free_pages(old_page);
+                               continue;
+                       }
                        addr = old_addr;
                        page = old_page;
                        break;
@@ -914,19 +933,13 @@ static int kimage_load_segment(struct kimage *image,
  */
 struct kimage *kexec_image;
 struct kimage *kexec_crash_image;
-/*
- * A home grown binary mutex.
- * Nothing can wait so this mutex is safe to use
- * in interrupt context :)
- */
-static int kexec_lock;
 
-asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
-                               struct kexec_segment __user *segments,
-                               unsigned long flags)
+static DEFINE_MUTEX(kexec_mutex);
+
+SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
+               struct kexec_segment __user *, segments, unsigned long, flags)
 {
        struct kimage **dest_image, *image;
-       int locked;
        int result;
 
        /* We only trust the superuser with rebooting the system. */
@@ -962,8 +975,7 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
         *
         * KISS: always take the mutex.
         */
-       locked = xchg(&kexec_lock, 1);
-       if (locked)
+       if (!mutex_trylock(&kexec_mutex))
                return -EBUSY;
 
        dest_image = &kexec_image;
@@ -988,6 +1000,8 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
                if (result)
                        goto out;
 
+               if (flags & KEXEC_PRESERVE_CONTEXT)
+                       image->preserve_context = 1;
                result = machine_kexec_prepare(image);
                if (result)
                        goto out;
@@ -997,16 +1011,13 @@ asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments,
                        if (result)
                                goto out;
                }
-               result = kimage_terminate(image);
-               if (result)
-                       goto out;
+               kimage_terminate(image);
        }
        /* Install the new kernel, and  Uninstall the old */
        image = xchg(dest_image, image);
 
 out:
-       locked = xchg(&kexec_lock, 0); /* Release the mutex */
-       BUG_ON(!locked);
+       mutex_unlock(&kexec_mutex);
        kimage_free(image);
 
        return result;
@@ -1053,10 +1064,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry,
 
 void crash_kexec(struct pt_regs *regs)
 {
-       int locked;
-
-
-       /* Take the kexec_lock here to prevent sys_kexec_load
+       /* Take the kexec_mutex here to prevent sys_kexec_load
         * running on one cpu from replacing the crash kernel
         * we are using after a panic on a different cpu.
         *
@@ -1064,20 +1072,77 @@ void crash_kexec(struct pt_regs *regs)
         * of memory the xchg(&kexec_crash_image) would be
         * sufficient.  But since I reuse the memory...
         */
-       locked = xchg(&kexec_lock, 1);
-       if (!locked) {
+       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);
                        machine_kexec(kexec_crash_image);
                }
-               locked = xchg(&kexec_lock, 0);
-               BUG_ON(!locked);
+               mutex_unlock(&kexec_mutex);
        }
 }
 
+size_t crash_get_memory_size(void)
+{
+       size_t size;
+       mutex_lock(&kexec_mutex);
+       size = crashk_res.end - crashk_res.start + 1;
+       mutex_unlock(&kexec_mutex);
+       return size;
+}
+
+static void 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);
+
+       free_reserved_phys_range(end, crashk_res.end);
+
+       if (start == end)
+               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)
 {
@@ -1111,7 +1176,7 @@ void crash_save_cpu(struct pt_regs *regs, int cpu)
        struct elf_prstatus prstatus;
        u32 *buf;
 
-       if ((cpu < 0) || (cpu >= NR_CPUS))
+       if ((cpu < 0) || (cpu >= nr_cpu_ids))
                return;
 
        /* Using ELF notes here is opportunistic.
@@ -1126,7 +1191,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);
@@ -1224,7 +1289,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++;
@@ -1367,6 +1432,7 @@ static int __init crash_save_vmcoreinfo_init(void)
        VMCOREINFO_SYMBOL(node_online_map);
        VMCOREINFO_SYMBOL(swapper_pg_dir);
        VMCOREINFO_SYMBOL(_stext);
+       VMCOREINFO_SYMBOL(vmlist);
 
 #ifndef CONFIG_NEED_MULTIPLE_NODES
        VMCOREINFO_SYMBOL(mem_map);
@@ -1402,7 +1468,9 @@ static int __init crash_save_vmcoreinfo_init(void)
        VMCOREINFO_OFFSET(free_area, free_list);
        VMCOREINFO_OFFSET(list_head, next);
        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);
@@ -1415,3 +1483,83 @@ static int __init crash_save_vmcoreinfo_init(void)
 }
 
 module_init(crash_save_vmcoreinfo_init)
+
+/*
+ * Move into place and start executing a preloaded standalone
+ * executable.  If nothing was preloaded return an error.
+ */
+int kernel_kexec(void)
+{
+       int error = 0;
+
+       if (!mutex_trylock(&kexec_mutex))
+               return -EBUSY;
+       if (!kexec_image) {
+               error = -EINVAL;
+               goto Unlock;
+       }
+
+#ifdef CONFIG_KEXEC_JUMP
+       if (kexec_image->preserve_context) {
+               mutex_lock(&pm_mutex);
+               pm_prepare_console();
+               error = freeze_processes();
+               if (error) {
+                       error = -EBUSY;
+                       goto Restore_console;
+               }
+               suspend_console();
+               error = dpm_suspend_start(PMSG_FREEZE);
+               if (error)
+                       goto Resume_console;
+               /* 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 = dpm_suspend_noirq(PMSG_FREEZE);
+               if (error)
+                       goto Resume_devices;
+               error = disable_nonboot_cpus();
+               if (error)
+                       goto Enable_cpus;
+               local_irq_disable();
+               /* Suspend system devices */
+               error = sysdev_suspend(PMSG_FREEZE);
+               if (error)
+                       goto Enable_irqs;
+       } else
+#endif
+       {
+               kernel_restart_prepare(NULL);
+               printk(KERN_EMERG "Starting new kernel\n");
+               machine_shutdown();
+       }
+
+       machine_kexec(kexec_image);
+
+#ifdef CONFIG_KEXEC_JUMP
+       if (kexec_image->preserve_context) {
+               sysdev_resume();
+ Enable_irqs:
+               local_irq_enable();
+ Enable_cpus:
+               enable_nonboot_cpus();
+               dpm_resume_noirq(PMSG_RESTORE);
+ Resume_devices:
+               dpm_resume_end(PMSG_RESTORE);
+ Resume_console:
+               resume_console();
+               thaw_processes();
+ Restore_console:
+               pm_restore_console();
+               mutex_unlock(&pm_mutex);
+       }
+#endif
+
+ Unlock:
+       mutex_unlock(&kexec_mutex);
+       return error;
+}