Skip to contentSkip to conversation

Early memory patch, revised

PATCH In review H. Peter Anvin 2 participants 8 messages Feb 24, 2004 – Mar 1, 2004

lore
Share & tools
lore
#0 lore raw

Hi all,

This is the latest version of the i386 early memory cleanup patch. It has the additional advantage that it removes some of the special casing for VISWS -- this is still untested; if you have access to a VISWS *please* test this out.

The main difference other than the VISWS code is that it always sets up the GDT. I agree with Eric this is a lot cleaner.

-hpa

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/boot/tools/build.c,v retrieving revision 1.4

arch/i386/boot/tools/build.c +2 -4
diff -u -r1.4 build.c
@@ -150,10 +150,8 @@
 	sz = sb.st_size;
 	fprintf (stderr, "System is %d kB\n", sz/1024);
 	sys_size = (sz + 15) / 16;
-	/* 0x40000*16 = 4.0 MB, reasonable estimate for the current maximum */
-	if (sys_size > (is_big_kernel ? 0x40000 : DEF_SYSSIZE))
-		die("System is too big. Try using %smodules.",
-			is_big_kernel ? "" : "bzImage or ");
+	if (!is_big_kernel && sys_size > DEF_SYSSIZE)
+		die("System is too big. Try using bzImage or modules.");
 	while (sz > 0) {
 		int l, n;
 

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/kernel/head.S,v retrieving revision 1.30

arch/i386/kernel/head.S +73 -69

204 lines not on this page · raw message

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/kernel/setup.c,v retrieving revision 1.107

arch/i386/kernel/setup.c +7 -3
diff -u -r1.107 setup.c
@@ -50,6 +50,11 @@
 #include "setup_arch_pre.h"
 #include "mach_resources.h"
 
+/* This value is set up by the early boot code to point to the value
+   immediately after the boot time page tables.  It contains a *physical*
+   address, and must not be in the .bss segment! */
+unsigned long init_pg_tables_end __initdata = ~0UL;
+
 int disable_pse __initdata = 0;
 
 static inline char * __init machine_specific_memory_setup(void);
@@ -115,7 +120,6 @@
 extern void dmi_scan_machine(void);
 extern void generic_apic_probe(char *);
 extern int root_mountflags;
-extern char _end[];
 
 unsigned long saved_videomode;
 
@@ -785,7 +789,7 @@
 	 * partially used pages are not usable - thus
 	 * we are rounding upwards:
 	 */
-	start_pfn = PFN_UP(__pa(_end));
+	start_pfn = PFN_UP(init_pg_tables_end);
 
 	find_max_pfn();
 
@@ -1097,7 +1101,7 @@
 	init_mm.start_code = (unsigned long) _text;
 	init_mm.end_code = (unsigned long) _etext;
 	init_mm.end_data = (unsigned long) _edata;
-	init_mm.brk = (unsigned long) _end;
+	init_mm.brk = init_pg_tables_end + PAGE_OFFSET;
 
 	code_resource.start = virt_to_phys(_text);
 	code_resource.end = virt_to_phys(_etext)-1;

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/kernel/trampoline.S,v retrieving revision 1.9

arch/i386/kernel/trampoline.S +0 -8
diff -u -r1.9 trampoline.S
@@ -63,13 +63,5 @@
 	.word	0			# idt limit = 0
 	.word	0, 0			# idt base = 0L
 
-#
-# NOTE: here we actually use CPU#0's GDT - but that is OK, we reload
-# the proper GDT shortly after booting up the secondary CPUs.
-#
-ENTRY(boot_gdt)
-	.word	__BOOT_DS + 7			# gdt limit
-	.long	boot_gdt_table-__PAGE_OFFSET	# gdt base = gdt (first SMP CPU)
-
 .globl trampoline_end
 trampoline_end:

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/kernel/vmlinux.lds.S,v retrieving revision 1.2

arch/i386/kernel/vmlinux.lds.S +1 -0
diff -u -r1.2 vmlinux.lds.S
@@ -105,6 +105,7 @@
 	
   __bss_start = .;		/* BSS */
   .bss : { *(.bss) }
+  . = ALIGN(4);
   __bss_stop = .; 
 
   _end = . ;

===================================================================
RCS file: /home/hpa/kernel/bkcvs/linux-2.5/arch/i386/mm/discontig.c,v retrieving revision 1.16

arch/i386/mm/discontig.c +2 -2
diff -u -r1.16 discontig.c
@@ -66,7 +66,7 @@
 extern void one_highpage_init(struct page *, int, int);
 
 extern struct e820map e820;
-extern char _end;
+extern unsigned long init_pg_tables_end;
 extern unsigned long highend_pfn, highstart_pfn;
 extern unsigned long max_low_pfn;
 extern unsigned long totalram_pages;
@@ -237,7 +237,7 @@
 	reserve_pages = calculate_numa_remap_pages();
 
 	/* partially used pages are not usable - thus round upwards */
-	system_start_pfn = min_low_pfn = PFN_UP(__pa(&_end));
+	system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end);
 
 	find_max_pfn();
 	system_max_low_pfn = max_low_pfn = find_max_low_pfn();
