diff options
Diffstat (limited to 'kernel/kexec.c')
| -rw-r--r-- | kernel/kexec.c | 131 | 
1 files changed, 110 insertions, 21 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c index bddd3d7a74b..b574920cbd4 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -55,7 +55,7 @@ struct resource crashk_res = {  	.flags = IORESOURCE_BUSY | IORESOURCE_MEM  };  struct resource crashk_low_res = { -	.name  = "Crash kernel low", +	.name  = "Crash kernel",  	.start = 0,  	.end   = 0,  	.flags = IORESOURCE_BUSY | IORESOURCE_MEM @@ -1118,12 +1118,8 @@ void __weak crash_free_reserved_phys_range(unsigned long begin,  {  	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++; -	} +	for (addr = begin; addr < end; addr += PAGE_SIZE) +		free_reserved_page(pfn_to_page(addr >> PAGE_SHIFT));  }  int crash_shrink_memory(unsigned long new_size) @@ -1368,35 +1364,114 @@ static int __init parse_crashkernel_simple(char 		*cmdline,  	return 0;  } +#define SUFFIX_HIGH 0 +#define SUFFIX_LOW  1 +#define SUFFIX_NULL 2 +static __initdata char *suffix_tbl[] = { +	[SUFFIX_HIGH] = ",high", +	[SUFFIX_LOW]  = ",low", +	[SUFFIX_NULL] = NULL, +}; +  /* - * That function is the entry point for command line parsing and should be - * called from the arch-specific code. + * That function parses "suffix"  crashkernel command lines like + * + *	crashkernel=size,[high|low] + * + * It returns 0 on success and -EINVAL on failure.   */ +static int __init parse_crashkernel_suffix(char *cmdline, +					   unsigned long long	*crash_size, +					   unsigned long long	*crash_base, +					   const char *suffix) +{ +	char *cur = cmdline; + +	*crash_size = memparse(cmdline, &cur); +	if (cmdline == cur) { +		pr_warn("crashkernel: memory value expected\n"); +		return -EINVAL; +	} + +	/* check with suffix */ +	if (strncmp(cur, suffix, strlen(suffix))) { +		pr_warn("crashkernel: unrecognized char\n"); +		return -EINVAL; +	} +	cur += strlen(suffix); +	if (*cur != ' ' && *cur != '\0') { +		pr_warn("crashkernel: unrecognized char\n"); +		return -EINVAL; +	} + +	return 0; +} + +static __init char *get_last_crashkernel(char *cmdline, +			     const char *name, +			     const char *suffix) +{ +	char *p = cmdline, *ck_cmdline = NULL; + +	/* find crashkernel and use the last one if there are more */ +	p = strstr(p, name); +	while (p) { +		char *end_p = strchr(p, ' '); +		char *q; + +		if (!end_p) +			end_p = p + strlen(p); + +		if (!suffix) { +			int i; + +			/* skip the one with any known suffix */ +			for (i = 0; suffix_tbl[i]; i++) { +				q = end_p - strlen(suffix_tbl[i]); +				if (!strncmp(q, suffix_tbl[i], +					     strlen(suffix_tbl[i]))) +					goto next; +			} +			ck_cmdline = p; +		} else { +			q = end_p - strlen(suffix); +			if (!strncmp(q, suffix, strlen(suffix))) +				ck_cmdline = p; +		} +next: +		p = strstr(p+1, name); +	} + +	if (!ck_cmdline) +		return NULL; + +	return ck_cmdline; +} +  static int __init __parse_crashkernel(char *cmdline,  			     unsigned long long system_ram,  			     unsigned long long *crash_size,  			     unsigned long long *crash_base, -				const char *name) +			     const char *name, +			     const char *suffix)  { -	char 	*p = cmdline, *ck_cmdline = NULL;  	char	*first_colon, *first_space; +	char	*ck_cmdline;  	BUG_ON(!crash_size || !crash_base);  	*crash_size = 0;  	*crash_base = 0; -	/* find crashkernel and use the last one if there are more */ -	p = strstr(p, name); -	while (p) { -		ck_cmdline = p; -		p = strstr(p+1, name); -	} +	ck_cmdline = get_last_crashkernel(cmdline, name, suffix);  	if (!ck_cmdline)  		return -EINVAL;  	ck_cmdline += strlen(name); +	if (suffix) +		return parse_crashkernel_suffix(ck_cmdline, crash_size, +				crash_base, suffix);  	/*  	 * if the commandline contains a ':', then that's the extended  	 * syntax -- if not, it must be the classic syntax @@ -1413,13 +1488,26 @@ static int __init __parse_crashkernel(char *cmdline,  	return 0;  } +/* + * That function is the entry point for command line parsing and should be + * called from the arch-specific code. + */  int __init parse_crashkernel(char *cmdline,  			     unsigned long long system_ram,  			     unsigned long long *crash_size,  			     unsigned long long *crash_base)  {  	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base, -					"crashkernel="); +					"crashkernel=", NULL); +} + +int __init parse_crashkernel_high(char *cmdline, +			     unsigned long long system_ram, +			     unsigned long long *crash_size, +			     unsigned long long *crash_base) +{ +	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base, +				"crashkernel=", suffix_tbl[SUFFIX_HIGH]);  }  int __init parse_crashkernel_low(char *cmdline, @@ -1428,7 +1516,7 @@ int __init parse_crashkernel_low(char *cmdline,  			     unsigned long long *crash_base)  {  	return __parse_crashkernel(cmdline, system_ram, crash_size, crash_base, -					"crashkernel_low="); +				"crashkernel=", suffix_tbl[SUFFIX_LOW]);  }  static void update_vmcoreinfo_note(void) @@ -1489,7 +1577,7 @@ static int __init crash_save_vmcoreinfo_init(void)  	VMCOREINFO_SYMBOL(swapper_pg_dir);  #endif  	VMCOREINFO_SYMBOL(_stext); -	VMCOREINFO_SYMBOL(vmlist); +	VMCOREINFO_SYMBOL(vmap_area_list);  #ifndef CONFIG_NEED_MULTIPLE_NODES  	VMCOREINFO_SYMBOL(mem_map); @@ -1527,7 +1615,8 @@ 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_OFFSET(vmap_area, va_start); +	VMCOREINFO_OFFSET(vmap_area, list);  	VMCOREINFO_LENGTH(zone.free_area, MAX_ORDER);  	log_buf_kexec_setup();  	VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);  |