Skip to contentSkip to conversation

Latest GBPAGES patchkit for 2.6.25

PATCH In review Andi Kleen 2 participants 21 messages Jan 29, 2008 – Jan 31, 2008

Subsystems (3)
lore
Share & tools
lore

Andi Kleen

author
#0 lore raw

cover Latest GBPAGES patchkit for 2.6.25

This patchkit implements support for the 1GB pages of AMD Fam10h CPUs in the kernel direct mapping.

Change to previous versions:
- Ported to latest change_page_attr
- kexec now works again
- Ported to latest git-x86
- Minor cleanups.

I believe this patchkit is ready for the 2.6.25 merge.

-Andi

author
#1 lore raw

PATCH 1/9 Handle kernel near memory hole in clear_kernel_mapping

This was a long standing obscure problem in the relocatable kernel. The AMD GART driver needs to unmap part of the GART in the kernel direct mapping to prevent cache corruption. With the relocatable kernel it is in theory possible that the separate kernel text mapping straddles that area too.

Normally it should not happen because GART tends to be >= 2GB, and the kernel is normally not loaded that high, but it is possible in theory.

Teach clear_kernel_mapping() about this case.

This will become more important once the kernel mapping uses 1GB pages.

---
 arch/x86/mm/init_64.c |   25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)
arch/x86/mm/init_64.c +24 -1
Index: linux/arch/x86/mm/init_64.c
===================================================================
@@ -438,7 +438,8 @@ void __init paging_init(void)
  * address and size must be aligned to 2MB boundaries.
  * Does nothing when the mapping doesn't exist.
  */
-void __init clear_kernel_mapping(unsigned long address, unsigned long size)
+static void __init
+__clear_kernel_mapping(unsigned long address, unsigned long size)
 {
 	unsigned long end = address + size;
 
@@ -475,6 +476,28 @@ void __init clear_kernel_mapping(unsigne
 	__flush_tlb_all();
 }
 
+#define overlaps(as, ae, bs, be) ((ae) >= (bs) && (as) <= (be))
+
+void __init clear_kernel_mapping(unsigned long address, unsigned long size)
+{
+	int sh = PMD_SHIFT;
+	unsigned long kernel = __pa(__START_KERNEL_map);
+
+	/*
+	 * Note that we cannot unmap the kernel itself because the unmapped
+	 * holes here are always at least 2MB aligned.
+	 * This just applies to the trailing areas of the 40MB kernel mapping.
+	 */
+	if (overlaps(kernel >> sh, (kernel + KERNEL_TEXT_SIZE) >> sh,
+			__pa(address) >> sh, __pa(address + size) >> sh)) {
+		printk(KERN_WARNING
+			"Kernel mapping at %lx within 2MB of memory hole\n",
+				kernel);
+		__clear_kernel_mapping(__START_KERNEL_map+__pa(address), size);
+	}
+	__clear_kernel_mapping(address, size);
+}
+
 /*
  * Memory hotplug specific functions
  */
#2 lore raw

Re: [PATCH] [1/9] Handle kernel near memory hole in clear_kernel_mapping

Quoted 7 lines · Andi Kleen

This was a long standing obscure problem in the relocatable kernel. The AMD GART driver needs to unmap part of the GART in the kernel direct mapping to prevent cache corruption. With the relocatable kernel it is in theory possible that the separate kernel text mapping straddles that area too.

Normally it should not happen because GART tends to be >= 2GB, and the kernel is normally not loaded that high, but it is possible in theory.

This smells fishy.

Quoted 1 line

....

Quoted 2 lines

+#define overlaps(as, ae, bs, be) ((ae) >= (bs) && (as) <= (be))

inline function please and a bit more intuituive arrangement of arguments.

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 488

Quoted 8 lines
arch/x86/mm/init_64.c +8 -0
+void __init clear_kernel_mapping(unsigned long address, unsigned long size)
+{
+	int sh = PMD_SHIFT;
+	unsigned long kernel = __pa(__START_KERNEL_map);
+
+	/*
+	 * Note that we cannot unmap the kernel itself because the unmapped
+	 * holes here are always at least 2MB aligned.

That's not enforced. The unmap code just does not split pages.

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 489

Quoted 1 line
arch/x86/mm/init_64.c +1 -0
+	 * This just applies to the trailing areas of the 40MB kernel mapping.

How is this ensured, that it only affects the end of the 40MB mapping ?

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 492

Quoted 3 lines
arch/x86/mm/init_64.c +3 -0
+	 */
+	if (overlaps(kernel >> sh, (kernel + KERNEL_TEXT_SIZE) >> sh,
+			__pa(address) >> sh, __pa(address + size) >> sh)) {

This checks:

     (kernel_end + 1) >= gart_start && kernel_start <= gart_end
One off error:  kernel + KERNEL_TEXT_SIZE 
needs to be:    kernel + KERNEL_TEXT_SIZE - 1

Also there is no sanity check, whether the area is inside real kernel text.

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 496

Quoted 6 lines
arch/x86/mm/init_64.c +4 -0
+		printk(KERN_WARNING
+			"Kernel mapping at %lx within 2MB of memory hole\n",
+				kernel);
 
 
+		__clear_kernel_mapping(__START_KERNEL_map+__pa(address), size);

Doh! This is unmapping the wrong place. According to __phys_addr():

     paddr = vaddr - __START_KERNEL_map + phys_base;

Transformation:

     vaddr = padd + __START_KERNEL_map - phys_base;

Sigh. The clear_kernel_mapping() functionality is just another wreckaged implementation of CPA.

We really should use CPA and have a function to mark entries not present. Actually the DEBUG_PAGEALLOC code has one already. There are sanity checks in the CPA code as well, which can be extended to cover the GART unmap in a safe way.

Thanks,
tglx

author
#3 lore raw

Re: [PATCH] [1/9] Handle kernel near memory hole in clear_kernel_mapping

Quoted 4 lines
Quoted 1 line

+#define overlaps(as, ae, bs, be) ((ae) >= (bs) && (as) <= (be))

inline function please and a bit more intuituive arrangement of arguments.

Which one do you prefer? (to be honest the current one is "intuitive" to me)

Quoted 11 lines

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 488

Quoted 9 lines
arch/x86/mm/init_64.c +8 -0
+void __init clear_kernel_mapping(unsigned long address, unsigned long size)
+{
+	int sh = PMD_SHIFT;
+	unsigned long kernel = __pa(__START_KERNEL_map);
+
+	/*
+	 * Note that we cannot unmap the kernel itself because the unmapped
+	 * holes here are always at least 2MB aligned.

That's not enforced. The unmap code just does not split pages.

It is -- there are BUG_ONs for this in __clear_kernel_mapping

Quoted 4 lines

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 489

Quoted 2 lines
arch/x86/mm/init_64.c +1 -0
+	 * This just applies to the trailing areas of the 40MB kernel mapping.

How is this ensured, that it only affects the end of the 40MB mapping ?

It is enforced in the callers (actually there is only a single caller -- the GART code ) by not calling it overlapping for the kernel itself. Given that could be checked too, but that would be probably overkill for an internal function.

Quoted 11 lines

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 492

Quoted 4 lines
arch/x86/mm/init_64.c +3 -0
+	 */
+	if (overlaps(kernel >> sh, (kernel + KERNEL_TEXT_SIZE) >> sh,
+			__pa(address) >> sh, __pa(address + size) >> sh)) {

This checks:

     (kernel_end + 1) >= gart_start && kernel_start <= gart_end
One off error:  kernel + KERNEL_TEXT_SIZE 
needs to be:    kernel + KERNEL_TEXT_SIZE - 1

Ok.

Quoted 2 lines

Also there is no sanity check, whether the area is inside real kernel text.

Hmm I can add one, but if that happens the caller is likely seriously confused and will likely cause other problems anyways.

I don't think it can happen for the GART code which is currently the only caller.

Quoted 11 lines

arch/x86/mm/init_64.c void __init clear_kernel_mapping(unsigne · line 496

Quoted 7 lines
arch/x86/mm/init_64.c +4 -0
+		printk(KERN_WARNING
+			"Kernel mapping at %lx within 2MB of memory hole\n",
+				kernel);
 
 
+		__clear_kernel_mapping(__START_KERNEL_map+__pa(address), size);

Doh! This is unmapping the wrong place. According to __phys_addr():

     paddr = vaddr - __START_KERNEL_map + phys_base;

Hmm true -- that will only affect relocatable kernels, but for those it's wrong. Given that the patch was supposed to fix a case that only happens relocatable kernels that's quite ironic :)

Actually thinking about it again you can just drop it for now. It is orthogonal to gbpages. I think I added it to the series when I was planning to do kernel mapping as GB pages, but that turned out to be a bad idea anyways.

Thanks for the review,

-Andi

author
#4 lore raw

PATCH 2/9 GBPAGES: Add feature macros for the gbpages cpuid bit

In reply to Andi Kleen
---
 include/asm-x86/cpufeature.h |    2 ++
 1 file changed, 2 insertions(+)
include/asm-x86/cpufeature.h +2 -0
Index: linux/include/asm-x86/cpufeature.h
===================================================================
@@ -49,6 +49,7 @@
 #define X86_FEATURE_MP		(1*32+19) /* MP Capable. */
 #define X86_FEATURE_NX		(1*32+20) /* Execute Disable */
 #define X86_FEATURE_MMXEXT	(1*32+22) /* AMD MMX extensions */
+#define X86_FEATURE_GBPAGES	(1*32+26) /* GB pages */
 #define X86_FEATURE_RDTSCP	(1*32+27) /* RDTSCP */
 #define X86_FEATURE_LM		(1*32+29) /* Long Mode (x86-64) */
 #define X86_FEATURE_3DNOWEXT	(1*32+30) /* AMD 3DNow! extensions */
@@ -175,6 +176,7 @@
 #define cpu_has_pebs		boot_cpu_has(X86_FEATURE_PEBS)
 #define cpu_has_clflush		boot_cpu_has(X86_FEATURE_CLFLSH)
 #define cpu_has_bts		boot_cpu_has(X86_FEATURE_BTS)
+#define cpu_has_gbpages 	boot_cpu_has(X86_FEATURE_GBPAGES)
 
 #if defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_64)
 # define cpu_has_invlpg		1
author
#5 lore raw

PATCH 3/9 GBPAGES: Split LARGE_PAGE_SIZE/MASK into PUD_PAGE_SIZE/PMD_PAGE_SIZE

In reply to Andi Kleen

Split the existing LARGE_PAGE_SIZE/MASK macro into two new macros PUD_PAGE_SIZE/MASK and PMD_PAGE_SIZE/MASK.

Fix up all callers to use the new names.

---
 arch/x86/boot/compressed/head_64.S |    8 ++++----
 arch/x86/kernel/head_64.S          |    4 ++--
 arch/x86/kernel/pci-gart_64.c      |    2 +-
 arch/x86/mm/init_64.c              |    6 +++---
 arch/x86/mm/pageattr.c             |    2 +-
 include/asm-x86/page.h             |    4 ++--
 include/asm-x86/page_32.h          |    4 ++++
 include/asm-x86/page_64.h          |    3 +++
 8 files changed, 20 insertions(+), 13 deletions(-)
include/asm-x86/page_64.h +3 -0
Index: linux/include/asm-x86/page_64.h
===================================================================
@@ -23,6 +23,9 @@
 #define MCE_STACK 5
 #define N_EXCEPTION_STACKS 5  /* hw limit: 7 */
 
+#define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT)
+#define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1))
+
 #define __PAGE_OFFSET           _AC(0xffff810000000000, UL)
 
 #define __PHYSICAL_START	CONFIG_PHYSICAL_START
arch/x86/boot/compressed/head_64.S +4 -4
Index: linux/arch/x86/boot/compressed/head_64.S
===================================================================
@@ -80,8 +80,8 @@ startup_32:
 
 #ifdef CONFIG_RELOCATABLE
 	movl	%ebp, %ebx
-	addl	$(LARGE_PAGE_SIZE -1), %ebx
-	andl	$LARGE_PAGE_MASK, %ebx
+	addl	$(PMD_PAGE_SIZE -1), %ebx
+	andl	$PMD_PAGE_MASK, %ebx
 #else
 	movl	$CONFIG_PHYSICAL_START, %ebx
 #endif
@@ -220,8 +220,8 @@ ENTRY(startup_64)
 	/* Start with the delta to where the kernel will run at. */
 #ifdef CONFIG_RELOCATABLE
 	leaq	startup_32(%rip) /* - $startup_32 */, %rbp
-	addq	$(LARGE_PAGE_SIZE - 1), %rbp
-	andq	$LARGE_PAGE_MASK, %rbp
+	addq	$(PMD_PAGE_SIZE - 1), %rbp
+	andq	$PMD_PAGE_MASK, %rbp
 	movq	%rbp, %rbx
 #else
 	movq	$CONFIG_PHYSICAL_START, %rbp
arch/x86/kernel/pci-gart_64.c +1 -1
Index: linux/arch/x86/kernel/pci-gart_64.c
===================================================================
@@ -501,7 +501,7 @@ static __init unsigned long check_iommu_
 	}
 
 	a = aper + iommu_size;
-	iommu_size -= round_up(a, LARGE_PAGE_SIZE) - a;
+	iommu_size -= round_up(a, PMD_PAGE_SIZE) - a;
 
 	if (iommu_size < 64*1024*1024) {
 		printk(KERN_WARNING
arch/x86/kernel/head_64.S +2 -2
Index: linux/arch/x86/kernel/head_64.S
===================================================================
@@ -63,7 +63,7 @@ startup_64:
 
 	/* Is the address not 2M aligned? */
 	movq	%rbp, %rax
-	andl	$~LARGE_PAGE_MASK, %eax
+	andl	$~PMD_PAGE_MASK, %eax
 	testl	%eax, %eax
 	jnz	bad_address
 
@@ -88,7 +88,7 @@ startup_64:
 
 	/* Add an Identity mapping if I am above 1G */
 	leaq	_text(%rip), %rdi
-	andq	$LARGE_PAGE_MASK, %rdi
+	andq	$PMD_PAGE_MASK, %rdi
 
 	movq	%rdi, %rax
 	shrq	$PUD_SHIFT, %rax
arch/x86/mm/init_64.c +3 -3
Index: linux/arch/x86/mm/init_64.c
===================================================================
@@ -443,10 +443,10 @@ __clear_kernel_mapping(unsigned long add
 {
 	unsigned long end = address + size;
 
-	BUG_ON(address & ~LARGE_PAGE_MASK);
-	BUG_ON(size & ~LARGE_PAGE_MASK);
+	BUG_ON(address & ~PMD_PAGE_MASK);
+	BUG_ON(size & ~PMD_PAGE_MASK);
 
-	for (; address < end; address += LARGE_PAGE_SIZE) {
+	for (; address < end; address += PMD_PAGE_SIZE) {
 		pgd_t *pgd = pgd_offset_k(address);
 		pud_t *pud;
 		pmd_t *pmd;
include/asm-x86/page_32.h +4 -0
Index: linux/include/asm-x86/page_32.h
===================================================================
@@ -13,6 +13,10 @@
  */
 #define __PAGE_OFFSET		_AC(CONFIG_PAGE_OFFSET, UL)
 
+/* Eventually 32bit should be moved over to the new names too */
+#define LARGE_PAGE_SIZE PMD_PAGE_SIZE
+#define LARGE_PAGE_MASK PMD_PAGE_MASK
+
 #ifdef CONFIG_X86_PAE
 #define __PHYSICAL_MASK_SHIFT	36
 #define __VIRTUAL_MASK_SHIFT	32
include/asm-x86/page.h +2 -2
Index: linux/include/asm-x86/page.h
===================================================================
@@ -13,8 +13,8 @@
 #define PHYSICAL_PAGE_MASK	(PAGE_MASK & __PHYSICAL_MASK)
 #define PTE_MASK		(_AT(long, PHYSICAL_PAGE_MASK))
 
-#define LARGE_PAGE_SIZE		(_AC(1,UL) << PMD_SHIFT)
-#define LARGE_PAGE_MASK		(~(LARGE_PAGE_SIZE-1))
+#define PMD_PAGE_SIZE		(_AC(1, UL) << PMD_SHIFT)
+#define PMD_PAGE_MASK		(~(PMD_PAGE_SIZE-1))
 
 #define HPAGE_SHIFT		PMD_SHIFT
 #define HPAGE_SIZE		(_AC(1,UL) << HPAGE_SHIFT)
arch/x86/mm/pageattr.c +1 -1
Index: linux/arch/x86/mm/pageattr.c
===================================================================
@@ -218,7 +218,7 @@ static int split_large_page(pte_t *kpte,
 	}
 
 	address = __pa(address);
-	addr = address & LARGE_PAGE_MASK;
+	addr = address & PMD_PAGE_MASK;
 	pbase = (pte_t *)page_address(base);
 #ifdef CONFIG_X86_32
 	paravirt_alloc_pt(&init_mm, page_to_pfn(base));
#6 lore raw

Re: [PATCH] [3/9] GBPAGES: Split LARGE_PAGE_SIZE/MASK into PUD_PAGE_SIZE/PMD_PAGE_SIZE

Quoted 2 lines · Andi Kleen

Split the existing LARGE_PAGE_SIZE/MASK macro into two new macros PUD_PAGE_SIZE/MASK and PMD_PAGE_SIZE/MASK.

This is not splitting anything. It adds PUD_PAGE_xxx and renames LARGE_PAGE_XXX to PMD_PAGE_XXX.

Please split the patch into one, which does the rename and one which adds the new defines.

include/asm-x86/page_32.h line 18

Quoted 4 lines
include/asm-x86/page_32.h +3 -0
+/* Eventually 32bit should be moved over to the new names too */
+#define LARGE_PAGE_SIZE PMD_PAGE_SIZE
+#define LARGE_PAGE_MASK PMD_PAGE_MASK

Grep tells, that there is no remaining user of LARGE_PAGE_XXX, so this is useless. Even if there would be users we'd better fix them right away.

Thanks,
tglx

author
#7 lore raw

PATCH 4/9 Add pgtable accessor functions for GB pages

In reply to Andi Kleen
---
 include/asm-x86/pgtable_32.h |    2 ++
 include/asm-x86/pgtable_64.h |    6 ++++++
 2 files changed, 8 insertions(+)
include/asm-x86/pgtable_64.h +6 -0
Index: linux/include/asm-x86/pgtable_64.h
===================================================================
@@ -199,6 +199,12 @@ static inline unsigned long pmd_bad(pmd_
 #define pud_offset(pgd, address) ((pud_t *) pgd_page_vaddr(*(pgd)) + pud_index(address))
 #define pud_present(pud) (pud_val(pud) & _PAGE_PRESENT)
 
+static inline int pud_large(pud_t pte)
+{
+	return (pud_val(pte) & (_PAGE_PSE|_PAGE_PRESENT)) ==
+		(_PAGE_PSE|_PAGE_PRESENT);
+}
+
 /* PMD  - Level 2 access */
 #define pmd_page_vaddr(pmd) ((unsigned long) __va(pmd_val(pmd) & PTE_MASK))
 #define pmd_page(pmd)		(pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT))
include/asm-x86/pgtable_32.h +2 -0
Index: linux/include/asm-x86/pgtable_32.h
===================================================================
@@ -148,6 +148,8 @@ static inline void clone_pgd_range(pgd_t
  */
 #define pgd_offset_k(address) pgd_offset(&init_mm, address)
 
+static inline int pud_large(pud_t pud) { return 0; }
+
 /*
  * the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD]
  *
author
#8 lore raw

PATCH 5/9 GBPAGES: Support gbpages in pagetable dump

In reply to Andi Kleen
---
 arch/x86/mm/fault.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/mm/fault.c +2 -1
Index: linux/arch/x86/mm/fault.c
===================================================================
@@ -240,7 +240,8 @@ void dump_pagetable(unsigned long addres
 	pud = pud_offset(pgd, address);
 	if (bad_address(pud)) goto bad;
 	printk("PUD %lx ", pud_val(*pud));
-	if (!pud_present(*pud))	goto ret;
+	if (!pud_present(*pud) || pud_large(*pud))
+		goto ret;
 
 	pmd = pmd_offset(pud, address);
 	if (bad_address(pmd)) goto bad;
author
#9 lore raw

PATCH 6/9 GBPAGES: Add gbpages support to lookup_address

In reply to Andi Kleen
---
 arch/x86/mm/pageattr.c    |    5 +++++
 include/asm-x86/pgtable.h |    1 +
 2 files changed, 6 insertions(+)
arch/x86/mm/pageattr.c +5 -0
Index: linux/arch/x86/mm/pageattr.c
===================================================================
@@ -152,9 +152,14 @@ pte_t *lookup_address(unsigned long addr
 
 	if (pgd_none(*pgd))
 		return NULL;
+
+	*level = PG_LEVEL_1G;
 	pud = pud_offset(pgd, address);
 	if (pud_none(*pud))
 		return NULL;
+	if (pud_large(*pud))
+		return (pte_t *)pud;
+
 	pmd = pmd_offset(pud, address);
 	if (pmd_none(*pmd))
 		return NULL;
include/asm-x86/pgtable.h +1 -0
Index: linux/include/asm-x86/pgtable.h
===================================================================
@@ -242,6 +242,7 @@ enum {
 	PG_LEVEL_NONE,
 	PG_LEVEL_4K,
 	PG_LEVEL_2M,
+	PG_LEVEL_1G,
 };
 
 /*
#10 lore raw

Re: [PATCH] [6/9] GBPAGES: Add gbpages support to lookup_address

Quoted 13 lines · Andi Kleen
---
 arch/x86/mm/pageattr.c    |    5 +++++
 include/asm-x86/pgtable.h |    1 +
 2 files changed, 6 insertions(+)

Index: linux/arch/x86/mm/pageattr.c
===================================================================

arch/x86/mm/pageattr.c pte_t *lookup_address(unsigned long addr · line 159

Quoted 8 lines
arch/x86/mm/pageattr.c +2 -0
@@ -152,9 +152,14 @@ pte_t *lookup_address(unsigned long addr
 
 	if (pgd_none(*pgd))
 		return NULL;
+
+	*level = PG_LEVEL_1G;
 	pud = pud_offset(pgd, address);
 	if (pud_none(*pud))
 		return NULL;

Please move the level update here, so it is consistent with PMD.

Thanks,

tglx

author
#11 lore raw

PATCH 7/9 Add an option to disable direct mapping gbpages and a global variable

In reply to Andi Kleen
---
 Documentation/x86_64/boot-options.txt |    3 +++
 arch/x86/mm/init_64.c                 |   12 ++++++++++++
 include/asm-x86/pgtable_64.h          |    2 ++
 3 files changed, 17 insertions(+)
arch/x86/mm/init_64.c +12 -0
Index: linux/arch/x86/mm/init_64.c
===================================================================
@@ -53,6 +53,18 @@ static unsigned long dma_reserve __initd
 
 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 
+int direct_gbpages;
+
+static int __init parse_direct_gbpages(char *arg)
+{
+	if (!strcmp(arg, "off")) {
+		direct_gbpages = -1;
+		return 0;
+	}
+	return -1;
+}
+early_param("direct_gbpages", parse_direct_gbpages);
+
 /*
  * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the
  * physical space so we can cache the place of the first one and move
include/asm-x86/pgtable_64.h +2 -0
Index: linux/include/asm-x86/pgtable_64.h
===================================================================
@@ -239,6 +239,8 @@ static inline int pud_large(pud_t pte)
 
 #define update_mmu_cache(vma,address,pte) do { } while (0)
 
+extern int direct_gbpages;
+
 /* Encode and de-code a swap entry */
 #define __swp_type(x)			(((x).val >> 1) & 0x3f)
 #define __swp_offset(x)			((x).val >> 8)
Documentation/x86_64/boot-options.txt +3 -0
Index: linux/Documentation/x86_64/boot-options.txt
===================================================================
@@ -307,3 +307,6 @@ Debugging
 			stuck (default)
 
 Miscellaneous
+
+	direct_gbpages=off
+		Do not use GB pages for kernel direct mapping.
#12 lore raw

Re: [PATCH] [7/9] Add an option to disable direct mapping gbpages and a global variable

arch/x86/mm/init_64.c static unsigned long dma_reserve __initd · line 66

Quoted 11 lines · Andi Kleen
arch/x86/mm/init_64.c +11 -0
+int direct_gbpages;
+
+static int __init parse_direct_gbpages(char *arg)
+{
+	if (!strcmp(arg, "off")) {
+		direct_gbpages = -1;
+		return 0;
+	}
+	return -1;
+}
+early_param("direct_gbpages", parse_direct_gbpages);

Can we please use simple boolean logic ? This 0, -1, 1 magic is more than confusing.

Thanks,
tglx

author
#13 lore raw

Re: [PATCH] [7/9] Add an option to disable direct mapping gbpages and a global variable

Quoted 15 lines · Thomas Gleixner

arch/x86/mm/init_64.c static unsigned long dma_reserve __initd · line 66

Quoted 11 lines · Andi Kleen
arch/x86/mm/init_64.c +11 -0
+int direct_gbpages;
+
+static int __init parse_direct_gbpages(char *arg)
+{
+	if (!strcmp(arg, "off")) {
+		direct_gbpages = -1;
+		return 0;
+	}
+	return -1;
+}
+early_param("direct_gbpages", parse_direct_gbpages);

Can we please use simple boolean logic ? This 0, -1, 1 magic is more than confusing.

I did it this way to later allow forcing gbpages. This actually makes sense together with debug pagealloc which right now forces them to be off.

The only bit missing for that is a check for direct_gbpages=on

-Andi

#14 lore raw

Re: [PATCH] [7/9] Add an option to disable direct mapping gbpages and a global variable

Quoted 17 lines · Andi Kleen
Quoted 15 lines · Thomas Gleixner

arch/x86/mm/init_64.c static unsigned long dma_reserve __initd · line 66

Quoted 11 lines · Andi Kleen
arch/x86/mm/init_64.c +11 -0
+int direct_gbpages;
+
+static int __init parse_direct_gbpages(char *arg)
+{
+	if (!strcmp(arg, "off")) {
+		direct_gbpages = -1;
+		return 0;
+	}
+	return -1;
+}
+early_param("direct_gbpages", parse_direct_gbpages);

Can we please use simple boolean logic ? This 0, -1, 1 magic is more than confusing.

Quoted 5 lines

I did it this way to later allow forcing gbpages. This actually makes sense together with debug pagealloc which right now forces them to be off.

The only bit missing for that is a check for direct_gbpages=on

Fair enough. But please use some sensible constants for that. Otherwise it's just annoying to figure out what 0, -1, 1 and later on 2 means.

Thanks,
tglx

author
#15 lore raw

PATCH 8/9 GBPAGES: Implement gbpages support in change_page_attr()

In reply to Andi Kleen
---
 arch/x86/mm/pageattr.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)
arch/x86/mm/pageattr.c +12 -1
Index: linux/arch/x86/mm/pageattr.c
===================================================================
@@ -203,6 +203,7 @@ static int split_large_page(pte_t *kpte,
 	pte_t *pbase, *tmp;
 	struct page *base;
 	int i, level;
+	unsigned long ps;
 
 #ifdef CONFIG_DEBUG_PAGEALLOC
 	gfp_flags = GFP_ATOMIC;
@@ -224,12 +225,22 @@ static int split_large_page(pte_t *kpte,
 
 	address = __pa(address);
 	addr = address & PMD_PAGE_MASK;
+
+	ps = PAGE_SIZE;
+#ifdef CONFIG_X86_64
+	if (level == PG_LEVEL_1G) {
+		ps = PMD_PAGE_SIZE;
+		pgprot_val(ref_prot) |= _PAGE_PSE;
+		addr &= PUD_PAGE_MASK;
+	}
+#endif
+
 	pbase = (pte_t *)page_address(base);
 #ifdef CONFIG_X86_32
 	paravirt_alloc_pt(&init_mm, page_to_pfn(base));
 #endif
 
-	for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE)
+	for (i = 0; i < PTRS_PER_PTE; i++, addr += ps)
 		set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, ref_prot));
 
 	/*
author
#16 lore raw

PATCH 9/9 GBPAGES: Do kernel direct mapping at boot using GB pages

In reply to Andi Kleen

This should decrease TLB pressure because the kernel will need less TLB faults for its own data access.

Only done for 64bit because i386 does not support GB page tables.

This only applies to the data portion of the direct mapping; the kernel text mapping stays with 2MB pages because the AMD Fam10h microarchitecture does not support GB ITLBs and AMD recommends against using GB mappings for code.

Can be disabled with direct_gbpages=off

---
 arch/x86/mm/init_64.c |   64 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 55 insertions(+), 9 deletions(-)
arch/x86/mm/init_64.c +55 -9
Index: linux/arch/x86/mm/init_64.c
===================================================================
@@ -279,13 +279,20 @@ __meminit void early_iounmap(void *addr,
 	__flush_tlb_all();
 }
 
+static unsigned long direct_entry(unsigned long paddr)
+{
+	unsigned long entry;
+	entry = __PAGE_KERNEL_LARGE|paddr;
+	entry &= __supported_pte_mask;
+	return entry;
+}
+
 static void __meminit
 phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end)
 {
 	int i = pmd_index(address);
 
 	for (; i < PTRS_PER_PMD; i++, address += PMD_SIZE) {
-		unsigned long entry;
 		pmd_t *pmd = pmd_page + pmd_index(address);
 
 		if (address >= end) {
@@ -299,9 +306,7 @@ phys_pmd_init(pmd_t *pmd_page, unsigned 
 		if (pmd_val(*pmd))
 			continue;
 
-		entry = __PAGE_KERNEL_LARGE|_PAGE_GLOBAL|address;
-		entry &= __supported_pte_mask;
-		set_pmd(pmd, __pmd(entry));
+		set_pmd(pmd, __pmd(direct_entry(address)));
 	}
 }
 
@@ -335,7 +340,13 @@ phys_pud_init(pud_t *pud_page, unsigned 
 		}
 
 		if (pud_val(*pud)) {
-			phys_pmd_update(pud, addr, end);
+			if (!pud_large(*pud))
+				phys_pmd_update(pud, addr, end);
+			continue;
+		}
+
+		if (direct_gbpages > 0) {
+			set_pud(pud, __pud(direct_entry(addr)));
 			continue;
 		}
 
@@ -356,9 +367,11 @@ static void __init find_early_table_spac
 	unsigned long puds, pmds, tables, start;
 
 	puds = (end + PUD_SIZE - 1) >> PUD_SHIFT;
-	pmds = (end + PMD_SIZE - 1) >> PMD_SHIFT;
-	tables = round_up(puds * sizeof(pud_t), PAGE_SIZE) +
-		 round_up(pmds * sizeof(pmd_t), PAGE_SIZE);
+	tables = round_up(puds * sizeof(pud_t), PAGE_SIZE);
+	if (!direct_gbpages) {
+		pmds = (end + PMD_SIZE - 1) >> PMD_SHIFT;
+		tables += round_up(pmds * sizeof(pmd_t), PAGE_SIZE);
+	}
 
 	/*
 	 * RED-PEN putting page tables only on node 0 could
@@ -378,6 +391,20 @@ static void __init find_early_table_spac
 		(table_start << PAGE_SHIFT) + tables);
 }
 
+static void init_gbpages(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC
+	/* debug pagealloc causes too much recursion with gbpages */
+	if (direct_gbpages == 0)
+		return;
+#endif
+	if (direct_gbpages >= 0 && cpu_has_gbpages) {
+		printk(KERN_INFO "Using GB pages for direct mapping\n");
+		direct_gbpages = 1;
+	} else
+		direct_gbpages = 0;
+}
+
 /*
  * Setup the direct mapping of the physical memory at PAGE_OFFSET.
  * This runs before bootmem is initialized and gets pages directly from
@@ -396,8 +423,10 @@ void __init_refok init_memory_mapping(un
 	 * memory mapped. Unfortunately this is done currently before the
 	 * nodes are discovered.
 	 */
-	if (!after_bootmem)
+	if (!after_bootmem) {
+		init_gbpages();
 		find_early_table_space(end);
+	}
 
 	start = (unsigned long)__va(start);
 	end = (unsigned long)__va(end);
@@ -444,6 +473,21 @@ void __init paging_init(void)
 }
 #endif
 
+static void split_gb_page(pud_t *pud, unsigned long paddr)
+{
+	int i;
+	pmd_t *pmd;
+	struct page *p = alloc_page(GFP_KERNEL);
+	if (!p)
+		return;
+
+	paddr &= PUD_PAGE_MASK;
+	pmd = page_address(p);
+	for (i = 0; i < PTRS_PER_PTE; i++, paddr += PMD_PAGE_SIZE)
+		pmd[i] = __pmd(direct_entry(paddr));
+	pud_populate(NULL, pud, pmd);
+}
+
 /*
  * Unmap a kernel mapping if it exists. This is useful to avoid
  * prefetches from the CPU leading to inconsistent cache lines.
@@ -467,6 +511,8 @@ __clear_kernel_mapping(unsigned long add
 			continue;
 
 		pud = pud_offset(pgd, address);
+		if (pud_large(*pud))
+			split_gb_page(pud, __pa(address));
 		if (pud_none(*pud))
 			continue;
 
#17 lore raw

Re: [PATCH] [9/9] GBPAGES: Do kernel direct mapping at boot using GB pages

Quoted 2 lines · Andi Kleen

+static unsigned long direct_entry(unsigned long paddr)

Please use a more sensible function name. This one has no association with the functionality at all.

arch/x86/mm/init_64.c __meminit void early_iounmap(void *addr, · line 284

Quoted 2 lines
arch/x86/mm/init_64.c +2 -0
+{
+	unsigned long entry;

New line please

Quoted 8 lines
arch/x86/mm/init_64.c +7 -0
+	entry = __PAGE_KERNEL_LARGE|paddr;
+	entry &= __supported_pte_mask;
+	return entry;
+}
 
+static void init_gbpages(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC

arch/x86/mm/init_64.c static void __init find_early_table_spac · line 406

Quoted 10 lines
arch/x86/mm/init_64.c +10 -0
+	/* debug pagealloc causes too much recursion with gbpages */
+	if (direct_gbpages == 0)
+		return;
+#endif
+	if (direct_gbpages >= 0 && cpu_has_gbpages) {
+		printk(KERN_INFO "Using GB pages for direct mapping\n");
+		direct_gbpages = 1;
+	} else
+		direct_gbpages = 0;
+}

Please use simple boolean logic. gbpages are either enabled or disabled.

Quoted 19 lines
arch/x86/mm/init_64.c +15 -0
+static void split_gb_page(pud_t *pud, unsigned long paddr)
+{
+	int i;
+	pmd_t *pmd;
+	struct page *p = alloc_page(GFP_KERNEL);
+	if (!p)
+		return;
+
+	paddr &= PUD_PAGE_MASK;
+	pmd = page_address(p);
+	for (i = 0; i < PTRS_PER_PTE; i++, paddr += PMD_PAGE_SIZE)
+		pmd[i] = __pmd(direct_entry(paddr));
+	pud_populate(NULL, pud, pmd);
+}
+
 /*
  * Unmap a kernel mapping if it exists. This is useful to avoid
  * prefetches from the CPU leading to inconsistent cache lines.

arch/x86/mm/init_64.c __clear_kernel_mapping(unsigned long add · line 517

Quoted 7 lines
arch/x86/mm/init_64.c +2 -0
@@ -467,6 +511,8 @@ __clear_kernel_mapping(unsigned long add
 			continue;
 
 		pud = pud_offset(pgd, address);
+		if (pud_large(*pud))
+			split_gb_page(pud, __pa(address));
 		if (pud_none(*pud))
 			continue;

As I said before, this needs to use CPA and not implement another variant.

Thanks,
tglx

author
#18 lore raw

Re: [PATCH] [9/9] GBPAGES: Do kernel direct mapping at boot using GB pages

Quoted 6 lines · Thomas Gleixner
Quoted 2 lines · Andi Kleen

+static unsigned long direct_entry(unsigned long paddr)

Please use a more sensible function name. This one has no association with the functionality at all.

Can you suggest one? I honestly cannot think of a better one. Its's a "entry" in the "direct" mapping.

Quoted 8 lines

arch/x86/mm/init_64.c static void __init find_early_table_spac · line 406

Quoted 6 lines
arch/x86/mm/init_64.c +6 -0
+	if (direct_gbpages >= 0 && cpu_has_gbpages) {
+		printk(KERN_INFO "Using GB pages for direct mapping\n");
+		direct_gbpages = 1;
+	} else
+		direct_gbpages = 0;
+}

Please use simple boolean logic. gbpages are either enabled or disabled.

It's a fairly standard widely used idiom for command line options because it allows default and forcing. e.g. there can be cases when the kernel disables it, but then it makes sense for the command line option to override it. There is already one case in the kernel that disables it.

Quoted 11 lines

arch/x86/mm/init_64.c __clear_kernel_mapping(unsigned long add · line 517

Quoted 9 lines

* prefetches from the CPU leading to inconsistent cache lines.

arch/x86/mm/init_64.c +2 -0
@@ -467,6 +511,8 @@ __clear_kernel_mapping(unsigned long add
 			continue;
 
 		pud = pud_offset(pgd, address);
+		if (pud_large(*pud))
+			split_gb_page(pud, __pa(address));
 		if (pud_none(*pud))
 			continue;

As I said before, this needs to use CPA and not implement another variant.

Ok, I can switch GART over to CPA, but that will make the patch much more intrusive and a little riskier. Is that ok for you and will it be still considered .25 candidate?

-Andi

#19 lore raw

Re: [PATCH] [9/9] GBPAGES: Do kernel direct mapping at boot using GB pages

Quoted 10 lines · Andi Kleen
Quoted 6 lines · Thomas Gleixner
Quoted 2 lines · Andi Kleen

+static unsigned long direct_entry(unsigned long paddr)

Please use a more sensible function name. This one has no association with the functionality at all.

Can you suggest one? I honestly cannot think of a better one. Its's a "entry" in the "direct" mapping.

Doh, yes :) It just did not trigger.

Quoted 9 lines
Quoted 8 lines

arch/x86/mm/init_64.c static void __init find_early_table_spac · line 406

Quoted 6 lines
arch/x86/mm/init_64.c +6 -0
+	if (direct_gbpages >= 0 && cpu_has_gbpages) {
+		printk(KERN_INFO "Using GB pages for direct mapping\n");
+		direct_gbpages = 1;
+	} else
+		direct_gbpages = 0;
+}

Please use simple boolean logic. gbpages are either enabled or disabled.

Quoted 5 lines

It's a fairly standard widely used idiom for command line options because it allows default and forcing. e.g. there can be cases when the kernel disables it, but then it makes sense for the command line option to override it. There is already one case in the kernel that disables it.

Right, but please use some constants. I was tripping over the

if (direct_gbpages > 0)

somewhere else in the code and it would have been simpler to see something like:

	if (direct_gbpages == GBPAGES_ENABLED)
Quoted 12 lines
Quoted 11 lines

arch/x86/mm/init_64.c __clear_kernel_mapping(unsigned long add · line 517

Quoted 9 lines

* prefetches from the CPU leading to inconsistent cache lines.

arch/x86/mm/init_64.c +2 -0
@@ -467,6 +511,8 @@ __clear_kernel_mapping(unsigned long add
 			continue;
 
 		pud = pud_offset(pgd, address);
+		if (pud_large(*pud))
+			split_gb_page(pud, __pa(address));
 		if (pud_none(*pud))
 			continue;

As I said before, this needs to use CPA and not implement another variant.

Quoted 3 lines

Ok, I can switch GART over to CPA, but that will make the patch much more intrusive and a little riskier. Is that ok for you and will it be still considered .25 candidate?

We have the code in place already and we need to shake out any remaining problems anyway. So using the CPA code would be preferred as it adds another user. Can you give it a try please ?

Thanks,
tglx

author
#20 lore raw

Re: [PATCH] [9/9] GBPAGES: Do kernel direct mapping at boot using GB pages

Quoted 7 lines · Thomas Gleixner
Quoted 5 lines · Andi Kleen
Quoted 4 lines · Thomas Gleixner
Quoted 2 lines · Andi Kleen

+static unsigned long direct_entry(unsigned long paddr)

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

Please use a more sensible function name. This one has no association with the functionality at all.

Can you suggest one? I honestly cannot think of a better one. Its's a "entry" in the "direct" mapping.

Doh, yes :) It just did not trigger.

I got rid of it now by switching over to the standard pte primitives (with the usual casts). It's not prettier, but at least more consistent.

I hope i didn't break the paravirt case though, we'll see.

-Andi