Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 268364a0 authored by Yinghai Lu's avatar Yinghai Lu Committed by Ingo Molnar
Browse files

IO resources: add reserve_region_with_split()



add reserve_region_with_split() to not lose e820 reserved entries if
they overlap with existing IO regions:

with test case by extend 0xe0000000 - 0xeffffff to 0xdd800000 -
we get:
	e0000000-efffffff : PCI MMCONFIG 0
		 e0000000-efffffff : reserved

and in /proc/iomem we get:
	found conflict for reserved [dd800000, efffffff], try to reserve with split
	    __reserve_region_with_split: (PCI Bus #80) [dd000000, ddffffff], res: (reserved) [dd800000, efffffff]
	    __reserve_region_with_split: (PCI Bus #00) [de000000, dfffffff], res: (reserved) [de000000, efffffff]
	initcall pci_subsys_init+0x0/0x121 returned 0 after 381 msecs
in dmesg

various fixes and improvements suggested by Linus.

Signed-off-by: default avatarYinghai Lu <yhlu.kernel@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent d210baf5
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -108,6 +108,9 @@ extern struct resource iomem_resource;

extern int request_resource(struct resource *root, struct resource *new);
extern int release_resource(struct resource *new);
extern void reserve_region_with_split(struct resource *root,
			     resource_size_t start, resource_size_t end,
			     const char *name);
extern int insert_resource(struct resource *parent, struct resource *new);
extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new);
extern int allocate_resource(struct resource *root, struct resource *new,
+68 −0
Original line number Diff line number Diff line
@@ -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);

/**