#1 lore raw
Quoted 9 lines · H. Peter Anvin

Hi all,

This is the latest version of the i386 early memory cleanup patch. It has the additional advantage that it removes some of the special casing for VISWS -- this is still untested; if you have access to a VISWS *please* test this out.

The main difference other than the VISWS code is that it always sets up the GDT.

I agree with Eric this is a lot cleaner.

Thanks. :)

Two little tweaks I can think of.
1) Can we reserve space between __bss_stop and _end for the page tables and the bitmap of memory?

This should make it obvious that the early boot code is touching that memory.

2) Can we export _end in setup.S so a bootloader can verify the kernel + bss will fit in memory?

Roughly the additions needed to vmlinux.lds.S look like:
+++ arch/i386/kernel/vmlinux.lds.S
@@ -105,6 +105,18 @@

__bss_start = .; /* BSS */
.bss : { *(.bss) }
+ . = ALIGN(4);
__bss_stop = .;
+
+ /* Reserve space for the initial page tables.

+   * A 1GB kernel needs 1MB of page tables.
+   * We must cover the first 1M ourselves and the initial memory bitmap.
+   * In the worst (1G kernel + 36G of ram) case this increases our
+   * page table size by 3K.
+   */

+ . += (((__bss_stop - _text + 4095) / 4096) + ((4*1024*1024) / 4096)) * 4 ; +
+ /* Reserve space for the initial memory bitmap 2^36/4096/8 = 2MB */
+ . += 2*1024*1024;

   _end = . ;

Eric

#2 lore raw
Quoted 10 lines · Eric W. Biederman

Thanks. :)

Two little tweaks I can think of.
1) Can we reserve space between __bss_stop and _end for the page tables and the bitmap of memory?

This should make it obvious that the early boot code is touching that memory.

The bitmap of memory, perhaps, I'll let wli speak to that one. The page tables shouldn't need to be fixed-sized like what you're proposing; although the current patch doesn't make use of it, I'd like to in the future, and this would make it impossible.

Quoted 2 lines

2) Can we export _end in setup.S so a bootloader can verify the kernel + bss will fit in memory?

For what good? Seriously... you're just pushing further into the kernel the point where you will run completely out of memory during initialization -- this case dynamic.

There is absolutely no use in trying to pointf*ck this. Really.

-hpa

#3 lore raw
In reply to Eric W. Biederman
Quoted 8 lines · Eric W. Biederman

Two little tweaks I can think of.
1) Can we reserve space between __bss_stop and _end for the page tables and the bitmap of memory?

This should make it obvious that the early boot code is touching that memory.

After thinking about this a little bit more, you will actually *cause* boot failures on low memory machines if you do this.

Not cool.

-hpa

#4 lore raw

PATCH 1/1 Early memory patch, +accounting

Quoted 6 lines · H. Peter Anvin
Quoted 5 lines · Eric W. Biederman

Two little tweaks I can think of.
1) Can we reserve space between __bss_stop and _end for the page tables and the bitmap of memory?

Quoted 6 lines
Quoted 3 lines

This should make it obvious that the early boot code is touching that memory.

After thinking about this a little bit more, you will actually *cause* boot failures on low memory machines if you do this.

I think we have a failure to communicate. I was not suggesting doing
anything different from what you were doing. I just want to report what is happening.

Now that I see that the bootmem bitmap is at most 128KB on a 32bit architecture I have a very hard time believing we can do real damage to a low mem machine.

On the odd bug list in removing pg0 you broke a couple of the x86 subarches, in particular the boot_ioremap code.

I have come up with a new version of the patch because otherwise I don't think we will be communicating clearly.

The differences are:
- I have corrected the formula for how much memory beyond the page tables is needed.
- I have moved some data structures in head.S in the .data segment
- I have moved the early page tables and the bootmem bitmap between __bss_stop and _end ( We are only talking about 32K in the normal case)
- I have made vmlinux report it's proper physical addresses of where it lives in memory.
- I saw a stupid optimization opportunity and I put boot_gdt in the null entry of boot_gdt_table so now only one label is needed.

I intend to export _end into setup.S so a bootloader can tell what memory the kernel will attempt to use before looking at the e820 map, the kernel command line, and the initrd location. After that the kernel can properly dynamically allocate things.

