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

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
...@@ -369,7 +369,14 @@ static inline int put_page_unless_one(struct page *page) ...@@ -369,7 +369,14 @@ static inline int put_page_unless_one(struct page *page)
} }
extern int page_is_ram(unsigned long pfn); 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 */ /* Support for virtually mapped pages */
struct page *vmalloc_to_page(const void *addr); struct page *vmalloc_to_page(const void *addr);
......
...@@ -492,40 +492,51 @@ int __weak page_is_ram(unsigned long pfn) ...@@ -492,40 +492,51 @@ int __weak page_is_ram(unsigned long pfn)
} }
EXPORT_SYMBOL_GPL(page_is_ram); EXPORT_SYMBOL_GPL(page_is_ram);
/* /**
* Search for a resouce entry that fully contains the specified region. * region_intersects() - determine intersection of region with known resources
* If found, return 1 if it is RAM, 0 if not. * @start: region start address
* If not found, or region is not fully contained, return -1 * @size: size of region
* @name: name of resource (in iomem_resource)
* *
* Used by the ioremap functions to ensure the user is not remapping RAM and is * Check if the specified region partially overlaps or fully eclipses a
* a vast speed up over walking through the resource table page by page. * 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.
*
* 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; unsigned long flags = IORESOURCE_MEM | IORESOURCE_BUSY;
const char *name = "System RAM"; resource_size_t end = start + size - 1;
int ret = -1; int type = 0; int other = 0;
struct resource *p;
read_lock(&resource_lock); read_lock(&resource_lock);
for (p = iomem_resource.child; p ; p = p->sibling) { for (p = iomem_resource.child; p ; p = p->sibling) {
if (p->end < start) bool is_type = strcmp(p->name, name) == 0 && p->flags == flags;
continue;
if (start >= p->start && start <= p->end)
if (p->start <= start && end <= p->end) { is_type ? type++ : other++;
/* resource fully contains region */ if (end >= p->start && end <= p->end)
if ((p->flags != flags) || strcmp(p->name, name)) is_type ? type++ : other++;
ret = 0; if (p->start >= start && p->end <= end)
else is_type ? type++ : other++;
ret = 1;
break;
}
if (end < p->start)
break; /* not found */
} }
read_unlock(&resource_lock); 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) void __weak arch_remove_reservations(struct resource *avail)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment