diff options
Diffstat (limited to 'kernel/kexec.c')
| -rw-r--r-- | kernel/kexec.c | 111 | 
1 files changed, 63 insertions, 48 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c index 2a74f307c5e..4b8f0c92588 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -32,6 +32,8 @@  #include <linux/vmalloc.h>  #include <linux/swap.h>  #include <linux/syscore_ops.h> +#include <linux/compiler.h> +#include <linux/hugetlb.h>  #include <asm/page.h>  #include <asm/uaccess.h> @@ -47,6 +49,9 @@ u32 vmcoreinfo_note[VMCOREINFO_NOTE_SIZE/4];  size_t vmcoreinfo_size;  size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data); +/* Flag to indicate we are going to kexec a new kernel */ +bool kexec_in_progress = false; +  /* Location of the reserved area for the crash kernel */  struct resource crashk_res = {  	.name  = "Crash kernel", @@ -121,8 +126,8 @@ static struct page *kimage_alloc_page(struct kimage *image,  				       unsigned long dest);  static int do_kimage_alloc(struct kimage **rimage, unsigned long entry, -	                    unsigned long nr_segments, -                            struct kexec_segment __user *segments) +			   unsigned long nr_segments, +			   struct kexec_segment __user *segments)  {  	size_t segment_bytes;  	struct kimage *image; @@ -253,13 +258,13 @@ static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry,  	image->control_code_page = kimage_alloc_control_pages(image,  					   get_order(KEXEC_CONTROL_PAGE_SIZE));  	if (!image->control_code_page) { -		printk(KERN_ERR "Could not allocate control_code_buffer\n"); +		pr_err("Could not allocate control_code_buffer\n");  		goto out_free;  	}  	image->swap_page = kimage_alloc_control_pages(image, 0);  	if (!image->swap_page) { -		printk(KERN_ERR "Could not allocate swap buffer\n"); +		pr_err("Could not allocate swap buffer\n");  		goto out_free;  	} @@ -328,7 +333,7 @@ static int kimage_crash_alloc(struct kimage **rimage, unsigned long entry,  	image->control_code_page = kimage_alloc_control_pages(image,  					   get_order(KEXEC_CONTROL_PAGE_SIZE));  	if (!image->control_code_page) { -		printk(KERN_ERR "Could not allocate control_code_buffer\n"); +		pr_err("Could not allocate control_code_buffer\n");  		goto out_free;  	} @@ -617,8 +622,8 @@ static void kimage_terminate(struct kimage *image)  #define for_each_kimage_entry(image, ptr, entry) \  	for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ -		ptr = (entry & IND_INDIRECTION)? \ -			phys_to_virt((entry & PAGE_MASK)): ptr +1) +		ptr = (entry & IND_INDIRECTION) ? \ +			phys_to_virt((entry & PAGE_MASK)) : ptr + 1)  static void kimage_free_entry(kimage_entry_t entry)  { @@ -646,8 +651,7 @@ static void kimage_free(struct kimage *image)  			 * done with it.  			 */  			ind = entry; -		} -		else if (entry & IND_SOURCE) +		} else if (entry & IND_SOURCE)  			kimage_free_entry(entry);  	}  	/* Free the final indirection page */ @@ -770,8 +774,7 @@ static struct page *kimage_alloc_page(struct kimage *image,  			addr = old_addr;  			page = old_page;  			break; -		} -		else { +		} else {  			/* Place the page on the destination list I  			 * will use it later.  			 */ @@ -921,7 +924,7 @@ static int kimage_load_segment(struct kimage *image,   *   reinitialize them.   *   * - A machine specific part that includes the syscall number - *   and the copies the image to it's final destination.  And + *   and then copies the image to it's final destination.  And   *   jumps into the image at entry.   *   * kexec does not sync, or unmount filesystems so if you need @@ -929,6 +932,7 @@ static int kimage_load_segment(struct kimage *image,   */  struct kimage *kexec_image;  struct kimage *kexec_crash_image; +int kexec_load_disabled;  static DEFINE_MUTEX(kexec_mutex); @@ -939,7 +943,7 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,  	int result;  	/* We only trust the superuser with rebooting the system. */ -	if (!capable(CAP_SYS_BOOT)) +	if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)  		return -EPERM;  	/* @@ -1035,10 +1039,10 @@ void __weak crash_unmap_reserved_pages(void)  {}  #ifdef CONFIG_COMPAT -asmlinkage long compat_sys_kexec_load(unsigned long entry, -				unsigned long nr_segments, -				struct compat_kexec_segment __user *segments, -				unsigned long flags) +COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry, +		       compat_ulong_t, nr_segments, +		       struct compat_kexec_segment __user *, segments, +		       compat_ulong_t, flags)  {  	struct compat_kexec_segment in;  	struct kexec_segment out, __user *ksegments; @@ -1054,7 +1058,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry,  		return -EINVAL;  	ksegments = compat_alloc_user_space(nr_segments * sizeof(out)); -	for (i=0; i < nr_segments; i++) { +	for (i = 0; i < nr_segments; i++) {  		result = copy_from_user(&in, &segments[i], sizeof(in));  		if (result)  			return -EFAULT; @@ -1209,14 +1213,14 @@ void crash_save_cpu(struct pt_regs *regs, int cpu)  	 * squirrelled away.  ELF notes happen to provide  	 * all of that, so there is no need to invent something new.  	 */ -	buf = (u32*)per_cpu_ptr(crash_notes, cpu); +	buf = (u32 *)per_cpu_ptr(crash_notes, cpu);  	if (!buf)  		return;  	memset(&prstatus, 0, sizeof(prstatus));  	prstatus.pr_pid = current->pid;  	elf_core_copy_kernel_regs(&prstatus.pr_reg, regs);  	buf = append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, -		      	      &prstatus, sizeof(prstatus)); +			      &prstatus, sizeof(prstatus));  	final_note(buf);  } @@ -1225,13 +1229,12 @@ static int __init crash_notes_memory_init(void)  	/* Allocate memory for saving cpu registers. */  	crash_notes = alloc_percpu(note_buf_t);  	if (!crash_notes) { -		printk("Kexec: Memory allocation for saving cpu register" -		" states failed\n"); +		pr_warn("Kexec: Memory allocation for saving cpu register states failed\n");  		return -ENOMEM;  	}  	return 0;  } -module_init(crash_notes_memory_init) +subsys_initcall(crash_notes_memory_init);  /* @@ -1248,10 +1251,10 @@ module_init(crash_notes_memory_init)   *   * The function returns 0 on success and -EINVAL on failure.   */ -static int __init parse_crashkernel_mem(char 			*cmdline, -					unsigned long long	system_ram, -					unsigned long long	*crash_size, -					unsigned long long	*crash_base) +static int __init parse_crashkernel_mem(char *cmdline, +					unsigned long long system_ram, +					unsigned long long *crash_size, +					unsigned long long *crash_base)  {  	char *cur = cmdline, *tmp; @@ -1262,12 +1265,12 @@ static int __init parse_crashkernel_mem(char 			*cmdline,  		/* get the start of the range */  		start = memparse(cur, &tmp);  		if (cur == tmp) { -			pr_warning("crashkernel: Memory value expected\n"); +			pr_warn("crashkernel: Memory value expected\n");  			return -EINVAL;  		}  		cur = tmp;  		if (*cur != '-') { -			pr_warning("crashkernel: '-' expected\n"); +			pr_warn("crashkernel: '-' expected\n");  			return -EINVAL;  		}  		cur++; @@ -1276,31 +1279,30 @@ static int __init parse_crashkernel_mem(char 			*cmdline,  		if (*cur != ':') {  			end = memparse(cur, &tmp);  			if (cur == tmp) { -				pr_warning("crashkernel: Memory " -						"value expected\n"); +				pr_warn("crashkernel: Memory value expected\n");  				return -EINVAL;  			}  			cur = tmp;  			if (end <= start) { -				pr_warning("crashkernel: end <= start\n"); +				pr_warn("crashkernel: end <= start\n");  				return -EINVAL;  			}  		}  		if (*cur != ':') { -			pr_warning("crashkernel: ':' expected\n"); +			pr_warn("crashkernel: ':' expected\n");  			return -EINVAL;  		}  		cur++;  		size = memparse(cur, &tmp);  		if (cur == tmp) { -			pr_warning("Memory value expected\n"); +			pr_warn("Memory value expected\n");  			return -EINVAL;  		}  		cur = tmp;  		if (size >= system_ram) { -			pr_warning("crashkernel: invalid size\n"); +			pr_warn("crashkernel: invalid size\n");  			return -EINVAL;  		} @@ -1318,8 +1320,7 @@ static int __init parse_crashkernel_mem(char 			*cmdline,  			cur++;  			*crash_base = memparse(cur, &tmp);  			if (cur == tmp) { -				pr_warning("Memory value expected " -						"after '@'\n"); +				pr_warn("Memory value expected after '@'\n");  				return -EINVAL;  			}  		} @@ -1331,26 +1332,26 @@ static int __init parse_crashkernel_mem(char 			*cmdline,  /*   * That function parses "simple" (old) crashkernel command lines like   * - * 	crashkernel=size[@offset] + *	crashkernel=size[@offset]   *   * It returns 0 on success and -EINVAL on failure.   */ -static int __init parse_crashkernel_simple(char 		*cmdline, -					   unsigned long long 	*crash_size, -					   unsigned long long 	*crash_base) +static int __init parse_crashkernel_simple(char *cmdline, +					   unsigned long long *crash_size, +					   unsigned long long *crash_base)  {  	char *cur = cmdline;  	*crash_size = memparse(cmdline, &cur);  	if (cmdline == cur) { -		pr_warning("crashkernel: memory value expected\n"); +		pr_warn("crashkernel: memory value expected\n");  		return -EINVAL;  	}  	if (*cur == '@')  		*crash_base = memparse(cur+1, &cur);  	else if (*cur != ' ' && *cur != '\0') { -		pr_warning("crashkernel: unrecognized char\n"); +		pr_warn("crashkernel: unrecognized char\n");  		return -EINVAL;  	} @@ -1533,7 +1534,7 @@ void vmcoreinfo_append_str(const char *fmt, ...)  	size_t r;  	va_start(args, fmt); -	r = vsnprintf(buf, sizeof(buf), fmt, args); +	r = vscnprintf(buf, sizeof(buf), fmt, args);  	va_end(args);  	r = min(r, vmcoreinfo_max_size - vmcoreinfo_size); @@ -1547,10 +1548,10 @@ void vmcoreinfo_append_str(const char *fmt, ...)   * provide an empty default implementation here -- architecture   * code may override this   */ -void __attribute__ ((weak)) arch_crash_save_vmcoreinfo(void) +void __weak arch_crash_save_vmcoreinfo(void)  {} -unsigned long __attribute__ ((weak)) paddr_vmcoreinfo_note(void) +unsigned long __weak paddr_vmcoreinfo_note(void)  {  	return __pa((unsigned long)(char *)&vmcoreinfo_note);  } @@ -1617,7 +1618,11 @@ static int __init crash_save_vmcoreinfo_init(void)  #ifdef CONFIG_MEMORY_FAILURE  	VMCOREINFO_NUMBER(PG_hwpoison);  #endif +	VMCOREINFO_NUMBER(PG_head_mask);  	VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE); +#ifdef CONFIG_HUGETLBFS +	VMCOREINFO_SYMBOL(free_huge_page); +#endif  	arch_crash_save_vmcoreinfo();  	update_vmcoreinfo_note(); @@ -1625,7 +1630,7 @@ static int __init crash_save_vmcoreinfo_init(void)  	return 0;  } -module_init(crash_save_vmcoreinfo_init) +subsys_initcall(crash_save_vmcoreinfo_init);  /*   * Move into place and start executing a preloaded standalone @@ -1675,8 +1680,18 @@ int kernel_kexec(void)  	} else  #endif  	{ +		kexec_in_progress = true;  		kernel_restart_prepare(NULL); -		printk(KERN_EMERG "Starting new kernel\n"); +		migrate_to_reboot_cpu(); + +		/* +		 * migrate_to_reboot_cpu() disables CPU hotplug assuming that +		 * no further code needs to use CPU hotplug (which is true in +		 * the reboot case). However, the kexec path depends on using +		 * CPU hotplug again; so re-enable it here. +		 */ +		cpu_hotplug_enable(); +		pr_emerg("Starting new kernel\n");  		machine_shutdown();  	}  | 