Exporting _end will allow two things.
1) On systems with a 15M-16M I/O hole the bootloader can check to see if a big kernel will attempt to use memory in that region.

2) On small memory systems it will let the bootloader make a good estimate to see if everything will fit into memory.

If 32KB of available ram makes the difference between being able to load everything into memory or not being able to load I suspect the system won't run for very well anyway.

An exported top of memory value is just advisory and a bootloader is free to totally ignore it, or just warn when it does not look like things will fit.

_end is advisory and an estimate so a bootloader is free to totally ignore it, or just warn when things
If not accounting for 32KB causes boot problems I suspect the system won't run very long anyway.

Anyway it is time for me to go to bed.

Eric

arch/i386/boot/tools/build.c +2 -4
diff -uNrX linux-ignore-files linux-2.6.3-bk7/arch/i386/boot/tools/build.c linux-2.6.3-bk7.initpgtbl/arch/i386/boot/tools/build.c
@@ -150,10 +150,8 @@
 	sz = sb.st_size;
 	fprintf (stderr, "System is %d kB\n", sz/1024);
 	sys_size = (sz + 15) / 16;
-	/* 0x40000*16 = 4.0 MB, reasonable estimate for the current maximum */
-	if (sys_size > (is_big_kernel ? 0x40000 : DEF_SYSSIZE))
-		die("System is too big. Try using %smodules.",
-			is_big_kernel ? "" : "bzImage or ");
+	if (!is_big_kernel && sys_size > DEF_SYSSIZE)
+		die("System is too big. Try using bzImage or modules.");
 	while (sz > 0) {
 		int l, n;
 
arch/i386/kernel/head.S +78 -94

224 lines not on this page · raw message

arch/i386/kernel/setup.c +7 -3
diff -uNrX linux-ignore-files linux-2.6.3-bk7/arch/i386/kernel/setup.c linux-2.6.3-bk7.initpgtbl/arch/i386/kernel/setup.c
@@ -50,6 +50,11 @@
 #include "setup_arch_pre.h"
 #include "mach_resources.h"
 
+/* This value is set up by the early boot code to point to the value
+   immediately after the boot time page tables.  It contains a *physical*
+   address, and must not be in the .bss segment! */
+unsigned long init_pg_tables_end __initdata = ~0UL;
+
 int disable_pse __initdata = 0;
 
 static inline char * __init machine_specific_memory_setup(void);
@@ -115,7 +120,6 @@
 extern void dmi_scan_machine(void);
 extern void generic_apic_probe(char *);
 extern int root_mountflags;
-extern char _end[];
 
 unsigned long saved_videomode;
 
@@ -785,7 +789,7 @@
 	 * partially used pages are not usable - thus
 	 * we are rounding upwards:
 	 */
-	start_pfn = PFN_UP(__pa(_end));
+	start_pfn = PFN_UP(init_pg_tables_end);
 
 	find_max_pfn();
 
@@ -1097,7 +1101,7 @@
 	init_mm.start_code = (unsigned long) _text;
 	init_mm.end_code = (unsigned long) _etext;
 	init_mm.end_data = (unsigned long) _edata;
-	init_mm.brk = (unsigned long) _end;
+	init_mm.brk = init_pg_tables_end + PAGE_OFFSET;
 
 	code_resource.start = virt_to_phys(_text);
 	code_resource.end = virt_to_phys(_etext)-1;
arch/i386/kernel/trampoline.S +0 -8
diff -uNrX linux-ignore-files linux-2.6.3-bk7/arch/i386/kernel/trampoline.S linux-2.6.3-bk7.initpgtbl/arch/i386/kernel/trampoline.S
@@ -63,13 +63,5 @@
 	.word	0			# idt limit = 0
 	.word	0, 0			# idt base = 0L
 
-#
-# NOTE: here we actually use CPU#0's GDT - but that is OK, we reload
-# the proper GDT shortly after booting up the secondary CPUs.
-#
-ENTRY(boot_gdt)
-	.word	__BOOT_DS + 7			# gdt limit
-	.long	boot_gdt_table-__PAGE_OFFSET	# gdt base = gdt (first SMP CPU)
-
 .globl trampoline_end
 trampoline_end:
arch/i386/kernel/vmlinux.lds.S +64 -26

168 lines not on this page · raw message

arch/i386/mm/discontig.c +2 -2
diff -uNrX linux-ignore-files linux-2.6.3-bk7/arch/i386/mm/discontig.c linux-2.6.3-bk7.initpgtbl/arch/i386/mm/discontig.c
@@ -66,7 +66,7 @@
 extern void one_highpage_init(struct page *, int, int);
 
 extern struct e820map e820;
-extern char _end;
+extern unsigned long init_pg_tables_end;
 extern unsigned long highend_pfn, highstart_pfn;
 extern unsigned long max_low_pfn;
 extern unsigned long totalram_pages;
@@ -237,7 +237,7 @@
 	reserve_pages = calculate_numa_remap_pages();
 
 	/* partially used pages are not usable - thus round upwards */
-	system_start_pfn = min_low_pfn = PFN_UP(__pa(&_end));
+	system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end);
 
 	find_max_pfn();
 	system_max_low_pfn = max_low_pfn = find_max_low_pfn();
