diff options
| author | Bjorn Helgaas <bjorn.helgaas@hp.com> | 2010-10-26 15:41:33 -0600 | 
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-10-26 15:33:31 -0700 | 
| commit | e7f8567db9a7f6b3151b0b275e245c1cef0d9c70 (patch) | |
| tree | f04a01581e86ec2b8c175b9f27648679c70d592c /kernel/resource.c | |
| parent | a1862e31079149a52b6223776228c3aee493d4a7 (diff) | |
resources: support allocating space within a region from the top down
Allocate space from the top of a region first, then work downward,
if an architecture desires this.
When we allocate space from a resource, we look for gaps between children
of the resource.  Previously, we always looked at gaps from the bottom up.
For example, given this:
    [mem 0xbff00000-0xf7ffffff] PCI Bus 0000:00
      [mem 0xbff00000-0xbfffffff] gap -- available
      [mem 0xc0000000-0xdfffffff] PCI Bus 0000:02
      [mem 0xe0000000-0xf7ffffff] gap -- available
we attempted to allocate from the [mem 0xbff00000-0xbfffffff] gap first,
then the [mem 0xe0000000-0xf7ffffff] gap.
With this patch an architecture can choose to allocate from the top gap
[mem 0xe0000000-0xf7ffffff] first.
We can't do this across the board because iomem_resource.end is initialized
to 0xffffffff_ffffffff on 64-bit architectures, and most machines can't
address the entire 64-bit physical address space.  Therefore, we only
allocate top-down if the arch requests it by clearing
"resource_alloc_from_bottom".
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 98 | 
1 files changed, 94 insertions, 4 deletions
| diff --git a/kernel/resource.c b/kernel/resource.c index e15b922d4ba4..716b6804077e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -40,6 +40,23 @@ EXPORT_SYMBOL(iomem_resource);  static DEFINE_RWLOCK(resource_lock); +/* + * By default, we allocate free space bottom-up.  The architecture can request + * top-down by clearing this flag.  The user can override the architecture's + * choice with the "resource_alloc_from_bottom" kernel boot option, but that + * should only be a debugging tool. + */ +int resource_alloc_from_bottom = 1; + +static __init int setup_alloc_from_bottom(char *s) +{ +	printk(KERN_INFO +	       "resource: allocating from bottom-up; please report a bug\n"); +	resource_alloc_from_bottom = 1; +	return 0; +} +early_param("resource_alloc_from_bottom", setup_alloc_from_bottom); +  static void *r_next(struct seq_file *m, void *v, loff_t *pos)  {  	struct resource *p = v; @@ -380,7 +397,74 @@ static bool resource_contains(struct resource *res1, struct resource *res2)  }  /* + * Find the resource before "child" in the sibling list of "root" children. + */ +static struct resource *find_sibling_prev(struct resource *root, struct resource *child) +{ +	struct resource *this; + +	for (this = root->child; this; this = this->sibling) +		if (this->sibling == child) +			return this; + +	return NULL; +} + +/* + * Find empty slot in the resource tree given range and alignment. + * This version allocates from the end of the root resource first. + */ +static int find_resource_from_top(struct resource *root, struct resource *new, +				  resource_size_t size, resource_size_t min, +				  resource_size_t max, resource_size_t align, +				  resource_size_t (*alignf)(void *, +						   const struct resource *, +						   resource_size_t, +						   resource_size_t), +				  void *alignf_data) +{ +	struct resource *this; +	struct resource tmp, avail, alloc; + +	tmp.start = root->end; +	tmp.end = root->end; + +	this = find_sibling_prev(root, NULL); +	for (;;) { +		if (this) { +			if (this->end < root->end) +				tmp.start = this->end + 1; +		} else +			tmp.start = root->start; + +		resource_clip(&tmp, min, max); + +		/* Check for overflow after ALIGN() */ +		avail = *new; +		avail.start = ALIGN(tmp.start, align); +		avail.end = tmp.end; +		if (avail.start >= tmp.start) { +			alloc.start = alignf(alignf_data, &avail, size, align); +			alloc.end = alloc.start + size - 1; +			if (resource_contains(&avail, &alloc)) { +				new->start = alloc.start; +				new->end = alloc.end; +				return 0; +			} +		} + +		if (!this || this->start == root->start) +			break; + +		tmp.end = this->start - 1; +		this = find_sibling_prev(root, this); +	} +	return -EBUSY; +} + +/*   * Find empty slot in the resource tree given range and alignment. + * This version allocates from the beginning of the root resource first.   */  static int find_resource(struct resource *root, struct resource *new,  			 resource_size_t size, resource_size_t min, @@ -396,14 +480,15 @@ static int find_resource(struct resource *root, struct resource *new,  	tmp.start = root->start;  	/* -	 * Skip past an allocated resource that starts at 0, since the assignment -	 * of this->start - 1 to tmp->end below would cause an underflow. +	 * Skip past an allocated resource that starts at 0, since the +	 * assignment of this->start - 1 to tmp->end below would cause an +	 * underflow.  	 */  	if (this && this->start == 0) {  		tmp.start = this->end + 1;  		this = this->sibling;  	} -	for(;;) { +	for (;;) {  		if (this)  			tmp.end = this->start - 1;  		else @@ -424,8 +509,10 @@ static int find_resource(struct resource *root, struct resource *new,  				return 0;  			}  		} +  		if (!this)  			break; +  		tmp.start = this->end + 1;  		this = this->sibling;  	} @@ -458,7 +545,10 @@ int allocate_resource(struct resource *root, struct resource *new,  		alignf = simple_align_resource;  	write_lock(&resource_lock); -	err = find_resource(root, new, size, min, max, align, alignf, alignf_data); +	if (resource_alloc_from_bottom) +		err = find_resource(root, new, size, min, max, align, alignf, alignf_data); +	else +		err = find_resource_from_top(root, new, size, min, max, align, alignf, alignf_data);  	if (err >= 0 && __request_resource(root, new))  		err = -EBUSY;  	write_unlock(&resource_lock); | 
