Commit 124fe20d authored by Dan Williams's avatar Dan Williams
Browse files

mm: enhance region_is_ram() to region_intersects()



region_is_ram() is used to prevent the establishment of aliased mappings
to physical "System RAM" with incompatible cache settings.  However, it
uses "-1" to indicate both "unknown" memory ranges (ranges not described
by platform firmware) and "mixed" ranges (where the parameters describe
a range that partially overlaps "System RAM").

Fix this up by explicitly tracking the "unknown" vs "mixed" resource
cases and returning REGION_INTERSECTS, REGION_MIXED, or REGION_DISJOINT.
This re-write also adds support for detecting when the requested region
completely eclipses all of a resource.  Note, the implementation treats
overlaps between "unknown" and the requested memory type as
REGION_INTERSECTS.

Finally, other memory types can be passed in by name, for now the only
usage "System RAM".

Suggested-by: default avatarLuis R. Rodriguez <mcgrof@suse.com>
Reviewed-by: default avatarToshi Kani <toshi.kani@hp.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent f6ef5a2a
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -369,7 +369,14 @@ static inline int put_page_unless_one(struct page *page)
}

extern int page_is_ram(unsigned long pfn);
extern int region_is_ram(resource_size_t phys_addr, unsigned long size);

enum {
	REGION_INTERSECTS,
	REGION_DISJOINT,
	REGION_MIXED,
};

int region_intersects(resource_size_t offset, size_t size, const char *type);

/* Support for virtually mapped pages */
struct page *vmalloc_to_page(const void *addr);
+36 −25
Original line number Diff line number Diff line
@@ -492,40 +492,51 @@ int __weak page_is_ram(unsigned long pfn)
}
EXPORT_SYMBOL_GPL(page_is_ram);

/*
 * Search for a resouce entry that fully contains the specified region.
 * If found, return 1 if it is RAM, 0 if not.
 * If not found, or region is not fully contained, return -1
/**
 * region_intersects() - determine intersection of region with known resources
 * @start: region start address
 * @size: size of region
 * @name: name of resource (in iomem_resource)
 *
 * Check if the specified region partially overlaps or fully eclipses a
 * resource identified by @name.  Return REGION_DISJOINT if the region
 * does not overlap @name, return REGION_MIXED if the region overlaps
 * @type and another resource, and return REGION_INTERSECTS if the
 * region overlaps @type and no other defined resource. Note, that
 * REGION_INTERSECTS is also returned in the case when the specified
 * region overlaps RAM and undefined memory holes.
 *
 * Used by the ioremap functions to ensure the user is not remapping RAM and is
 * a vast speed up over walking through the resource table page by page.
 * region_intersect() is used by memory remapping functions to ensure
 * the user is not remapping RAM and is a vast speed up over walking
 * through the resource table page by page.
 */
int region_is_ram(resource_size_t start, unsigned long size)
int region_intersects(resource_size_t start, size_t size, const char *name)
{
	struct resource *p;
	resource_size_t end = start + size - 1;
	unsigned long flags = IORESOURCE_MEM | IORESOURCE_BUSY;
	const char *name = "System RAM";
	int ret = -1;
	resource_size_t end = start + size - 1;
	int type = 0; int other = 0;
	struct resource *p;

	read_lock(&resource_lock);
	for (p = iomem_resource.child; p ; p = p->sibling) {
		if (p->end < start)
			continue;
		bool is_type = strcmp(p->name, name) == 0 && p->flags == flags;

		if (p->start <= start && end <= p->end) {
			/* resource fully contains region */
			if ((p->flags != flags) || strcmp(p->name, name))
				ret = 0;
			else
				ret = 1;
			break;
		}
		if (end < p->start)
			break;	/* not found */
		if (start >= p->start && start <= p->end)
			is_type ? type++ : other++;
		if (end >= p->start && end <= p->end)
			is_type ? type++ : other++;
		if (p->start >= start && p->end <= end)
			is_type ? type++ : other++;
	}
	read_unlock(&resource_lock);
	return ret;

	if (other == 0)
		return type ? REGION_INTERSECTS : REGION_DISJOINT;

	if (type)
		return REGION_MIXED;

	return REGION_DISJOINT;
}

void __weak arch_remove_reservations(struct resource *avail)