diff options
| author | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2013-05-15 10:26:50 -0400 | 
|---|---|---|
| committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2013-05-15 10:26:50 -0400 | 
| commit | 12e04ffcd93b25dfd726d46338c2ee7d23de556e (patch) | |
| tree | f91479a62805619168994fd3ee55e3ffa23fc24e /kernel/resource.c | |
| parent | 9eff37a8713939f218ab8bf0dc93f1d67af7b8b4 (diff) | |
| parent | f722406faae2d073cc1d01063d1123c35425939e (diff) | |
Merge tag 'v3.10-rc1' into stable/for-linus-3.10
Linux 3.10-rc1
* tag 'v3.10-rc1': (12273 commits)
  Linux 3.10-rc1
  [SCSI] qla2xxx: Update firmware link in Kconfig file.
  [SCSI] iscsi class, qla4xxx: fix sess/conn refcounting when find fns are used
  [SCSI] sas: unify the pointlessly separated enums sas_dev_type and sas_device_type
  [SCSI] pm80xx: thermal, sas controller config and error handling update
  [SCSI] pm80xx: NCQ error handling changes
  [SCSI] pm80xx: WWN Modification for PM8081/88/89 controllers
  [SCSI] pm80xx: Changed module name and debug messages update
  [SCSI] pm80xx: Firmware flash memory free fix, with addition of new memory region for it
  [SCSI] pm80xx: SPC new firmware changes for device id 0x8081 alone
  [SCSI] pm80xx: Added SPCv/ve specific hardware functionalities and relevant changes in common files
  [SCSI] pm80xx: MSI-X implementation for using 64 interrupts
  [SCSI] pm80xx: Updated common functions common for SPC and SPCv/ve
  [SCSI] pm80xx: Multiple inbound/outbound queue configuration
  [SCSI] pm80xx: Added SPCv/ve specific ids, variables and modify for SPC
  [SCSI] lpfc: fix up Kconfig dependencies
  [SCSI] Handle MLQUEUE busy response in scsi_send_eh_cmnd
  dm cache: set config value
  dm cache: move config fns
  dm thin: generate event when metadata threshold passed
  ...
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 198 | 
1 files changed, 176 insertions, 22 deletions
| diff --git a/kernel/resource.c b/kernel/resource.c index 73f35d4b30b..d7386986e10 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -21,6 +21,7 @@  #include <linux/seq_file.h>  #include <linux/device.h>  #include <linux/pfn.h> +#include <linux/mm.h>  #include <asm/io.h> @@ -50,6 +51,14 @@ struct resource_constraint {  static DEFINE_RWLOCK(resource_lock); +/* + * For memory hotplug, there is no way to free resource entries allocated + * by boot mem after the system is up. So for reusing the resource entry + * we need to remember the resource. + */ +static struct resource *bootmem_resource_free; +static DEFINE_SPINLOCK(bootmem_resource_lock); +  static void *r_next(struct seq_file *m, void *v, loff_t *pos)  {  	struct resource *p = v; @@ -151,6 +160,40 @@ __initcall(ioresources_init);  #endif /* CONFIG_PROC_FS */ +static void free_resource(struct resource *res) +{ +	if (!res) +		return; + +	if (!PageSlab(virt_to_head_page(res))) { +		spin_lock(&bootmem_resource_lock); +		res->sibling = bootmem_resource_free; +		bootmem_resource_free = res; +		spin_unlock(&bootmem_resource_lock); +	} else { +		kfree(res); +	} +} + +static struct resource *alloc_resource(gfp_t flags) +{ +	struct resource *res = NULL; + +	spin_lock(&bootmem_resource_lock); +	if (bootmem_resource_free) { +		res = bootmem_resource_free; +		bootmem_resource_free = res->sibling; +	} +	spin_unlock(&bootmem_resource_lock); + +	if (res) +		memset(res, 0, sizeof(struct resource)); +	else +		res = kzalloc(sizeof(struct resource), flags); + +	return res; +} +  /* Return the conflict entry if you can't request it */  static struct resource * __request_resource(struct resource *root, struct resource *new)  { @@ -706,24 +749,13 @@ void insert_resource_expand_to_fit(struct resource *root, struct resource *new)  	write_unlock(&resource_lock);  } -/** - * adjust_resource - modify a resource's start and size - * @res: resource to modify - * @start: new start value - * @size: new size - * - * Given an existing resource, change its start and size to match the - * arguments.  Returns 0 on success, -EBUSY if it can't fit. - * Existing children of the resource are assumed to be immutable. - */ -int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size) +static int __adjust_resource(struct resource *res, resource_size_t start, +				resource_size_t size)  {  	struct resource *tmp, *parent = res->parent;  	resource_size_t end = start + size - 1;  	int result = -EBUSY; -	write_lock(&resource_lock); -  	if (!parent)  		goto skip; @@ -751,6 +783,26 @@ skip:  	result = 0;   out: +	return result; +} + +/** + * adjust_resource - modify a resource's start and size + * @res: resource to modify + * @start: new start value + * @size: new size + * + * Given an existing resource, change its start and size to match the + * arguments.  Returns 0 on success, -EBUSY if it can't fit. + * Existing children of the resource are assumed to be immutable. + */ +int adjust_resource(struct resource *res, resource_size_t start, +			resource_size_t size) +{ +	int result; + +	write_lock(&resource_lock); +	result = __adjust_resource(res, start, size);  	write_unlock(&resource_lock);  	return result;  } @@ -762,7 +814,7 @@ static void __init __reserve_region_with_split(struct resource *root,  {  	struct resource *parent = root;  	struct resource *conflict; -	struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC); +	struct resource *res = alloc_resource(GFP_ATOMIC);  	struct resource *next_res = NULL;  	if (!res) @@ -787,7 +839,7 @@ static void __init __reserve_region_with_split(struct resource *root,  		/* conflict covered whole area */  		if (conflict->start <= res->start &&  				conflict->end >= res->end) { -			kfree(res); +			free_resource(res);  			WARN_ON(next_res);  			break;  		} @@ -797,10 +849,9 @@ static void __init __reserve_region_with_split(struct resource *root,  			end = res->end;  			res->end = conflict->start - 1;  			if (conflict->end < end) { -				next_res = kzalloc(sizeof(*next_res), -						GFP_ATOMIC); +				next_res = alloc_resource(GFP_ATOMIC);  				if (!next_res) { -					kfree(res); +					free_resource(res);  					break;  				}  				next_res->name = name; @@ -890,7 +941,7 @@ struct resource * __request_region(struct resource *parent,  				   const char *name, int flags)  {  	DECLARE_WAITQUEUE(wait, current); -	struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); +	struct resource *res = alloc_resource(GFP_KERNEL);  	if (!res)  		return NULL; @@ -924,7 +975,7 @@ struct resource * __request_region(struct resource *parent,  			continue;  		}  		/* Uhhuh, that didn't work out.. */ -		kfree(res); +		free_resource(res);  		res = NULL;  		break;  	} @@ -958,7 +1009,7 @@ int __check_region(struct resource *parent, resource_size_t start,  		return -EBUSY;  	release_resource(res); -	kfree(res); +	free_resource(res);  	return 0;  }  EXPORT_SYMBOL(__check_region); @@ -998,7 +1049,7 @@ void __release_region(struct resource *parent, resource_size_t start,  			write_unlock(&resource_lock);  			if (res->flags & IORESOURCE_MUXED)  				wake_up(&muxed_resource_wait); -			kfree(res); +			free_resource(res);  			return;  		}  		p = &res->sibling; @@ -1012,6 +1063,109 @@ void __release_region(struct resource *parent, resource_size_t start,  }  EXPORT_SYMBOL(__release_region); +#ifdef CONFIG_MEMORY_HOTREMOVE +/** + * release_mem_region_adjustable - release a previously reserved memory region + * @parent: parent resource descriptor + * @start: resource start address + * @size: resource region size + * + * This interface is intended for memory hot-delete.  The requested region + * is released from a currently busy memory resource.  The requested region + * must either match exactly or fit into a single busy resource entry.  In + * the latter case, the remaining resource is adjusted accordingly. + * Existing children of the busy memory resource must be immutable in the + * request. + * + * Note: + * - Additional release conditions, such as overlapping region, can be + *   supported after they are confirmed as valid cases. + * - When a busy memory resource gets split into two entries, the code + *   assumes that all children remain in the lower address entry for + *   simplicity.  Enhance this logic when necessary. + */ +int release_mem_region_adjustable(struct resource *parent, +			resource_size_t start, resource_size_t size) +{ +	struct resource **p; +	struct resource *res; +	struct resource *new_res; +	resource_size_t end; +	int ret = -EINVAL; + +	end = start + size - 1; +	if ((start < parent->start) || (end > parent->end)) +		return ret; + +	/* The alloc_resource() result gets checked later */ +	new_res = alloc_resource(GFP_KERNEL); + +	p = &parent->child; +	write_lock(&resource_lock); + +	while ((res = *p)) { +		if (res->start >= end) +			break; + +		/* look for the next resource if it does not fit into */ +		if (res->start > start || res->end < end) { +			p = &res->sibling; +			continue; +		} + +		if (!(res->flags & IORESOURCE_MEM)) +			break; + +		if (!(res->flags & IORESOURCE_BUSY)) { +			p = &res->child; +			continue; +		} + +		/* found the target resource; let's adjust accordingly */ +		if (res->start == start && res->end == end) { +			/* free the whole entry */ +			*p = res->sibling; +			free_resource(res); +			ret = 0; +		} else if (res->start == start && res->end != end) { +			/* adjust the start */ +			ret = __adjust_resource(res, end + 1, +						res->end - end); +		} else if (res->start != start && res->end == end) { +			/* adjust the end */ +			ret = __adjust_resource(res, res->start, +						start - res->start); +		} else { +			/* split into two entries */ +			if (!new_res) { +				ret = -ENOMEM; +				break; +			} +			new_res->name = res->name; +			new_res->start = end + 1; +			new_res->end = res->end; +			new_res->flags = res->flags; +			new_res->parent = res->parent; +			new_res->sibling = res->sibling; +			new_res->child = NULL; + +			ret = __adjust_resource(res, res->start, +						start - res->start); +			if (ret) +				break; +			res->sibling = new_res; +			new_res = NULL; +		} + +		break; +	} + +	write_unlock(&resource_lock); +	free_resource(new_res); +	return ret; +} +#endif	/* CONFIG_MEMORY_HOTREMOVE */ +  /*   * Managed region resource   */ | 
