Skip to contentSkip to conversation

x86, mm: fix boot hang regression

PATCH In review Yuanhan Liu 5 participants 15 messages May 25, 2013 – May 31, 2013

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 x86, mm: fix boot hang regression

Commit 8d57470d introduced a kernel panic while setting mem=2G at boot time, and commit c9b3234a6 turns the the kernel panic to hang.

While, the reason is the same: the are accessing a BAD address; I mean the mapping is broken.

Here is a mem mapping range dumped at boot time:

    [mem 0x00000000-0x000fffff] page 4k  (0)
    [mem 0x7fe00000-0x7fffffff] page 1G  (1)
    [mem 0x7c000000-0x7fdfffff] page 1G  (2)
    [mem 0x00100000-0x001fffff] page 4k  (3)
    [mem 0x00200000-0x7bffffff] page 2M  (4)

Where, we met no problems while setting memory map for region (0) to (3). But we have set PG_LEVEL_1G mapping for pud index 0x1 at (1).

And pud index comes to 0x1 as well while setting 0x40000000-0x7bf00000 part of (4). What's more, it's PG_LEVEL_2M mapping, which results to a splitting of PG_LEVEL_1G mapping. This breaks former mapping for (1) and (2). In the same time, due to "end" setting to 0x7c000000, we missed the chance to fix it at phys_pmd_init() for code:

	if (address >= end) {
		....
		continue;
	}

Thus, using a extra flag to indicate we are splitting a large PUD(or PMD) and changing the above if statement to following will make this issue gone:

	if(address >= end && !spliting) {
		...
	}

---
I reported this panic regression long time ago, and I didn't notic the above panic->hang change before, which might confuse Yinghai for understanding what happened from 2 logs I sent before(one is from 8d57470d, another is from the HEAD commit at that time, which turn to a hang as stated). More, it seems that Yinghai can't produce it. And I was busying at something else. And I finally got a day yesterday(and a good mood ;).

Last, Thanks Changlong's effort for bisecting the 2 above commit.

---
 arch/x86/mm/init_64.c |   51 +++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 43 insertions(+), 8 deletions(-)
arch/x86/mm/init_64.c +43 -8
index bb00c46..e4c7038 100644
@@ -401,7 +401,7 @@ void __init cleanup_highmap(void)
 
 static unsigned long __meminit
 phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end,
-	      pgprot_t prot)
+	      pgprot_t prot, bool split_pmd)
 {
 	unsigned long pages = 0, next;
 	unsigned long last_map_addr = end;
@@ -411,7 +411,7 @@ phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end,
 
 	for (i = pte_index(addr); i < PTRS_PER_PTE; i++, addr = next, pte++) {
 		next = (addr & PAGE_MASK) + PAGE_SIZE;
-		if (addr >= end) {
+		if (addr >= end && !split_pmd) {
 			if (!after_bootmem &&
 			    !e820_any_mapped(addr & PAGE_MASK, next, E820_RAM) &&
 			    !e820_any_mapped(addr & PAGE_MASK, next, E820_RESERVED_KERN))
@@ -446,7 +446,7 @@ phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end,
 
 static unsigned long __meminit
 phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
-	      unsigned long page_size_mask, pgprot_t prot)
+	      unsigned long page_size_mask, pgprot_t prot, bool split_pud)
 {
 	unsigned long pages = 0, next;
 	unsigned long last_map_addr = end;
@@ -457,9 +457,10 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 		pmd_t *pmd = pmd_page + pmd_index(address);
 		pte_t *pte;
 		pgprot_t new_prot = prot;
+		bool split_pmd = false;
 
 		next = (address & PMD_MASK) + PMD_SIZE;
-		if (address >= end) {
+		if (address >= end && !split_pud) {
 			if (!after_bootmem &&
 			    !e820_any_mapped(address & PMD_MASK, next, E820_RAM) &&
 			    !e820_any_mapped(address & PMD_MASK, next, E820_RESERVED_KERN))
@@ -472,7 +473,7 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 				spin_lock(&init_mm.page_table_lock);
 				pte = (pte_t *)pmd_page_vaddr(*pmd);
 				last_map_addr = phys_pte_init(pte, address,
-								end, prot);
+							end, prot, split_pmd);
 				spin_unlock(&init_mm.page_table_lock);
 				continue;
 			}
@@ -495,6 +496,7 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 				continue;
 			}
 			new_prot = pte_pgprot(pte_clrhuge(*(pte_t *)pmd));
+			split_pmd = true;
 		}
 
 		if (page_size_mask & (1<<PG_LEVEL_2M)) {
@@ -509,7 +511,8 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end,
 		}
 
 		pte = alloc_low_page();
-		last_map_addr = phys_pte_init(pte, address, end, new_prot);
+		last_map_addr = phys_pte_init(pte, address, end,
+					      new_prot, split_pmd);
 
 		spin_lock(&init_mm.page_table_lock);
 		pmd_populate_kernel(&init_mm, pmd, pte);
@@ -531,6 +534,7 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 		pud_t *pud = pud_page + pud_index(addr);
 		pmd_t *pmd;
 		pgprot_t prot = PAGE_KERNEL;
+		bool split_pud = false;
 
 		next = (addr & PUD_MASK) + PUD_SIZE;
 		if (addr >= end) {
@@ -545,7 +549,8 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 			if (!pud_large(*pud)) {
 				pmd = pmd_offset(pud, 0);
 				last_map_addr = phys_pmd_init(pmd, addr, end,
-							 page_size_mask, prot);
+							 page_size_mask, prot,
+							 split_pud);
 				__flush_tlb_all();
 				continue;
 			}
@@ -568,6 +573,36 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 				continue;
 			}
 			prot = pte_pgprot(pte_clrhuge(*(pte_t *)pud));