include/asm-i386/page.h +4 -1
diff -uNrX linux-ignore-files linux-2.6.3-bk7/include/asm-i386/page.h linux-2.6.3-bk7.initpgtbl/include/asm-i386/page.h
@@ -116,14 +116,17 @@
 
 #ifdef __ASSEMBLY__
 #define __PAGE_OFFSET		(0xC0000000)
+/* ld does not reliabely negate numbers */
+#define __MAXMEM		(0x40000000 - __VMALLOC_RESERVE)
 #else
 #define __PAGE_OFFSET		(0xC0000000UL)
+#define __MAXMEM		(-__PAGE_OFFSET - __VMALLOC_RESERVE)
 #endif
 
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
 #define VMALLOC_RESERVE		((unsigned long)__VMALLOC_RESERVE)
-#define MAXMEM			(-__PAGE_OFFSET-__VMALLOC_RESERVE)
+#define MAXMEM			__MAXMEM
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define pfn_to_kaddr(pfn)      __va((pfn) << PAGE_SHIFT)
#5 lore raw

Re: [PATCH] Early memory patch, +accounting

Quoted 6 lines · Eric W. Biederman

I intend to export _end into setup.S so a bootloader can tell what memory the kernel will attempt to use before looking at the e820 map, the kernel command line, and the initrd location. After that the kernel can properly dynamically allocate things.

You're confusing "may" with "will". This is the fundamental problem with this approach.

Quoted 6 lines

Exporting _end will allow two things.
1) On systems with a 15M-16M I/O hole the bootloader can check to see if a big kernel will attempt to use memory in that region.

2) On small memory systems it will let the bootloader make a good estimate to see if everything will fit into memory.

No, it won't, because it still has no idea whatsoever how much dynamically allocated memory it needs during boot.

-hpa

#6 lore raw

Re: [PATCH] Early memory patch, +accounting

Quoted 9 lines · H. Peter Anvin
Quoted 5 lines · Eric W. Biederman

I intend to export _end into setup.S so a bootloader can tell what memory the kernel will attempt to use before looking at the e820 map, the kernel command line, and the initrd location. After that the kernel can properly dynamically allocate things.

You're confusing "may" with "will". This is the fundamental problem with this approach.

If the margin of error was significant I would agree. The margin of error in this case is very small.

Quoted 8 lines
Quoted 5 lines

Exporting _end will allow two things.
1) On systems with a 15M-16M I/O hole the bootloader can check to see if a big kernel will attempt to use memory in that region.
2) On small memory systems it will let the bootloader make a good estimate to see if everything will fit into memory.

No, it won't, because it still has no idea whatsoever how much dynamically allocated memory it needs during boot.

It will at least ensure the kernel can run long enough to fail gracefully. I admit that a panic is not the most graceful failure but it does at least let you know what went wrong.

Eric

#7 lore raw
In reply to H. Peter Anvin
Quoted 1 line · H. Peter Anvin

Hi all,

While looking and understanding what this code does I have found a real bug.

Quoted 8 lines

+/*
+ * This is how much memory *in addition to the memory covered up to
+ * and including _end* we need mapped initially. We need one bit for
+ * each possible page, which currently means 2^36/4096/8 = 2 MB
+ * (64-bit-capable chips can do more, but if you have more than 64 GB
+ * of memory you *really* should be running a 64-bit kernel. However,
+ * if this really bothers someone we could query this dynamically.)
+ *

Quoted 8 lines

+ * The other thing we may want to do dynamically in the future is to
+ * detect PSE and skip generating the PTEs.
+ *
+ * Modulo rounding, each megabyte assigned here requires a kilobyte of
+ * memory, which is currently unreclaimed.
+ *
+ * This should be a multiple of a page.
*/

The comment about the bootmem is wrong and misleading. The bootmem bitmap only included low memory. So in the worst case with a 4G/4G split it can be 2^32/4096/8 = 128KiB. Normally the worst case is only 32KiB with a 3G/1G split.

Quoted 1 line

+#define INIT_MAP_BEYOND_END (2*1024*1024)

#define INIT_MAP_BEYOND_END (128*1024)
is the correct value here.

Eric