Skip to contentSkip to conversation

memblock: Make sure reserved.regions is freed really

PATCH In review Yinghai Lu 2 participants 5 messages Jun 16, 2012 – Jun 18, 2012

Subsystems (2)
lore
Share & tools
lore

Yinghai Lu

author
#0 lore raw

PATCH 1/1 memblock: Make sure reserved.regions is freed really

memblock_free() would double reserved.regions too, so we could free old range for reserved.regions.

So need to check that regions get doubled. If it is doubled, we need to free it.

Cc Tejun Heo <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Andrew Morton <[email protected]>
---
 mm/memblock.c |   23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)
mm/memblock.c +21 -2
Index: linux-2.6/mm/memblock.c
===================================================================
@@ -148,11 +148,26 @@ phys_addr_t __init_memblock memblock_fin
  */
 int __init_memblock memblock_free_reserved_regions(void)
 {
+	struct memblock_region *r;
+	int ret;
+
 	if (memblock.reserved.regions == memblock_reserved_init_regions)
 		return 0;
 
-	return memblock_free(__pa(memblock.reserved.regions),
-		 sizeof(struct memblock_region) * memblock.reserved.max);
+	/*
+	 * During memblock_free, reserved.regions could be doubled,
+	 * try to check with old one for checking, and need to free the new one.
+	 */
+	do {
+		r = memblock.reserved.regions;
+		ret = memblock_free(__pa(memblock.reserved.regions),
+			sizeof(struct memblock_region) * memblock.reserved.max);
+
+		if (ret)
+			break;
+	} while (memblock.reserved.regions != r);
+
+	return ret;
 }
 
 /*
@@ -163,6 +178,10 @@ int __init_memblock memblock_reserve_res
 	if (memblock.reserved.regions == memblock_reserved_init_regions)
 		return 0;
 
+	/*
+	 * Don't need to worry about if reserved.regions get updated later,
+	 * and it is big enough after memblock_free_reserved_regions().
+	 */
 	return memblock_reserve(__pa(memblock.reserved.regions),
 		 sizeof(struct memblock_region) * memblock.reserved.max);
 }
#1 lore raw

Hello, Yinghai.

Quoted 36 lines · Yinghai Lu

memblock_free() would double reserved.regions too, so we could free old range for reserved.regions.

So need to check that regions get doubled. If it is doubled, we need to free it.

Cc Tejun Heo <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Andrew Morton <[email protected]>
---
 mm/memblock.c |   23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

Index: linux-2.6/mm/memblock.c
===================================================================

mm/memblock.c +8 -2
@@ -148,11 +148,26 @@ phys_addr_t __init_memblock memblock_fin
  */
 int __init_memblock memblock_free_reserved_regions(void)
 {
+	struct memblock_region *r;
+	int ret;
+
 	if (memblock.reserved.regions == memblock_reserved_init_regions)
 		return 0;
 
-	return memblock_free(__pa(memblock.reserved.regions),
-		 sizeof(struct memblock_region) * memblock.reserved.max);
+	/*
+	 * During memblock_free, reserved.regions could be doubled,
+	 * try to check with old one for checking, and need to free the new one.
+	 */
+	do {

mm/memblock.c phys_addr_t __init_memblock memblock_fin · line 171

Quoted 10 lines
mm/memblock.c +9 -0
+		r = memblock.reserved.regions;
+		ret = memblock_free(__pa(memblock.reserved.regions),
+			sizeof(struct memblock_region) * memblock.reserved.max);
+
+		if (ret)
+			break;
+	} while (memblock.reserved.regions != r);
+
+	return ret;
 }

Hmmm... nice catch but I think it's a bit complex and ugly. In this case, we *know* that the region isn't gonna be split. Maybe a better option is to add something like the following?

void memblock_remove_region_by_ptr(struct memblock_type *type,
struct memblock_region *r)

{
	WARN_ON(/* make sure @r is inside @type->regions */);
	memblock_remove_region(type, r - type->regions);
}

Thanks.

Signature · 1 line
tejun
author
#2 lore raw
Quoted 45 lines · Tejun Heo

Hello, Yinghai.

Quoted 42 lines · Yinghai Lu

memblock_free() would double reserved.regions too, so we could free old range for reserved.regions.

So need to check that regions get doubled. If it is doubled, we need to free it.

Cc Tejun Heo <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Andrew Morton <[email protected]>

---
 mm/memblock.c |   23 +++++++++++++++++++++--

 1 file changed, 21 insertions(+), 2 deletions(-)

Index: linux-2.6/mm/memblock.c
===================================================================