+			/*
+			 * We set page table in top-down now, which means we
+			 * might have set a PG_LEVEL_1G mapping for a higher
+			 * address.
+			 *
+			 * And in the meantime, here we meet the same PUD in
+			 * a lower mem region and we are about to split it.
+			 * Setting split_pud to make sure we will re-map
+			 * former mapping as well.  Or, we will just ignore
+			 * it due to
+			 *     if (address >= end) {
+			 *     	       ...
+			 *     	       continue;
+			 *     }
+			 * at phys_pmd_init().
+			 *
+			 * Example: here is one case I met:
+			 *     [mem 0x00000000-0x000fffff] page 4k  (0)
+			 *     [mem 0x7fe00000-0x7fffffff] page 1G  (1)
+			 *     [mem 0x7c000000-0x7fdfffff] page 1G  (2)
+			 *     [mem 0x00100000-0x001fffff] page 4k  (3)
+			 *     [mem 0x00200000-0x7bffffff] page 2M  (4)
+			 *
+			 * Where mem 0x400000000 to mem 0x7fffffff will use same
+			 * PUD, and we have set a PG_LEVEL_1G mapping at (1).
+			 * While handling 0x40000000 - 0x7bf00000 part of (4),
+			 * we will split PUD and break former mapping for (1)
+			 * and (2) as stated above.
+			 */
+			split_pud = true;
 		}
 
 		if (page_size_mask & (1<<PG_LEVEL_1G)) {
@@ -583,7 +618,7 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end,
 
 		pmd = alloc_low_page();
 		last_map_addr = phys_pmd_init(pmd, addr, end, page_size_mask,
-					      prot);
+					      prot, split_pud);
 
 		spin_lock(&init_mm.page_table_lock);
 		pud_populate(&init_mm, pud, pmd);
Signature · 1 line
1.7.7.6
#1 lore raw
Quoted 13 lines · Yuanhan Liu

Commit 8d57470d introduced a kernel panic while setting mem=2G at boot time, and commit c9b3234a6 turns the the kernel panic to hang.

While, the reason is the same: the are accessing a BAD address; I mean the mapping is broken.

Here is a mem mapping range dumped at boot time:

    [mem 0x00000000-0x000fffff] page 4k  (0)
    [mem 0x7fe00000-0x7fffffff] page 1G  (1)
    [mem 0x7c000000-0x7fdfffff] page 1G  (2)
    [mem 0x00100000-0x001fffff] page 4k  (3)
    [mem 0x00200000-0x7bffffff] page 2M  (4)

...

Quoted 11 lines

I reported this panic regression long time ago, and I didn't notic the above panic->hang change before, which might confuse Yinghai for understanding what happened from 2 logs I sent before(one is from 8d57470d, another is from the HEAD commit at that time, which turn to a hang as stated). More, it seems that Yinghai can't produce it. And I was busying at something else. And I finally got a day yesterday(and a good mood ;).

Last, Thanks Changlong's effort for bisecting the 2 above commit.

---
 arch/x86/mm/init_64.c |   51 +++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 43 insertions(+), 8 deletions(-)

oh, I know the reason, my intel box has acpi or reserved area just below 2GiB.

your patch is not right fix.

Attached patch should fix the problem.

Thanks

Yinghai

Subject: [PATCH] x86: Fix adjust_range_size_mask calling position

Commit 8d57470d cause a kernel panic while setting mem=2G.

Quoted 5 lines
    [mem 0x00000000-0x000fffff] page 4k
    [mem 0x7fe00000-0x7fffffff] page 1G
    [mem 0x7c000000-0x7fdfffff] page 1G
    [mem 0x00100000-0x001fffff] page 4k
    [mem 0x00200000-0x7bffffff] page 2M

but for last entry we should have

Quoted 2 lines
    [mem 0x00200000-0x3fffffff] page 2M
    [mem 0x40000000-0x7bffffff] page 1G

Actually there is bug about calling sequence for adjust_range_page_size_mask(). merge first will make adjust to 1g for partial range fail.

Fix that by calling adjust_range_size_mask before merging mem_range with same page size.

We need this one for v3.9 stable.

---
 arch/x86/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/mm/init.c +3 -3
Index: linux-2.6/arch/x86/mm/init.c
===================================================================
@@ -277,6 +277,9 @@ static int __meminit split_mem_range(str
 	end_pfn = limit_pfn;
 	nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0);
 
