[PATCH] imalloc supersets
John Rose
johnrose at austin.ibm.com
Tue Jul 20 04:30:01 EST 2004
The patch below implements the ability to query outstanding imalloc regions for
a given virtual address range. The patch extends im_get_area() to allow a
region criterion of IM_REGION_SUPERSET. For a particular "superset" virtual
address and size passed into im_get_area(), the function returns the first
outstanding region that is contained within this superset region.
The patch also changes iounmap_explicit() to allow for the unmapping of all
regions that fit under a "supserset".
This ability is necessary for PHB DLPAR. For a PHB removal, the RPA requires
that all of its children slots already be dynamically removed. Each of these
slot-level removals has fractured the imalloc region assigned to the PHB at
boot. At PHB removal time, it is necessary to iounmap() the remaining
artifacts of the initial PHB region.
Thanks-
John
Signed-off-by: John Rose <johnrose at austin.ibm.com>
diff -X /home/johnrose/tmp/diffignore.txt -urpN sles9-rc5-vanilla/arch/ppc64/mm/imalloc.c sles9-rc5/arch/ppc64/mm/imalloc.c
--- sles9-rc5-vanilla/arch/ppc64/mm/imalloc.c 2004-07-16 16:11:54.000000000 -0500
+++ sles9-rc5/arch/ppc64/mm/imalloc.c 2004-07-19 11:14:29.000000000 -0500
@@ -37,33 +37,51 @@ static int get_free_im_addr(unsigned lon
return 0;
}
+/* Return whether the region described by v_addr and size is a subset
+ * of the region described by parent
+ */
+static inline int im_region_is_subset(unsigned long v_addr, unsigned long size,
+ struct vm_struct *parent)
+{
+ return (int) (v_addr >= (unsigned long) parent->addr &&
+ v_addr < (unsigned long) parent->addr + parent->size &&
+ size < parent->size);
+}
+
+/* Return whether the region described by v_addr and size is a superset
+ * of the region described by child
+ */
+static int im_region_is_superset(unsigned long v_addr, unsigned long size,
+ struct vm_struct *child)
+{
+ struct vm_struct parent;
+
+ parent.addr = (void *) v_addr;
+ parent.size = size;
+
+ return im_region_is_subset((unsigned long) child->addr, child->size,
+ &parent);
+}
+
/* Return whether the region described by v_addr and size overlaps
* the region described by vm. Overlapping regions meet the
* following conditions:
* 1) The regions share some part of the address space
* 2) The regions aren't identical
- * 3) The first region is not a subset of the second
+ * 3) Neither region is a subset of the other
*/
-static inline int im_region_overlaps(unsigned long v_addr, unsigned long size,
+static int im_region_overlaps(unsigned long v_addr, unsigned long size,
struct vm_struct *vm)
{
+ if (im_region_is_superset(v_addr, size, vm))
+ return 0;
+
return (v_addr + size > (unsigned long) vm->addr + vm->size &&
v_addr < (unsigned long) vm->addr + vm->size) ||
(v_addr < (unsigned long) vm->addr &&
v_addr + size > (unsigned long) vm->addr);
}
-/* Return whether the region described by v_addr and size is a subset
- * of the region described by vm
- */
-static inline int im_region_is_subset(unsigned long v_addr, unsigned long size,
- struct vm_struct *vm)
-{
- return (int) (v_addr >= (unsigned long) vm->addr &&
- v_addr < (unsigned long) vm->addr + vm->size &&
- size < vm->size);
-}
-
/* Determine imalloc status of region described by v_addr and size.
* Can return one of the following:
* IM_REGION_UNUSED - Entire region is unallocated in imalloc space.
@@ -73,28 +91,37 @@ static inline int im_region_is_subset(un
* IM_REGION_EXISTS - Exact region already allocated in imalloc space.
* vm will be assigned to a ptr to the existing imlist
* member.
- * IM_REGION_OVERLAPS - A portion of the region is already allocated in
- * imalloc space.
+ * IM_REGION_OVERLAPS - Region overlaps an allocated region in imalloc space.
+ * IM_REGION_SUPERSET - Region is a superset of a region that is already
+ * allocated in imalloc space.
*/
static int im_region_status(unsigned long v_addr, unsigned long size,
struct vm_struct **vm)
{
struct vm_struct *tmp;
- for (tmp = imlist; tmp; tmp = tmp->next)
- if (v_addr < (unsigned long) tmp->addr + tmp->size)
+ for (tmp = imlist; tmp; tmp = tmp->next)
+ if (v_addr < (unsigned long) tmp->addr + tmp->size)
break;
-
+
if (tmp) {
if (im_region_overlaps(v_addr, size, tmp))
return IM_REGION_OVERLAP;
*vm = tmp;
- if (im_region_is_subset(v_addr, size, tmp))
+ if (im_region_is_subset(v_addr, size, tmp)) {
+ /* Return with tmp pointing to superset */
return IM_REGION_SUBSET;
+ }
+ if (im_region_is_superset(v_addr, size, tmp)) {
+ /* Return with tmp pointing to first subset */
+ return IM_REGION_SUPERSET;
+ }
else if (v_addr == (unsigned long) tmp->addr &&
- size == tmp->size)
+ size == tmp->size) {
+ /* Return with tmp pointing to exact region */
return IM_REGION_EXISTS;
+ }
}
*vm = NULL;
@@ -208,6 +235,10 @@ static struct vm_struct * __im_get_area(
tmp = split_im_region(req_addr, size, tmp);
break;
case IM_REGION_EXISTS:
+ /* Return requested region */
+ break;
+ case IM_REGION_SUPERSET:
+ /* Return first existing subset of requested region */
break;
default:
printk(KERN_ERR "%s() unexpected imalloc region status\n",
diff -X /home/johnrose/tmp/diffignore.txt -urpN sles9-rc5-vanilla/arch/ppc64/mm/init.c sles9-rc5/arch/ppc64/mm/init.c
--- sles9-rc5-vanilla/arch/ppc64/mm/init.c 2004-07-16 16:11:54.000000000 -0500
+++ sles9-rc5/arch/ppc64/mm/init.c 2004-07-19 13:08:56.000000000 -0500
@@ -392,9 +392,28 @@ void iounmap(void *addr)
return;
}
+static int iounmap_subset_regions(void *addr, unsigned long size)
+{
+ struct vm_struct *area;
+
+ /* Check whether subsets of this region exist */
+ area = im_get_area((unsigned long) addr, size, IM_REGION_SUPERSET);
+ if (area == NULL)
+ return 1;
+
+ while (area) {
+ iounmap(area->addr);
+ area = im_get_area((unsigned long) addr, size,
+ IM_REGION_SUPERSET);
+ }
+
+ return 0;
+}
+
int iounmap_explicit(void *addr, unsigned long size)
{
struct vm_struct *area;
+ int rc;
/* addr could be in EEH or IO region, map it to IO region regardless.
*/
@@ -407,12 +426,17 @@ int iounmap_explicit(void *addr, unsigne
area = im_get_area((unsigned long) addr, size,
IM_REGION_EXISTS | IM_REGION_SUBSET);
if (area == NULL) {
- printk(KERN_ERR "%s() cannot unmap nonexistant range 0x%lx\n",
+ /* Determine whether subset regions exist. If so, unmap */
+ rc = iounmap_subset_regions(addr, size);
+ if (rc) {
+ printk(KERN_ERR
+ "%s() cannot unmap nonexistent range 0x%lx\n",
__FUNCTION__, (unsigned long) addr);
- return 1;
+ return 1;
+ }
+ } else {
+ iounmap(area->addr);
}
-
- iounmap(area->addr);
return 0;
}
** Sent via the linuxppc64-dev mail list. See http://lists.linuxppc.org/
More information about the Linuxppc64-dev
mailing list