mm/memblock.c +14 -2
@@ -148,11 +148,26 @@ phys_addr_t __init_memblock memblock_fin
   */
  int __init_memblock memblock_free_reserved_regions(void)
  {
+     struct memblock_region *r;
+     int ret;
+
       if (memblock.reserved.regions == memblock_reserved_init_regions)
               return 0;
 
-     return memblock_free(__pa(memblock.reserved.regions),
-              sizeof(struct memblock_region) * memblock.reserved.max);
+     /*
+      * During memblock_free, reserved.regions could be doubled,
+      * try to check with old one for checking, and need to free the new one.
+      */
+     do {
+             r = memblock.reserved.regions;
+             ret = memblock_free(__pa(memblock.reserved.regions),
+                     sizeof(struct memblock_region) * memblock.reserved.max);
+
+             if (ret)
+                     break;
Quoted 8 lines

mm/memblock.c phys_addr_t __init_memblock memblock_fin · line 171

Quoted 4 lines
mm/memblock.c +3 -0
+     } while (memblock.reserved.regions != r);
+
+     return ret;
  }

Hmmm... nice catch but I think it's a bit complex and ugly.  In this case, we *know* that the region isn't gonna be split.  Maybe a better option is to add something like the following?

how do you know __pa(memblock.reserved.regions) would not be merged with other entries
in reserved.regions?

Quoted 7 lines

void memblock_remove_region_by_ptr(struct memblock_type *type, struct memblock_region *r) {
       WARN_ON(/* make sure @r is inside @type->regions */); memblock_remove_region(type, r - type->regions); }

you want to change

/*
* Free memblock.reserved.regions
*/
int __init_memblock memblock_free_reserved_regions(void)

{
        if (memblock.reserved.regions == memblock_reserved_init_regions)
                return 0;

        return memblock_free(__pa(memblock.reserved.regions),
                 sizeof(struct memblock_region) * memblock.reserved.max);
}

to use memblock_remove_region_by_ptr() ?

Thanks

Yinghai

#3 lore raw

Hey, Yinghai.

Quoted 9 lines · Yinghai Lu
Quoted 3 lines

Hmmm... nice catch but I think it's a bit complex and ugly.  In this case, we *know* that the region isn't gonna be split.  Maybe a better option is to add something like the following?

how do you know __pa(memblock.reserved.regions) would not be merged with other entries
in reserved.regions?

Quoted 1 line
Quoted 8 lines
Quoted 6 lines

void memblock_remove_region_by_ptr(struct memblock_type *type, struct memblock_region *r) {
       WARN_ON(/* make sure @r is inside @type->regions */); memblock_remove_region(type, r - type->regions); }

you want to change

Heh, you're right. My suggestion was completely bonkers. Sorry about that. Hmm.... how about separating out removal pre-expansion and doing it before calling into free? ie. something like

static int memblock_prepare_for_isolation(struct memblock_type *type)

{
	/* we'll create at most two more regions */
	while (type->cnt + 2 > type->max)
		if (memblock_double_array(type) < 0)
			return -ENOMEM;
	return 0;
}

int __init_memblock memblock_free_reserved_regions(void)

{
	int ret;

	if (memblock.reserved.regions == memblock_reserved_init_regions)
		return 0;

	ret = memblock_prepare_for_isolation(&memblock.reserved);
	if (ret)
		return ret;

	ret = memblock_free(__pa(memblock.reserved.regions),
		sizeof(struct memblock_region) * memblock.reserved.max);
	WARN_ON(ret || memblock.reserved.regions has changed);
	return ret;
}

Thanks.

Signature · 1 line
tejun
author
#4 lore raw
Quoted 45 lines · Tejun Heo

Hey, Yinghai.

Quoted 9 lines · Yinghai Lu
Quoted 3 lines

Hmmm... nice catch but I think it's a bit complex and ugly.  In this case, we *know* that the region isn't gonna be split.  Maybe a better option is to add something like the following?

how do you know __pa(memblock.reserved.regions) would not be merged with other entries
in reserved.regions?

Quoted 1 line
Quoted 8 lines
Quoted 6 lines

void memblock_remove_region_by_ptr(struct memblock_type *type, struct memblock_region *r) {
       WARN_ON(/* make sure @r is inside @type->regions */); memblock_remove_region(type, r - type->regions); }

you want to change

Heh, you're right.  My suggestion was completely bonkers.  Sorry about that.  Hmm.... how about separating out removal pre-expansion and doing it before calling into free?  ie. something like

static int memblock_prepare_for_isolation(struct memblock_type *type) {
       /* we'll create at most two more regions */
       while (type->cnt + 2 > type->max)
               if (memblock_double_array(type) < 0)
                       return -ENOMEM;
       return 0;
}

int __init_memblock memblock_free_reserved_regions(void) {
       int ret;

       if (memblock.reserved.regions == memblock_reserved_init_regions) return 0;

       ret = memblock_prepare_for_isolation(&memblock.reserved); if (ret)
               return ret;

Quoted 5 lines

       ret = memblock_free(__pa(memblock.reserved.regions), sizeof(struct memblock_region) * memblock.reserved.max); WARN_ON(ret || memblock.reserved.regions has changed); return ret;
}

yes. that looks good. but that WARN_ON looks wrong, could just drop that line.

Please send complete version to Andrew or Linus.

Thanks

Yinghai