+	if (!after_bootmem)
+		adjust_range_page_size_mask(mr, nr_range);
+
 	/* try to merge same page size and continuous */
 	for (i = 0; nr_range > 1 && i < nr_range - 1; i++) {
 		unsigned long old_start;
@@ -291,9 +294,6 @@ static int __meminit split_mem_range(str
 		nr_range--;
 	}
 
-	if (!after_bootmem)
-		adjust_range_page_size_mask(mr, nr_range);
-
 	for (i = 0; i < nr_range; i++)
 		printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
 				mr[i].start, mr[i].end - 1,
author Tested-by
#2 lore raw
Quoted 23 lines · Yinghai Lu
Quoted 13 lines · Yuanhan Liu

Commit 8d57470d introduced a kernel panic while setting mem=2G at boot time, and commit c9b3234a6 turns the the kernel panic to hang.

While, the reason is the same: the are accessing a BAD address; I mean the mapping is broken.

Here is a mem mapping range dumped at boot time:

    [mem 0x00000000-0x000fffff] page 4k  (0)
    [mem 0x7fe00000-0x7fffffff] page 1G  (1)
    [mem 0x7c000000-0x7fdfffff] page 1G  (2)
    [mem 0x00100000-0x001fffff] page 4k  (3)
    [mem 0x00200000-0x7bffffff] page 2M  (4)

...

Quoted 7 lines

I reported this panic regression long time ago, and I didn't notic the above panic->hang change before, which might confuse Yinghai for understanding what happened from 2 logs I sent before(one is from 8d57470d, another is from the HEAD commit at that time, which turn to a hang as stated). More, it seems that Yinghai can't produce it. And I was busying at something else. And I finally got a day yesterday(and a good mood ;).

Quoted 6 lines
Quoted 4 lines

Last, Thanks Changlong's effort for bisecting the 2 above commit.

---
 arch/x86/mm/init_64.c |   51 +++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 43 insertions(+), 8 deletions(-)

oh, I know the reason, my intel box has acpi or reserved area just below 2GiB.

Due to the 1GB page mapping support, it seems that this issue does not exist on platform before Sandybridge.

Quoted 4 lines

your patch is not right fix.

Attached patch should fix the problem.

Firstly, your patch works and feel free to add:

While, your patch fixes this issue on source side. I thought that before, too. But I think it's better to fix it on root side. That's why I sent a patch to try to fix it at mapping stage; Meanwhile, I was trying to sent another patch to fix this issue on source side later. And you did that.

On the other hand, since we support splitting PUD(and PMD), I guess we should make it do the right work just in case will meet such case later.

So, I still think my patch does fix something and should be merged, unless I did it wrongly. And please correct me if I'm wrong.

Thanks.

	--yliu
#3 lore raw
Quoted 2 lines · Yuanhan Liu

Firstly, your patch works and feel free to add:

ok, will resend it in complete form.

Quoted 9 lines

While, your patch fixes this issue on source side. I thought that before, too. But I think it's better to fix it on root side. That's why I sent a patch to try to fix it at mapping stage; Meanwhile, I was trying to sent another patch to fix this issue on source side later. And you did that.

On the other hand, since we support splitting PUD(and PMD), I guess we should make it do the right work just in case will meet such case later.

No, we should not split them.

Thanks

Yinghai

#4 lore raw

PATCH 1/1 x86: Fix adjust_range_size_mask calling position

Commit 8d57470d cause a kernel panic while setting mem=2G.

     [mem 0x00000000-0x000fffff] page 4k
     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G
     [mem 0x00100000-0x001fffff] page 4k
     [mem 0x00200000-0x7bffffff] page 2M

but for last entry we should have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 1G

Actually there is bug about calling sequence for adjust_range_page_size_mask(). Merge first will make adjust to 1g for second partial 1g range fail.

Fix that by calling adjust_range_size_mask before merging mem_range with same page size.

We need this one for v3.9 stable.

---
 arch/x86/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/mm/init.c +3 -3
Index: linux-2.6/arch/x86/mm/init.c
===================================================================
@@ -277,6 +277,9 @@ static int __meminit split_mem_range(str
 	end_pfn = limit_pfn;
 	nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0);
 
+	if (!after_bootmem)
+		adjust_range_page_size_mask(mr, nr_range);
+
 	/* try to merge same page size and continuous */
 	for (i = 0; nr_range > 1 && i < nr_range - 1; i++) {
 		unsigned long old_start;
@@ -291,9 +294,6 @@ static int __meminit split_mem_range(str
 		nr_range--;
 	}
 
-	if (!after_bootmem)
-		adjust_range_page_size_mask(mr, nr_range);
-
 	for (i = 0; i < nr_range; i++)
 		printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
 				mr[i].start, mr[i].end - 1,
#5 lore raw

Re: [PATCH] x86: Fix adjust_range_size_mask calling position

Quoted 5 lines · Yinghai Lu

Actually there is bug about calling sequence for adjust_range_page_size_mask(). Merge first will make adjust to 1g for second partial 1g range fail.

Sorry, I'm not sure I understand what the above paragraph is trying to say.

-hpa

#6 lore raw

Re: [PATCH] x86: Fix adjust_range_size_mask calling position

Quoted 8 lines · H. Peter Anvin
Quoted 5 lines · Yinghai Lu

Actually there is bug about calling sequence for adjust_range_page_size_mask(). Merge first will make adjust to 1g for second partial 1g range fail.

Sorry, I'm not sure I understand what the above paragraph is trying to say.

We merge the continuous range with same page size allow too early. in the case

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M
after merging them, will get
     [mem 0x00200000-0x7bffffff] page 2M
even we can use 1G page to map
     [mem 0x40000000-0x7bffffff]

that will cause problem, because we map

     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G

with 1G page.

so need to adjust page size at first before merge

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M

Yinghai

#7 lore raw

Re: [PATCH] x86: Fix adjust_range_size_mask calling position

Quoted 12 lines · Yinghai Lu
Quoted 2 lines

Sorry, I'm not sure I understand what the above paragraph is trying to say.

We merge the continuous range with same page size allow too early. in the case

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M
after merging them, will get
     [mem 0x00200000-0x7bffffff] page 2M
even we can use 1G page to map
     [mem 0x40000000-0x7bffffff]
Quoted 9 lines

that will cause problem, because we map

     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G

with 1G page.

so need to adjust page size at first before merge

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M

What I meant with that is please update the description so someone uninitiated can follow the description, either now or in 5-10 years.

-hpa

#8 lore raw

PATCH v2 1/1 x86: Fix adjust_range_size_mask calling position

In reply to Yinghai Lu

Commit 8d57470d cause a kernel panic while setting mem=2G.

     [mem 0x00000000-0x000fffff] page 4k
     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G
     [mem 0x00100000-0x001fffff] page 4k
     [mem 0x00200000-0x7bffffff] page 2M

for last entry is not what we want, we should have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 1G

Actually we merge the continuous ranges with same page size too early. in this case, before merging we have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M
after merging them, will get
     [mem 0x00200000-0x7bffffff] page 2M
even we can use 1G page to map
     [mem 0x40000000-0x7bffffff]

that will cause problem, because we already map

     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G

with 1G page.

Fix that by calling adjust_range_size_mask before merging range with same page size.

We need this one for v3.9 stable.

-v2: update change log.

---
 arch/x86/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/mm/init.c +3 -3
Index: linux-2.6/arch/x86/mm/init.c
===================================================================
@@ -277,6 +277,9 @@ static int __meminit split_mem_range(str
 	end_pfn = limit_pfn;
 	nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0);
 
+	if (!after_bootmem)
+		adjust_range_page_size_mask(mr, nr_range);
+
 	/* try to merge same page size and continuous */
 	for (i = 0; nr_range > 1 && i < nr_range - 1; i++) {
 		unsigned long old_start;
@@ -291,9 +294,6 @@ static int __meminit split_mem_range(str
 		nr_range--;
 	}
 
-	if (!after_bootmem)
-		adjust_range_page_size_mask(mr, nr_range);
-
 	for (i = 0; i < nr_range; i++)
 		printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
 				mr[i].start, mr[i].end - 1,
#9 lore raw

Re: [PATCH v2] x86: Fix adjust_range_size_mask calling position

Quoted 1 line · Yinghai Lu

Commit 8d57470d cause a kernel panic while setting mem=2G.

'Commit 8d57470d' is not the standard way of how we refer to upstream commits in changelogs. See commit 5e427ec2d066 as an example for the proper format.

Thanks,

Ingo

#10 lore raw

Re: [PATCH v2] x86: Fix adjust_range_size_mask calling position

In reply to Yinghai Lu
Quoted 10 lines · Yinghai Lu

Commit 8d57470d cause a kernel panic while setting mem=2G.

     [mem 0x00000000-0x000fffff] page 4k
     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G
     [mem 0x00100000-0x001fffff] page 4k
     [mem 0x00200000-0x7bffffff] page 2M

for last entry is not what we want, we should have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 1G

What this changelog does not mention, why does the erroneous entry result in a kernel panic? It looks like a valid range, so a crash is unexpected.

(This is worth explaining, even if the fix for that is in a separate patch.)

Thanks,

Ingo

#11 lore raw

PATCH v3 1/1 x86: Fix adjust_range_size_mask calling position

Commit 8d57470d cause a kernel panic while setting mem=2G.

     [mem 0x00000000-0x000fffff] page 4k
     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G
     [mem 0x00100000-0x001fffff] page 4k
     [mem 0x00200000-0x7bffffff] page 2M

for last entry is not what we want, we should have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 1G

Actually we merge the continuous ranges with same page size too early. in this case, before merging we have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M
after merging them, will get
     [mem 0x00200000-0x7bffffff] page 2M
even we can use 1G page to map
     [mem 0x40000000-0x7bffffff]

that will cause problem, because we already map

     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G

with 1G page, aka [0x40000000-0x7fffffff] is mapped with 1G page already. During phys_pud_init() for [0x40000000-0x7bffffff], it will not reuse existing that pud page, and allocate new one then try to use 2M page to map it instead, as page_size_mask does not include PG_LEVEL_1G. At end will have [7c000000-0x7fffffff] not mapped, loop in phys_pmd_init stop mapping at 0x7bffffff.

That is right behavoir, it maps exact range with exact page size that we ask, and we should explicitly call it to map [7c000000-0x7fffffff] before or after mapping 0x40000000-0x7bffffff.
Anyway we need to make sure ranges' page_size_mask correct and consistent after split_mem_range for each range.

Fix that by calling adjust_range_size_mask before merging range with same page size.

We need this one for v3.9 stable.

-v2: update change log.
-v3: add more explanation why [7c000000-0x7fffffff] is not mapped, and
it causes panic.

---
 arch/x86/mm/init.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/mm/init.c +3 -3
Index: linux-2.6/arch/x86/mm/init.c
===================================================================
@@ -277,6 +277,9 @@ static int __meminit split_mem_range(str
 	end_pfn = limit_pfn;
 	nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0);
 
+	if (!after_bootmem)
+		adjust_range_page_size_mask(mr, nr_range);
+
 	/* try to merge same page size and continuous */
 	for (i = 0; nr_range > 1 && i < nr_range - 1; i++) {
 		unsigned long old_start;
@@ -291,9 +294,6 @@ static int __meminit split_mem_range(str
 		nr_range--;
 	}
 
-	if (!after_bootmem)
-		adjust_range_page_size_mask(mr, nr_range);
-
 	for (i = 0; i < nr_range; i++)
 		printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
 				mr[i].start, mr[i].end - 1,
#12 lore raw

Re: [PATCH v3] x86: Fix adjust_range_size_mask calling position

Quoted 1 line · Yinghai Lu

Commit 8d57470d cause a kernel panic while setting mem=2G.

As Ingo pointed out, don't just refer to a commit by number; you need to include the number as well as the subject line. This is particularly important if git ever switches away from SHA-1.

-hpa

#14 lore raw

[tip:x86/urgent] x86: Fix adjust_range_size_mask calling position

In reply to Yinghai Lu
x86: Fix adjust_range_size_mask calling position
Commit-ID:  7de3d66b1387ddf5a37d9689e5eb8510fb75c765
Gitweb:     http://git.kernel.org/tip/7de3d66b1387ddf5a37d9689e5eb8510fb75c765
Author:     Yinghai Lu <[email protected]>
AuthorDate: Fri, 31 May 2013 08:53:07 -0700
Committer:  H. Peter Anvin <[email protected]>
CommitDate: Fri, 31 May 2013 13:21:32 -0700

x86: Fix adjust_range_size_mask calling position

Commit

8d57470d x86, mm: setup page table in top-down

causes a kernel panic while setting mem=2G.

     [mem 0x00000000-0x000fffff] page 4k
     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G
     [mem 0x00100000-0x001fffff] page 4k
     [mem 0x00200000-0x7bffffff] page 2M

for last entry is not what we want, we should have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 1G

Actually we merge the continuous ranges with same page size too early. in this case, before merging we have

     [mem 0x00200000-0x3fffffff] page 2M
     [mem 0x40000000-0x7bffffff] page 2M
after merging them, will get
     [mem 0x00200000-0x7bffffff] page 2M
even we can use 1G page to map
     [mem 0x40000000-0x7bffffff]

that will cause problem, because we already map

     [mem 0x7fe00000-0x7fffffff] page 1G
     [mem 0x7c000000-0x7fdfffff] page 1G

with 1G page, aka [0x40000000-0x7fffffff] is mapped with 1G page already. During phys_pud_init() for [0x40000000-0x7bffffff], it will not reuse existing that pud page, and allocate new one then try to use 2M page to map it instead, as page_size_mask does not include PG_LEVEL_1G. At end will have [7c000000-0x7fffffff] not mapped, loop in phys_pmd_init stop mapping at 0x7bffffff.

That is right behavoir, it maps exact range with exact page size that we ask, and we should explicitly call it to map [7c000000-0x7fffffff] before or after mapping 0x40000000-0x7bffffff.
Anyway we need to make sure ranges' page_size_mask correct and consistent after split_mem_range for each range.

Fix that by calling adjust_range_size_mask before merging range with same page size.

-v2: update change log.
-v3: add more explanation why [7c000000-0x7fffffff] is not mapped, and
it causes panic.

---
 arch/x86/mm/init.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/x86/mm/init.c +3 -3
index eaac174..1f34e92 100644
@@ -277,6 +277,9 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range,
 	end_pfn = limit_pfn;
 	nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0);
 
+	if (!after_bootmem)
+		adjust_range_page_size_mask(mr, nr_range);
+
 	/* try to merge same page size and continuous */
 	for (i = 0; nr_range > 1 && i < nr_range - 1; i++) {
 		unsigned long old_start;
@@ -291,9 +294,6 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range,
 		nr_range--;
 	}
 
-	if (!after_bootmem)
-		adjust_range_page_size_mask(mr, nr_range);
-
 	for (i = 0; i < nr_range; i++)
 		printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
 				mr[i].start, mr[i].end - 1,