diff options
Diffstat (limited to 'kernel/resource.c')
| -rw-r--r-- | kernel/resource.c | 68 | 
1 files changed, 68 insertions, 0 deletions
diff --git a/kernel/resource.c b/kernel/resource.c index 03d796c1b2e..414d6fc9131 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -516,6 +516,74 @@ int adjust_resource(struct resource *res, resource_size_t start, resource_size_t  	return result;  } +static void __init __reserve_region_with_split(struct resource *root, +		resource_size_t start, resource_size_t end, +		const char *name) +{ +	struct resource *parent = root; +	struct resource *conflict; +	struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); + +	if (!res) +		return; + +	res->name = name; +	res->start = start; +	res->end = end; +	res->flags = IORESOURCE_BUSY; + +	for (;;) { +		conflict = __request_resource(parent, res); +		if (!conflict) +			break; +		if (conflict != parent) { +			parent = conflict; +			if (!(conflict->flags & IORESOURCE_BUSY)) +				continue; +		} + +		/* Uhhuh, that didn't work out.. */ +		kfree(res); +		res = NULL; +		break; +	} + +	if (!res) { +		printk(KERN_DEBUG "    __reserve_region_with_split: (%s) [%llx, %llx], res: (%s) [%llx, %llx]\n", +			 conflict->name, conflict->start, conflict->end, +			 name, start, end); + +		/* failed, split and try again */ + +		/* conflict coverred whole area */ +		if (conflict->start <= start && conflict->end >= end) +			return; + +		if (conflict->start > start) +			__reserve_region_with_split(root, start, conflict->start-1, name); +		if (!(conflict->flags & IORESOURCE_BUSY)) { +			resource_size_t common_start, common_end; + +			common_start = max(conflict->start, start); +			common_end = min(conflict->end, end); +			if (common_start < common_end) +				__reserve_region_with_split(root, common_start, common_end, name); +		} +		if (conflict->end < end) +			__reserve_region_with_split(root, conflict->end+1, end, name); +	} + +} + +void reserve_region_with_split(struct resource *root, +		resource_size_t start, resource_size_t end, +		const char *name) +{ +	write_lock(&resource_lock); +	__reserve_region_with_split(root, start, end, name); +	write_unlock(&resource_lock); +} +  EXPORT_SYMBOL(adjust_resource);  /**  | 
