Skip to contentSkip to conversation

Updates for firstfloor paravirt-ops patches

PATCH In review Jeremy Fitzhardinge 9 participants 70 messages Apr 14, 2007 – Apr 29, 2007

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

cover Updates for firstfloor paravirt-ops patches

Hi Andi,

This is a set of updates for the firstfloor patch queue.

Quick rundown:

revert-mm-x86_64-mm-account-for-module-percpu-space-separately-from-kernel-percpu.patch separate-module-percpu-space.patch
Update the module percpu accounting patch

fix-ff-allow-percpu-variables-to-be-page-aligned.patch
Make sure the percpu memory allocation is page-aligned

deflate-stack-usage-in-lib_inflate_c.patch
Fix deflate stack usage. With all the arch-fixes rolled in.

i386-gdt-cleanups-page-align-the-gdt.patch i386-convert-pda-into-the-percpu-section.patch i386-cleanups-to-help-using-per-cpu-variables-from-asm.patch percpu-define-per_cpu_offset.patch fix-uniproc-gdt-bugs.patch
Percpu and GDT fixes.

x86-map-enough-initial-memory.patch
Fix head.S to map enough memory.

cleanup-cleanup-asm-bugs_h.patch cleanup-identify_cpu-fix.patch
This is the pair I tried to post yesterday, but they got interrupted by an network outage. They basically add a little more cleanup, and move a misplaced hunk. cleanup-cleanup-asm-bugs_h.patch should go after/roll into the clean-up-asm-(i386|x86_64)-bugs_h patches, and cleanup-identify_cpu-fix.patch should go after/roll into clean-up-identify_cpu patch.

paravirt-flush-on-kunmap_atomic.patch
Flush pending lazy mmu operations on kunmap_atomic too.

paravirt-fix-paravirt_lazy.patch
Fix up an apparent mismerge: remove the #defines for PARAVIRT_LAZY_* and add _LAZY_FLUSH to the enum.

i386-sysenter-arch-pages-fix.patch i386-acpi-remove-earlyquirk-warning.patch i386-mcheck-p4-grotesque-and-needless-warning-fix.patch i386-pgd-clone-under-lock-fix.patch paravirt-kmap_atomic_pte-tidy.patch vmi-supports-compat-vdso.patch vmi-kmap_atomic_pte-fix.patch vmi-timer-update.patch buslogic-check-range-fixes.patch pte-drop-ptep_get_and_clear-paravirt-op.patch
A chunk of pages from Zach.

rename-the-parainstructions-symbols-to-be-consistent-with-the-others.patch rename-the-parainstructions-symbols-to-be-consistent-with-the-others-fix.patch
Obvious.

vmi-fix-ff.patch
Make VMI compile in the -ff patchstack.

paravirt-sched-clock-ff.patch
Updated paravirt-sched-lock for your sched_clock.

Thanks,
J

#1 lore raw

PATCH 1/28 revert account-for-module-percpu-space-separately-from-kernel-percpu

---
 include/asm-i386/percpu.h |   10 ----------
 1 file changed, 10 deletions(-)

===================================================================

include/asm-i386/percpu.h +0 -10
@@ -4,16 +4,6 @@
 #ifndef __ASSEMBLY__
 #include <asm-generic/percpu.h>
 #else
-
-#ifdef CONFIG_MODULES
-# define PERCPU_MODULE_RESERVE 8192
-#else
-# define PERCPU_MODULE_RESERVE 0
-#endif
-
-#define PERCPU_ENOUGH_ROOM \
-	(ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
-	 PERCPU_MODULE_RESERVE)
 
 /*
  * PER_CPU finds an address of a per-cpu variable.
#2 lore raw

PATCH 2/28 Account for module percpu space separately from kernel percpu

In reply to Jeremy Fitzhardinge

Rather than using a single constant PERCPU_ENOUGH_ROOM, compute it as the sum of kernel_percpu + PERCPU_MODULE_RESERVE. This is now common to all architectures; if an architecture wants to set PERCPU_ENOUGH_ROOM to something special, then it may do so (ia64 is the only one which does).

Cc Rusty Russell <[email protected]>
Cc Eric W. Biederman <[email protected]>
Cc Andi Kleen <[email protected]>
---
 include/asm-alpha/percpu.h   |   14 --------------
 include/asm-sparc64/percpu.h |   10 ----------
 include/asm-x86_64/percpu.h  |   10 ----------
 include/linux/percpu.h       |    9 ++++++++-
 kernel/module.c              |    2 +-
 5 files changed, 9 insertions(+), 36 deletions(-)

===================================================================

include/asm-alpha/percpu.h +0 -14
@@ -1,19 +1,5 @@
 #ifndef __ALPHA_PERCPU_H
 #define __ALPHA_PERCPU_H
-
-/*
- * Increase the per cpu area for Alpha so that
- * modules using percpu area can load.
- */
-#ifdef CONFIG_MODULES
-# define PERCPU_MODULE_RESERVE 8192
-#else
-# define PERCPU_MODULE_RESERVE 0
-#endif
-
-#define PERCPU_ENOUGH_ROOM \
-	(ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
-	 PERCPU_MODULE_RESERVE)
 
 #include <asm-generic/percpu.h>
 

===================================================================

include/asm-sparc64/percpu.h +0 -10
@@ -4,16 +4,6 @@
 #include <linux/compiler.h>
 
 #ifdef CONFIG_SMP
-
-#ifdef CONFIG_MODULES
-# define PERCPU_MODULE_RESERVE 8192
-#else
-# define PERCPU_MODULE_RESERVE 0
-#endif
-
-#define PERCPU_ENOUGH_ROOM \
-	(ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
-	 PERCPU_MODULE_RESERVE)
 
 extern void setup_per_cpu_areas(void);
 

===================================================================

include/asm-x86_64/percpu.h +0 -10
@@ -10,16 +10,6 @@
 #ifdef CONFIG_SMP
 
 #include <asm/pda.h>
-
-#ifdef CONFIG_MODULES
-# define PERCPU_MODULE_RESERVE 8192
-#else
-# define PERCPU_MODULE_RESERVE 0
-#endif
-
-#define PERCPU_ENOUGH_ROOM \
-	(ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
-	 PERCPU_MODULE_RESERVE)
 
 #define __per_cpu_offset(cpu) (cpu_pda(cpu)->data_offset)
 #define __my_cpu_offset() read_pda(data_offset)

===================================================================

include/linux/percpu.h +8 -1
@@ -11,8 +11,15 @@
 
 /* Enough to cover all DEFINE_PER_CPUs in kernel, including modules. */
 #ifndef PERCPU_ENOUGH_ROOM
-#define PERCPU_ENOUGH_ROOM 32768
+#ifdef CONFIG_MODULES
+#define PERCPU_MODULE_RESERVE	8192
+#else
+#define PERCPU_MODULE_RESERVE	0
 #endif
+
+#define PERCPU_ENOUGH_ROOM						\
+	(__per_cpu_end - __per_cpu_start + PERCPU_MODULE_RESERVE)
+#endif	/* PERCPU_ENOUGH_ROOM */
 
 /*
  * Must be an lvalue. Since @var must be a simple identifier,

===================================================================

kernel/module.c +1 -1
@@ -430,7 +430,7 @@ static int percpu_modinit(void)
 	pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated,
 			    GFP_KERNEL);
 	/* Static in-kernel percpu data (used). */
-	pcpu_size[0] = -ALIGN(__per_cpu_end-__per_cpu_start, SMP_CACHE_BYTES);
+	pcpu_size[0] = -(__per_cpu_end-__per_cpu_start);
 	/* Free room. */
 	pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
 	if (pcpu_size[1] < 0) {
#3 lore raw

PATCH 3/28 fix allow-percpu-variables-to-be-page-aligned.patch

In reply to Jeremy Fitzhardinge

Make sure allocation is page-aligned.

---
 init/main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

init/main.c +1 -1
@@ -370,7 +370,7 @@ static void __init setup_per_cpu_areas(v
 
 	/* Copy section for each CPU (we discard the original) */
 	size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
-	ptr = alloc_bootmem(size * nr_possible_cpus);
+	ptr = alloc_bootmem_pages(size * nr_possible_cpus);
 
 	for_each_possible_cpu(i) {
 		__per_cpu_offset[i] = ptr - __per_cpu_start;
#4 lore raw

PATCH 4/28 deflate stack usage in lib/inflate.c

In reply to Jeremy Fitzhardinge

inflate_fixed and huft_build together use around 2.7k of stack. When using 4k stacks, I saw stack overflows from interrupts arriving while unpacking the root initrd:

do_IRQ: stack overflow: 384

 [<c0106b64>] show_trace_log_lvl+0x1a/0x30
 [<c01075e6>] show_trace+0x12/0x14
 [<c010763f>] dump_stack+0x16/0x18
 [<c0107ca4>] do_IRQ+0x6d/0xd9
 [<c010202b>] xen_evtchn_do_upcall+0x6e/0xa2
 [<c0106781>] xen_hypervisor_callback+0x25/0x2c
 [<c010116c>] xen_restore_fl+0x27/0x29
 [<c0330f63>] _spin_unlock_irqrestore+0x4a/0x50
 [<c0117aab>] change_page_attr+0x577/0x584
 [<c0117b45>] kernel_map_pages+0x8d/0xb4
 [<c016a314>] cache_alloc_refill+0x53f/0x632
 [<c016a6c2>] __kmalloc+0xc1/0x10d
 [<c0463d34>] malloc+0x10/0x12
 [<c04641c1>] huft_build+0x2a7/0x5fa
 [<c04645a5>] inflate_fixed+0x91/0x136
 [<c04657e2>] unpack_to_rootfs+0x5f2/0x8c1
 [<c0465acf>] populate_rootfs+0x1e/0xe4

(This was under Xen, but there's no reason it couldn't happen on bare
hardware.)

This patch mallocs the local variables, thereby reducing the stack usage to sane levels.

Also, up the heap size for the kernel decompressor to deal with the extra allocation.

Cc Tim Yamin <[email protected]>
Cc Andi Kleen <[email protected]>
Cc Matt Mackall <[email protected]>
Cc Ivan Kokshaysky <[email protected]>
Cc Richard Henderson <[email protected]>
Cc Russell King <[email protected]>
Cc Ian Molton <[email protected]>
---
 arch/alpha/boot/misc.c             |    2 -
 arch/arm/boot/compressed/misc.c    |    2 -
 arch/arm26/boot/compressed/misc.c  |    2 -
 arch/i386/boot/compressed/misc.c   |    2 -
 arch/x86_64/boot/compressed/misc.c |    2 -
 lib/inflate.c                      |   66 ++++++++++++++++++++++++++----------
 6 files changed, 54 insertions(+), 22 deletions(-)

===================================================================

arch/alpha/boot/misc.c +1 -1
@@ -98,7 +98,7 @@ static ulg free_mem_ptr;
 static ulg free_mem_ptr;
 static ulg free_mem_ptr_end;
 
-#define HEAP_SIZE 0x2000
+#define HEAP_SIZE 0x3000
 
 #include "../../../lib/inflate.c"
 

===================================================================

arch/arm/boot/compressed/misc.c +1 -1
@@ -239,7 +239,7 @@ static ulg free_mem_ptr;
 static ulg free_mem_ptr;
 static ulg free_mem_ptr_end;
 
-#define HEAP_SIZE 0x2000
+#define HEAP_SIZE 0x3000
 
 #include "../../../../lib/inflate.c"
 

===================================================================

arch/arm26/boot/compressed/misc.c +1 -1
@@ -182,7 +182,7 @@ static ulg free_mem_ptr;
 static ulg free_mem_ptr;
 static ulg free_mem_ptr_end;
 
-#define HEAP_SIZE 0x2000
+#define HEAP_SIZE 0x3000
 
 #include "../../../../lib/inflate.c"
 

===================================================================

arch/i386/boot/compressed/misc.c +1 -1
@@ -189,7 +189,7 @@ static unsigned long free_mem_ptr;
 static unsigned long free_mem_ptr;
 static unsigned long free_mem_end_ptr;
 
-#define HEAP_SIZE             0x3000
+#define HEAP_SIZE             0x4000
 
 static char *vidmem = (char *)0xb8000;
 static int vidport;

===================================================================

arch/x86_64/boot/compressed/misc.c +1 -1
@@ -189,7 +189,7 @@ static long free_mem_ptr;
 static long free_mem_ptr;
 static long free_mem_end_ptr;
 
-#define HEAP_SIZE             0x6000
+#define HEAP_SIZE             0x7000
 
 static char *vidmem = (char *)0xb8000;
 static int vidport;

===================================================================

lib/inflate.c +49 -17

140 lines not on this page · raw message

#5 lore raw

PATCH 5/28 Page-align the GDT

In reply to Jeremy Fitzhardinge

Xen wants a dedicated page for the GDT. I believe VMI likes it too. lguest, KVM and native don't care.

Simple transformation to page-aligned "struct gdt_page".

---
 arch/i386/kernel/cpu/common.c |    6 +++---
 arch/i386/kernel/entry.S      |    2 +-
 arch/i386/kernel/head.S       |    2 +-
 arch/i386/kernel/traps.c      |    2 +-
 include/asm-i386/desc.h       |    9 +++++++--
 5 files changed, 13 insertions(+), 8 deletions(-)

===================================================================

arch/i386/kernel/cpu/common.c +3 -3
@@ -22,7 +22,7 @@
 
 #include "cpu.h"
 
-DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]) = {
+DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = {
 	[GDT_ENTRY_KERNEL_CS] = { 0x0000ffff, 0x00cf9a00 },
 	[GDT_ENTRY_KERNEL_DS] = { 0x0000ffff, 0x00cf9200 },
 	[GDT_ENTRY_DEFAULT_USER_CS] = { 0x0000ffff, 0x00cffa00 },
@@ -48,8 +48,8 @@ DEFINE_PER_CPU(struct desc_struct, cpu_g
 
 	[GDT_ENTRY_ESPFIX_SS] = { 0x00000000, 0x00c09200 },
 	[GDT_ENTRY_PDA] = { 0x00000000, 0x00c09200 }, /* set in setup_pda */
-};
-EXPORT_PER_CPU_SYMBOL_GPL(cpu_gdt);
+} };
+EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
 
 DEFINE_PER_CPU(struct i386_pda, _cpu_pda) = {
 	._pda = &per_cpu___cpu_pda,

===================================================================

arch/i386/kernel/entry.S +1 -1
@@ -558,7 +558,7 @@ END(syscall_badsys)
 #define FIXUP_ESPFIX_STACK \
 	/* since we are on a wrong stack, we cant make it a C code :( */ \
 	movl %fs:PDA_cpu, %ebx; \
-	PER_CPU(cpu_gdt, %ebx); \
+	PER_CPU(gdt_page, %ebx); \
 	GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \
 	addl %esp, %eax; \
 	pushl $__KERNEL_DS; \

===================================================================

arch/i386/kernel/head.S +1 -1
@@ -599,7 +599,7 @@ idt_descr:
 	.word 0				# 32 bit align gdt_desc.address
 ENTRY(early_gdt_descr)
 	.word GDT_ENTRIES*8-1
-	.long per_cpu__cpu_gdt		/* Overwritten for secondary CPUs */
+	.long per_cpu__gdt_page		/* Overwritten for secondary CPUs */
 
 /*
  * The boot_gdt_table must mirror the equivalent in setup.S and is

===================================================================

arch/i386/kernel/traps.c +1 -1
@@ -1037,7 +1037,7 @@ fastcall unsigned long patch_espfix_desc
 fastcall unsigned long patch_espfix_desc(unsigned long uesp,
 					  unsigned long kesp)
 {
-	struct desc_struct *gdt = __get_cpu_var(cpu_gdt);
+	struct desc_struct *gdt = __get_cpu_var(gdt_page).gdt;
 	unsigned long base = (kesp - uesp) & -THREAD_SIZE;
 	unsigned long new_kesp = kesp - base;
 	unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT;

===================================================================

include/asm-i386/desc.h +7 -2
@@ -18,10 +18,15 @@ struct Xgt_desc_struct {
 	unsigned short pad;
 } __attribute__ ((packed));
 
-DECLARE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
+struct gdt_page
+{
+	struct desc_struct gdt[GDT_ENTRIES];
+} __attribute__((aligned(PAGE_SIZE)));
+DECLARE_PER_CPU(struct gdt_page, gdt_page);
+
 static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
 {
-	return per_cpu(cpu_gdt, cpu);
+	return per_cpu(gdt_page, cpu).gdt;
 }
 
 extern struct Xgt_desc_struct idt_descr;
#6 lore raw

PATCH 6/28 Convert PDA into the percpu section

In reply to Jeremy Fitzhardinge

Currently x86 (similar to x84-64) has a special per-cpu structure called "i386_pda" which can be easily and efficiently referenced via the %fs register. An ELF section is more flexible than a structure, allowing any piece of code to use this area. Indeed, such a section already exists: the per-cpu area.

So this patch:
(1) Removes the PDA and uses per-cpu variables for each current member.
(2) Replaces the __KERNEL_PDA segment with __KERNEL_PERCPU.
(3) Creates a per-cpu mirror of __per_cpu_offset called this_cpu_off, which can be used to calculate addresses for this CPU's variables.
(4) Simplifies startup, because %fs doesn't need to be loaded with a special segment at early boot; it can be deferred until the first percpu area is allocated (or never for UP).

The result is less code and one less x86-specific concept.

---
 arch/i386/kernel/asm-offsets.c |    5 -
 arch/i386/kernel/cpu/common.c  |   17 -----
 arch/i386/kernel/entry.S       |    5 -
 arch/i386/kernel/head.S        |   31 +--------
 arch/i386/kernel/i386_ksyms.c  |    2 
 arch/i386/kernel/irq.c         |    3 
 arch/i386/kernel/process.c     |   12 ++-
 arch/i386/kernel/smpboot.c     |   34 ++++------
 arch/i386/kernel/vmi.c         |    6 -
 arch/i386/kernel/vmlinux.lds.S |    1 
 include/asm-i386/current.h     |    5 -
 include/asm-i386/irq_regs.h    |   12 ++-
 include/asm-i386/pda.h         |   99 ------------------------------
 include/asm-i386/percpu.h      |  132 +++++++++++++++++++++++++++++++++++++---
 include/asm-i386/processor.h   |    2 
 include/asm-i386/segment.h     |    6 -
 include/asm-i386/smp.h         |    4 -
 include/asm-i386/unwind.h      |    2 
 18 files changed, 180 insertions(+), 198 deletions(-)

===================================================================

arch/i386/kernel/asm-offsets.c +0 -5
@@ -15,7 +15,6 @@
 #include <asm/processor.h>
 #include <asm/thread_info.h>
 #include <asm/elf.h>
-#include <asm/pda.h>
 
 #define DEFINE(sym, val) \
         asm volatile("\n->" #sym " %0 " #val : : "i" (val))
@@ -101,10 +100,6 @@ void foo(void)
 
 	OFFSET(crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx);
 
-	BLANK();
- 	OFFSET(PDA_cpu, i386_pda, cpu_number);
-	OFFSET(PDA_pcurrent, i386_pda, pcurrent);
-
 #ifdef CONFIG_PARAVIRT
 	BLANK();
 	OFFSET(PARAVIRT_enabled, paravirt_ops, paravirt_enabled);

===================================================================

arch/i386/kernel/cpu/common.c +3 -14
@@ -18,7 +18,6 @@
 #include <asm/apic.h>
 #include <mach_apic.h>
 #endif
-#include <asm/pda.h>
 
 #include "cpu.h"
 
@@ -47,12 +46,9 @@ DEFINE_PER_CPU(struct gdt_page, gdt_page
 	[GDT_ENTRY_APMBIOS_BASE+2] = { 0x0000ffff, 0x00409200 }, /* data */
 
 	[GDT_ENTRY_ESPFIX_SS] = { 0x00000000, 0x00c09200 },
-	[GDT_ENTRY_PDA] = { 0x00000000, 0x00c09200 }, /* set in setup_pda */
+	[GDT_ENTRY_PERCPU] = { 0x00000000, 0x00000000 },
 } };
 EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
-
-DEFINE_PER_CPU(struct i386_pda, _cpu_pda);
-EXPORT_PER_CPU_SYMBOL(_cpu_pda);
 
 static int cachesize_override __cpuinitdata = -1;
 static int disable_x86_fxsr __cpuinitdata;
@@ -634,20 +630,13 @@ void __init early_cpu_init(void)
 #endif
 }
 
-/* Make sure %gs is initialized properly in idle threads */
+/* Make sure %fs is initialized properly in idle threads */
 struct pt_regs * __devinit idle_regs(struct pt_regs *regs)
 {
 	memset(regs, 0, sizeof(struct pt_regs));
-	regs->xfs = __KERNEL_PDA;
+	regs->xfs = __KERNEL_PERCPU;
 	return regs;
 }
-
-/* Initial PDA used by boot CPU */
-struct i386_pda boot_pda = {
-	._pda = &boot_pda,
-	.cpu_number = 0,
-	.pcurrent = &init_task,
-};
 
 /*
  * cpu_init() initializes state that is per-CPU. Some data is already

===================================================================

arch/i386/kernel/entry.S +2 -3
@@ -132,7 +132,7 @@ 1:
 	movl $(__USER_DS), %edx; \
 	movl %edx, %ds; \
 	movl %edx, %es; \
-	movl $(__KERNEL_PDA), %edx; \
+	movl $(__KERNEL_PERCPU), %edx; \
 	movl %edx, %fs
 
 #define RESTORE_INT_REGS \
@@ -556,7 +556,6 @@ END(syscall_badsys)
 
 #define FIXUP_ESPFIX_STACK \
 	/* since we are on a wrong stack, we cant make it a C code :( */ \
-	movl %fs:PDA_cpu, %ebx; \
 	PER_CPU(gdt_page, %ebx); \
 	GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \
 	addl %esp, %eax; \
@@ -681,7 +680,7 @@ error_code:
 	pushl %fs
 	CFI_ADJUST_CFA_OFFSET 4
 	/*CFI_REL_OFFSET fs, 0*/
-	movl $(__KERNEL_PDA), %ecx
+	movl $(__KERNEL_PERCPU), %ecx
 	movl %ecx, %fs
 	UNWIND_ESPFIX_STACK
 	popl %ecx

===================================================================

arch/i386/kernel/head.S +6 -25
@@ -317,12 +317,12 @@ 2:	movl %cr0,%eax
 	movl %eax,%cr0
 
 	call check_x87
-	call setup_pda
 	lgdt early_gdt_descr
 	lidt idt_descr
 	ljmp $(__KERNEL_CS),$1f
 1:	movl $(__KERNEL_DS),%eax	# reload all the segment registers
 	movl %eax,%ss			# after changing gdt.
+	movl %eax,%fs			# gets reset once there's real percpu
 
 	movl $(__USER_DS),%eax		# DS/ES contains default USER segment
 	movl %eax,%ds
@@ -331,9 +331,6 @@ 1:	movl $(__KERNEL_DS),%eax	# reload all
 	xorl %eax,%eax			# Clear GS and LDT
 	movl %eax,%gs
 	lldt %ax
-
-	movl $(__KERNEL_PDA),%eax
-	mov  %eax,%fs
 
 	cld			# gcc2 wants the direction flag cleared at all times
 	pushl $0		# fake return address for unwinder
@@ -341,7 +338,11 @@ 1:	movl $(__KERNEL_DS),%eax	# reload all
 	movb ready, %cl
 	movb $1, ready
 	cmpb $0,%cl		# the first CPU calls start_kernel
-	jne initialize_secondary # all other CPUs call initialize_secondary
+	je   1f
+	movl $(__KERNEL_PERCPU), %eax
+	movl %eax,%fs		# set this cpu's percpu
+	jmp initialize_secondary # all other CPUs call initialize_secondary
+1:
 #endif /* CONFIG_SMP */
 	jmp start_kernel
 
@@ -362,23 +363,6 @@ check_x87:
 	ALIGN
 1:	movb $1,X86_HARD_MATH
 	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */
-	ret
-
-/*
- * Point the GDT at this CPU's PDA.  On boot this will be
- * cpu_gdt_table and boot_pda; for secondary CPUs, these will be
- * that CPU's GDT and PDA.
- */
-ENTRY(setup_pda)
-	/* get the PDA pointer */
-	movl start_pda, %eax
-
-	/* slot the PDA address into the GDT */
-	mov early_gdt_descr+2, %ecx
-	mov %ax, (__KERNEL_PDA+0+2)(%ecx)		/* base & 0x0000ffff */
-	shr $16, %eax
-	mov %al, (__KERNEL_PDA+4+0)(%ecx)		/* base & 0x00ff0000 */
-	mov %ah, (__KERNEL_PDA+4+3)(%ecx)		/* base & 0xff000000 */
 	ret
 
 /*
@@ -553,9 +537,6 @@ ENTRY(empty_zero_page)
  * This starts the data section.
  */
 .data
-ENTRY(start_pda)
-	.long boot_pda
-
 ENTRY(stack_start)
 	.long init_thread_union+THREAD_SIZE
 	.long __BOOT_DS

===================================================================

arch/i386/kernel/i386_ksyms.c +0 -2
@@ -28,5 +28,3 @@ EXPORT_SYMBOL(__read_lock_failed);
 #endif
 
 EXPORT_SYMBOL(csum_partial);
-
-EXPORT_SYMBOL(_proxy_pda);

===================================================================

arch/i386/kernel/irq.c +3 -0
@@ -23,6 +23,9 @@
 
 DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_internodealigned_in_smp;
 EXPORT_PER_CPU_SYMBOL(irq_stat);
+
+DEFINE_PER_CPU(struct pt_regs *, irq_regs);
+EXPORT_PER_CPU_SYMBOL(irq_regs);
 
 /*
  * 'what should we do if we get a hw irq event on an illegal vector'.

===================================================================

arch/i386/kernel/process.c +9 -3
@@ -39,6 +39,7 @@
 #include <linux/random.h>
 #include <linux/personality.h>
 #include <linux/tick.h>
+#include <linux/percpu.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -57,7 +58,6 @@
 
 #include <asm/tlbflush.h>
 #include <asm/cpu.h>
-#include <asm/pda.h>
 
 asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
 
@@ -65,6 +65,12 @@ static int hlt_counter;
 
 unsigned long boot_option_idle_override = 0;
 EXPORT_SYMBOL(boot_option_idle_override);
+
+DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
+EXPORT_PER_CPU_SYMBOL(current_task);
+
+DEFINE_PER_CPU(int, cpu_number);
+EXPORT_PER_CPU_SYMBOL(cpu_number);
 
 /*
  * Return saved PC of a blocked thread.
@@ -342,7 +348,7 @@ int kernel_thread(int (*fn)(void *), voi
 
 	regs.xds = __USER_DS;
 	regs.xes = __USER_DS;
-	regs.xfs = __KERNEL_PDA;
+	regs.xfs = __KERNEL_PERCPU;
 	regs.orig_eax = -1;
 	regs.eip = (unsigned long) kernel_thread_helper;
 	regs.xcs = __KERNEL_CS | get_kernel_rpl();
@@ -711,7 +717,7 @@ struct task_struct fastcall * __switch_t
 	if (prev->gs | next->gs)
 		loadsegment(gs, next->gs);
 
-	write_pda(pcurrent, next_p);
+	x86_write_percpu(current_task, next_p);
 
 	return prev_p;
 }

===================================================================

arch/i386/kernel/smpboot.c +16 -18
@@ -53,7 +53,6 @@
 #include <asm/desc.h>
 #include <asm/arch_hooks.h>
 #include <asm/nmi.h>
-#include <asm/pda.h>
 
 #include <mach_apic.h>
 #include <mach_wakecpu.h>
@@ -98,6 +97,9 @@ EXPORT_SYMBOL(x86_cpu_to_apicid);
 EXPORT_SYMBOL(x86_cpu_to_apicid);
 
 u8 apicid_2_node[MAX_APICID];
+
+DEFINE_PER_CPU(unsigned long, this_cpu_off);
+EXPORT_PER_CPU_SYMBOL(this_cpu_off);
 
 /*
  * Trampoline 80x86 program as an array.
@@ -456,7 +458,6 @@ extern struct {
 	void * esp;
 	unsigned short ss;
 } stack_start;
-extern struct i386_pda *start_pda;
 
 #ifdef CONFIG_NUMA
 
@@ -784,20 +785,17 @@ static inline struct task_struct * alloc
 /* Initialize the CPU's GDT.  This is either the boot CPU doing itself
    (still using the master per-cpu area), or a CPU doing it for a
    secondary which will soon come up. */
-static __cpuinit void init_gdt(int cpu, struct task_struct *idle)
+static __cpuinit void init_gdt(int cpu)
 {
 	struct desc_struct *gdt = get_cpu_gdt_table(cpu);
-	struct i386_pda *pda = &per_cpu(_cpu_pda, cpu);
-
-	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PDA].a,
-			(u32 *)&gdt[GDT_ENTRY_PDA].b,
-			(unsigned long)pda, sizeof(*pda) - 1,
-			0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */
-
-	memset(pda, 0, sizeof(*pda));
-	pda->_pda = pda;
-	pda->cpu_number = cpu;
-	pda->pcurrent = idle;
+
+	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PERCPU].a,
+			(u32 *)&gdt[GDT_ENTRY_PERCPU].b,
+			__per_cpu_offset[cpu], 0xFFFFF,
+			0x80 | DESCTYPE_S | 0x2, 0x8);
+
+	per_cpu(this_cpu_off, cpu) = __per_cpu_offset[cpu];
+	per_cpu(cpu_number, cpu) = cpu;
 }
 
 /* Defined in head.S */
@@ -824,9 +822,9 @@ static int __cpuinit do_boot_cpu(int api
 	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpu);
 
-	init_gdt(cpu, idle);
+	init_gdt(cpu);
+ 	per_cpu(current_task, cpu) = idle;
 	early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu);
-	start_pda = cpu_pda(cpu);
 
 	idle->thread.eip = (unsigned long) start_secondary;
 	/* start_eip had better be page-aligned! */
@@ -1188,14 +1186,14 @@ static inline void switch_to_new_gdt(voi
 	gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
 	gdt_descr.size = GDT_SIZE - 1;
 	load_gdt(&gdt_descr);
-	asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_PDA) : "memory");
+	asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory");
 }
 
 void __init native_smp_prepare_boot_cpu(void)
 {
 	unsigned int cpu = smp_processor_id();
 
-	init_gdt(cpu, current);
+	init_gdt(cpu);
 	switch_to_new_gdt();
 
 	cpu_set(cpu, cpu_online_map);

===================================================================

arch/i386/kernel/vmi.c +1 -5
@@ -504,8 +504,6 @@ static void vmi_pmd_clear(pmd_t *pmd)
 #endif
 
 #ifdef CONFIG_SMP
-extern void setup_pda(void);
-
 static void __devinit
 vmi_startup_ipi_hook(int phys_apicid, unsigned long start_eip,
 		     unsigned long start_esp)
@@ -530,12 +528,10 @@ vmi_startup_ipi_hook(int phys_apicid, un
 
 	ap.ds = __USER_DS;
 	ap.es = __USER_DS;
-	ap.fs = __KERNEL_PDA;
+	ap.fs = __KERNEL_PERCPU;
 	ap.gs = 0;
 
 	ap.eflags = 0;
-
-	setup_pda();
 
 #ifdef CONFIG_X86_PAE
 	/* efer should match BSP efer. */

===================================================================

arch/i386/kernel/vmlinux.lds.S +0 -1
@@ -26,7 +26,6 @@ OUTPUT_ARCH(i386)
 OUTPUT_ARCH(i386)
 ENTRY(phys_startup_32)
 jiffies = jiffies_64;
-_proxy_pda = 0;
 
 PHDRS {
 	text PT_LOAD FLAGS(5);	/* R_E */

===================================================================

include/asm-i386/current.h +3 -2
@@ -1,14 +1,15 @@
 #ifndef _I386_CURRENT_H
 #define _I386_CURRENT_H
 
-#include <asm/pda.h>
 #include <linux/compiler.h>
+#include <asm/percpu.h>
 
 struct task_struct;
 
+DECLARE_PER_CPU(struct task_struct *, current_task);
 static __always_inline struct task_struct *get_current(void)
 {
-	return read_pda(pcurrent);
+	return x86_read_percpu(current_task);
 }
  
 #define current get_current()

===================================================================

include/asm-i386/irq_regs.h +7 -5
@@ -1,25 +1,27 @@
 /*
  * Per-cpu current frame pointer - the location of the last exception frame on
- * the stack, stored in the PDA.
+ * the stack, stored in the per-cpu area.
  *
  * Jeremy Fitzhardinge <[email protected]>
  */
 #ifndef _ASM_I386_IRQ_REGS_H
 #define _ASM_I386_IRQ_REGS_H
 
-#include <asm/pda.h>
+#include <asm/percpu.h>
+
+DECLARE_PER_CPU(struct pt_regs *, irq_regs);
 
 static inline struct pt_regs *get_irq_regs(void)
 {
-	return read_pda(irq_regs);
+	return x86_read_percpu(irq_regs);
 }
 
 static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
 {
 	struct pt_regs *old_regs;
 
-	old_regs = read_pda(irq_regs);
-	write_pda(irq_regs, new_regs);
+	old_regs = get_irq_regs();
+	x86_write_percpu(irq_regs, new_regs);
 
 	return old_regs;
 }

===================================================================

include/asm-i386/pda.h deleted +0 -99

99 lines not on this page · raw message

===================================================================

include/asm-i386/percpu.h +123 -9

146 lines not on this page · raw message

===================================================================

include/asm-i386/processor.h +1 -1
@@ -377,7 +377,7 @@ struct thread_struct {
 	.vm86_info = NULL,						\
 	.sysenter_cs = __KERNEL_CS,					\
 	.io_bitmap_ptr = NULL,						\
-	.fs = __KERNEL_PDA,						\
+	.fs = __KERNEL_PERCPU,						\
 }
 
 /*

===================================================================

include/asm-i386/segment.h +3 -3
@@ -39,7 +39,7 @@
  *  25 - APM BIOS support 
  *
  *  26 - ESPFIX small SS
- *  27 - PDA				[ per-cpu private data area ]
+ *  27 - per-cpu			[ offset to per-cpu data area ]
  *  28 - unused
  *  29 - unused
  *  30 - unused
@@ -74,8 +74,8 @@
 #define GDT_ENTRY_ESPFIX_SS		(GDT_ENTRY_KERNEL_BASE + 14)
 #define __ESPFIX_SS (GDT_ENTRY_ESPFIX_SS * 8)
 
-#define GDT_ENTRY_PDA			(GDT_ENTRY_KERNEL_BASE + 15)
-#define __KERNEL_PDA (GDT_ENTRY_PDA * 8)
+#define GDT_ENTRY_PERCPU			(GDT_ENTRY_KERNEL_BASE + 15)
+#define __KERNEL_PERCPU (GDT_ENTRY_PERCPU * 8)
 
 #define GDT_ENTRY_DOUBLEFAULT_TSS	31
 

===================================================================

include/asm-i386/smp.h +2 -2
@@ -8,7 +8,6 @@
 #include <linux/kernel.h>
 #include <linux/threads.h>
 #include <linux/cpumask.h>
-#include <asm/pda.h>
 #endif
 
 #if defined(CONFIG_X86_LOCAL_APIC) && !defined(__ASSEMBLY__)
@@ -112,7 +111,8 @@ do { } while (0)
  * from the initial startup. We map APIC_BASE very early in page_setup(),
  * so this is correct in the x86 case.
  */
-#define raw_smp_processor_id() (read_pda(cpu_number))
+DECLARE_PER_CPU(int, cpu_number);
+#define raw_smp_processor_id() (x86_read_percpu(cpu_number))
 
 extern cpumask_t cpu_callout_map;
 extern cpumask_t cpu_callin_map;

===================================================================

include/asm-i386/unwind.h +1 -1
@@ -71,7 +71,7 @@ static inline void arch_unw_init_blocked
 	info->regs.xss = __KERNEL_DS;
 	info->regs.xds = __USER_DS;
 	info->regs.xes = __USER_DS;
-	info->regs.xfs = __KERNEL_PDA;
+	info->regs.xfs = __KERNEL_PERCPU;
 }
 
 extern asmlinkage int arch_unwind_init_running(struct unwind_frame_info *,
#7 lore raw

PATCH 7/28 cleanups to help using per-cpu variables from asm

In reply to Jeremy Fitzhardinge

This patch does a few small cleanups:
- use PER_CPU_NAME to generate the names of per-cpu variables
- use lea to add the per_cpu offset in PER_CPU(), because it doesn't affect condition flags
- add PER_CPU_VAR which allows direct access to pre-cpu variables with the %fs: prefix on SMP.

---
 include/asm-i386/percpu.h |   12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

===================================================================

include/asm-i386/percpu.h +7 -5
@@ -16,12 +16,14 @@
  *    PER_CPU(cpu_gdt_descr, %ebx)
  */
 #ifdef CONFIG_SMP
+#define PER_CPU(var, reg)				\
+	movl %fs:per_cpu__##this_cpu_off, reg;		\
+	lea per_cpu__##var(reg), reg
+#define PER_CPU_VAR(var)	%fs:per_cpu__##var
+#else /* ! SMP */
 #define PER_CPU(var, reg)			\
-	movl %fs:per_cpu__this_cpu_off, reg;		\
-	addl $per_cpu__##var, reg
-#else /* ! SMP */
-#define PER_CPU(var, reg) \
-	movl $per_cpu__##var, reg;
+	movl $per_cpu__##var, reg
+#define PER_CPU_VAR(var)	per_cpu__##var
 #endif	/* SMP */
 
 #else /* ...!ASSEMBLY */
#8 lore raw

PATCH 8/28 Define per_cpu_offset

In reply to Jeremy Fitzhardinge

Define per_cpu_offset in asm-i386/percpu.h when SMP defined, like asm-generic/percpu.h does for UP.

---
 include/asm-i386/percpu.h |    2 ++
 1 file changed, 2 insertions(+)

===================================================================

include/asm-i386/percpu.h +2 -0
@@ -34,6 +34,8 @@
 
 /* This is used for other cpus to find our section. */
 extern unsigned long __per_cpu_offset[];
+
+#define per_cpu_offset(x) (__per_cpu_offset[x])
 
 /* Separate out the type, so (int[3], foo) works. */
 #define DECLARE_PER_CPU(type, name) extern __typeof__(type) per_cpu__##name
#9 lore raw

PATCH 9/28 Fix UP gdt bugs

In reply to Jeremy Fitzhardinge

Fixes two problems with the GDT when compiling for uniprocessor:
- There's no percpu segment, so trying to load its selector into %fs fails. Use a null selector instead.
- The real gdt needs to be loaded at some point. Do it in cpu_init().

---
 arch/i386/kernel/cpu/common.c |   13 +++++++++++++
 arch/i386/kernel/smpboot.c    |   12 ------------
 include/asm-i386/processor.h  |    1 +
 include/asm-i386/segment.h    |    4 ++++
 4 files changed, 18 insertions(+), 12 deletions(-)

===================================================================

arch/i386/kernel/cpu/common.c +13 -0
@@ -644,6 +644,18 @@ struct pt_regs * __devinit idle_regs(str
 	return regs;
 }
 
+/* Current gdt points %fs at the "master" per-cpu area: after this,
+ * it's on the real one. */
+void switch_to_new_gdt(void)
+{
+	struct Xgt_desc_struct gdt_descr;
+
+	gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
+	gdt_descr.size = GDT_SIZE - 1;
+	load_gdt(&gdt_descr);
+	asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory");
+}
+
 /*
  * cpu_init() initializes state that is per-CPU. Some data is already
  * initialized (naturally) in the bootstrap process, such as the GDT
@@ -674,6 +688,7 @@ void __cpuinit cpu_init(void)
 	}
 
 	load_idt(&idt_descr);
+	switch_to_new_gdt();
 
 	/*
 	 * Set up and load the per-CPU TSS and LDT

===================================================================

arch/i386/kernel/smpboot.c +0 -12
@@ -1176,18 +1176,6 @@ void __init native_smp_prepare_cpus(unsi
 	smp_boot_cpus(max_cpus);
 }
 
-/* Current gdt points %fs at the "master" per-cpu area: after this,
- * it's on the real one. */
-static inline void switch_to_new_gdt(void)
-{
-	struct Xgt_desc_struct gdt_descr;
-
-	gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
-	gdt_descr.size = GDT_SIZE - 1;
-	load_gdt(&gdt_descr);
-	asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory");
-}
-
 void __init native_smp_prepare_boot_cpu(void)
 {
 	unsigned int cpu = smp_processor_id();

===================================================================

include/asm-i386/processor.h +1 -0
@@ -777,6 +777,7 @@ extern int sysenter_setup(void);
 extern int sysenter_setup(void);
 
 extern void cpu_set_gdt(int);
+extern void switch_to_new_gdt(void);
 extern void cpu_init(void);
 
 #endif /* __ASM_I386_PROCESSOR_H */

===================================================================

include/asm-i386/segment.h +4 -0
@@ -75,7 +75,11 @@
 #define __ESPFIX_SS (GDT_ENTRY_ESPFIX_SS * 8)
 
 #define GDT_ENTRY_PERCPU			(GDT_ENTRY_KERNEL_BASE + 15)
+#ifdef CONFIG_SMP
 #define __KERNEL_PERCPU (GDT_ENTRY_PERCPU * 8)
+#else
+#define __KERNEL_PERCPU 0
+#endif
 
 #define GDT_ENTRY_DOUBLEFAULT_TSS	31
 
#10 lore raw

PATCH 10/28 i386: map enough initial memory to create lowmem mappings

In reply to Jeremy Fitzhardinge

head.S creates the very initial pagetable for the kernel. This just maps enough space for the kernel itself, and an allocation bitmap. The amount of mapped memory is rounded up to 4Mbytes, and so this typically ends up mapping 8Mbytes of memory.

When booting, pagetable_init() needs to create mappings for all lowmem, and the pagetables for these mappings are allocated from the free pages around the kernel in low memory. If the number of pagetable pages + kernel size exceeds head.S's initial mapping, it will end up faulting on an unmapped page. This will only happen with specific combinations of kernel size and memory size.

This patch makes sure that head.S also maps enough space to fit the kernel pagetables as well as the kernel itself. It ends up using an additional two pages of unreclaimable memory.

Cc Andi Kleen <[email protected]>
Cc Zachary Amsden <[email protected]>
Cc Chris Wright <[email protected]>
Cc "Eric W. Biederman" <[email protected]>
Cc Linus Torvalds <[email protected]>,
---
 arch/i386/kernel/asm-offsets.c |    6 ++++++
 arch/i386/kernel/head.S        |   25 ++++++++++++++++++++-----
 2 files changed, 26 insertions(+), 5 deletions(-)

===================================================================

arch/i386/kernel/asm-offsets.c +6 -0
@@ -11,6 +11,7 @@
 #include <linux/suspend.h>
 #include <asm/ucontext.h>
 #include "sigframe.h"
+#include <asm/pgtable.h>
 #include <asm/fixmap.h>
 #include <asm/processor.h>
 #include <asm/thread_info.h>
@@ -96,6 +97,11 @@ void foo(void)
 		 sizeof(struct tss_struct));
 
 	DEFINE(PAGE_SIZE_asm, PAGE_SIZE);
+	DEFINE(PAGE_SHIFT_asm, PAGE_SHIFT);
+	DEFINE(PTRS_PER_PTE, PTRS_PER_PTE);
+	DEFINE(PTRS_PER_PMD, PTRS_PER_PMD);
+	DEFINE(PTRS_PER_PGD, PTRS_PER_PGD);
+
 	DEFINE(VDSO_PRELINK_asm, VDSO_PRELINK);
 
 	OFFSET(crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx);

===================================================================

arch/i386/kernel/head.S +20 -5
@@ -34,17 +34,32 @@
 
 /*
  * 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, but only in low memory, which means
- * 2^32/4096/8 = 128K worst case (4G/4G split.)
+ * and including _end* we need mapped initially.
+ * We need:
+ *  - one bit for each possible page, but only in low memory, which means
+ *     2^32/4096/8 = 128K worst case (4G/4G split.)
+ *  - enough space to map all low memory, which means
+ *     (2^32/4096) / 1024 pages (worst case, non PAE)
+ *     (2^32/4096) / 512 + 4 pages (worst case for PAE)
+ *  - a few pages for allocator use before the kernel pagetable has
+ *     been set up
  *
  * Modulo rounding, each megabyte assigned here requires a kilobyte of
  * memory, which is currently unreclaimed.
  *
  * This should be a multiple of a page.
  */
-#define INIT_MAP_BEYOND_END	(128*1024)
-
+LOW_PAGES = 1<<(32-PAGE_SHIFT_asm)
+
+#if PTRS_PER_PMD > 1
+PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PMD) + PTRS_PER_PGD
+#else
+PAGE_TABLE_SIZE = (LOW_PAGES / PTRS_PER_PGD)
+#endif
+BOOTBITMAP_SIZE = LOW_PAGES / 8
+ALLOCATOR_SLOP = 4
+
+INIT_MAP_BEYOND_END = BOOTBITMAP_SIZE + (PAGE_TABLE_SIZE + ALLOCATOR_SLOP)*PAGE_SIZE_asm
 
 /*
  * 32-bit kernel entrypoint; only used by the boot CPU.  On entry,
#11 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

arch/i386/kernel/head.S line 53

Quoted 3 lines · Jeremy Fitzhardinge
arch/i386/kernel/head.S +2 -1
-
+LOW_PAGES = 1<<(32-PAGE_SHIFT_asm)
+

Again, for debugging... it would be interesting to replace this with:

LOW_PAGES = (0x100000000-__PAGE_OFFSET) >> PAGE_SHIFT_asm

... to smoke out further problems; this will take the strict definition of "lowmem" (modulo the pci region, which someone unwisely made dynamically adjustable... which would have been fine if initramfs could live in highmem, which it can't yet.)

-hpa

#12 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 9 lines · H. Peter Anvin

arch/i386/kernel/head.S line 53

Quoted 3 lines · Jeremy Fitzhardinge
arch/i386/kernel/head.S +2 -1
-
+LOW_PAGES = 1<<(32-PAGE_SHIFT_asm)
+

Again, for debugging... it would be interesting to replace this with:

LOW_PAGES = (0x100000000-__PAGE_OFFSET) >> PAGE_SHIFT_asm

LOW_PAGES = (0x100000000ULL - __PAGE_OFFSET) >> PAGE_SHIT_asm

Jan

#13 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 1 line · Jan Engelhardt

LOW_PAGES = (0x100000000ULL - __PAGE_OFFSET) >> PAGE_SHIT_asm

The assembler does not now anything about ULL.

Andreas.

Signature · 4 lines
Andreas Schwab, SuSE Labs, [email protected]
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."
#14 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to Jeremy Fitzhardinge
Quoted 16 lines · Jeremy Fitzhardinge

head.S creates the very initial pagetable for the kernel. This just maps enough space for the kernel itself, and an allocation bitmap. The amount of mapped memory is rounded up to 4Mbytes, and so this typically ends up mapping 8Mbytes of memory.

When booting, pagetable_init() needs to create mappings for all lowmem, and the pagetables for these mappings are allocated from the free pages around the kernel in low memory. If the number of pagetable pages + kernel size exceeds head.S's initial mapping, it will end up faulting on an unmapped page. This will only happen with specific combinations of kernel size and memory size.

This patch makes sure that head.S also maps enough space to fit the kernel pagetables as well as the kernel itself. It ends up using an additional two pages of unreclaimable memory.

Quoted 7 lines
Cc Andi Kleen <[email protected]>
Cc Zachary Amsden <[email protected]>
Cc Chris Wright <[email protected]>
Cc "Eric W. Biederman" <[email protected]>
Cc Linus Torvalds <[email protected]>,

Is some version of this going in for 2.6.21, or is it not a real problem?

#15 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines

Is some version of this going in for 2.6.21, or is it not a real problem?

When it's only seen with Xen it's not a real problem right now.

-Andi

#16 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines · Andi Kleen
Quoted 1 line

Is some version of this going in for 2.6.21, or is it not a real problem?

When it's only seen with Xen it's not a real problem right now.

It's not just seen only with Xen, though. It will affect all kernels in a particular range of sizes, and we have ordinary kernels covering that range (in fact, Xen hardly affects size in that way.)

-hpa

#17 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 8 lines · On Thursday 19 April 2007 22:55:50 H. Peter Anvin wrote:
Quoted 3 lines · Andi Kleen
Quoted 1 line

Is some version of this going in for 2.6.21, or is it not a real problem?

When it's only seen with Xen it's not a real problem right now.

It's not just seen only with Xen, though. It will affect all kernels in a particular range of sizes, and we have ordinary kernels covering that range (in fact, Xen hardly affects size in that way.)

Then we would have seen reports surely?

-Andi

#18 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 7 lines · Andi Kleen
Quoted 4 lines · On Thursday 19 April 2007 22:55:50 H. Peter Anvin wrote:
Quoted 2 lines · Andi Kleen
Quoted 1 line

Is some version of this going in for 2.6.21, or is it not a real problem?

When it's only seen with Xen it's not a real problem right now.

It's not just seen only with Xen, though. It will affect all kernels in a particular range of sizes, and we have ordinary kernels covering that range (in fact, Xen hardly affects size in that way.)

Then we would have seen reports surely?

The critical size window is relatively small, though. The size of the uncompressed kernel mod 4 MB must be above 3.75 MB or so. If you have less than 1 GB RAM, the window is even smaller. This is strictly configuration-dependent, and has nothing to do with Xen. It just so happened that the first people to detect and diagnose it (as opposed to just "it doesn't boot") were Xen people.

-hpa

#19 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 10 lines · H. Peter Anvin
Quoted 9 lines · Andi Kleen
Quoted 7 lines · On Thursday 19 April 2007 22:55:50 H. Peter Anvin wrote:
Quoted 3 lines · Andi Kleen
Quoted 2 lines

Is some version of this going in for 2.6.21, or is it not a real problem?

When it's only seen with Xen it's not a real problem right now.

It's not just seen only with Xen, though. It will affect all kernels in a particular range of sizes, and we have ordinary kernels covering that range (in fact, Xen hardly affects size in that way.)

Quoted 8 lines
Quoted 1 line

Then we would have seen reports surely?

The critical size window is relatively small, though. The size of the uncompressed kernel mod 4 MB must be above 3.75 MB or so. If you have less than 1 GB RAM, the window is even smaller. This is strictly configuration-dependent, and has nothing to do with Xen. It just so happened that the first people to detect and diagnose it (as opposed to just "it doesn't boot") were Xen people.

I never saw a description of the symptoms of encountering this bug. Does it just hang, or what?

#20 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 6 lines · Chuck Ebbert
Quoted 4 lines · H. Peter Anvin
Quoted 2 lines · Andi Kleen

Then we would have seen reports surely?

Yes, I would have thought so. It surprised me that such an obvious bug could be there, apparently for a long time. But it's real, and potentially affects everyone. It probably doesn't affect highly modular distros much, since the kernel itself will be relatively small.

Quoted 3 lines

I never saw a description of the symptoms of encountering this bug. Does it just hang, or what?

You get an early-fault message on-screen, assuming that's enabled; otherwise it will just appear to hang. It happens in pagetable_init, when it allocates a new pagetable above the head.S mapping (8M in my case). It will only hit if the kernel size approaches a 4M boundary, since it won't leave enough space mapped to construct the lowmem mappings.

It only affects native booting, since under Xen all those mappings have already been constructed. It happened to me with a paravirt kernel that happened to Xen compiled into it, but that was irrelevent (though misleading; the 40k difference in kernel size was enough to make it not happen in a non-Xen kernel).

J

#21 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 23 lines · Jeremy Fitzhardinge
Quoted 6 lines · Chuck Ebbert
Quoted 4 lines · H. Peter Anvin
Quoted 2 lines · Andi Kleen

Then we would have seen reports surely?

Yes, I would have thought so. It surprised me that such an obvious bug could be there, apparently for a long time. But it's real, and potentially affects everyone. It probably doesn't affect highly modular distros much, since the kernel itself will be relatively small.

Quoted 3 lines

I never saw a description of the symptoms of encountering this bug. Does it just hang, or what?

You get an early-fault message on-screen, assuming that's enabled; otherwise it will just appear to hang. It happens in pagetable_init, when it allocates a new pagetable above the head.S mapping (8M in my case). It will only hit if the kernel size approaches a 4M boundary, since it won't leave enough space mapped to construct the lowmem mappings.

Quoted 5 lines

It only affects native booting, since under Xen all those mappings have already been constructed. It happened to me with a paravirt kernel that happened to Xen compiled into it, but that was irrelevent (though misleading; the 40k difference in kernel size was enough to make it not happen in a non-Xen kernel).

I happened to be looking at this stretch of code and I have realized that this is quite simply the wrong fix.

The problem is that it depends intimately on the details of alloc_bootmem_pages_low. Essentially the problem is that when we are setting up the identity mappings in paging_init we assume the identity mappings already exist.

If there are holes in the memory map or someone changes the way pages are returned from alloc_bootmem_pages_low() this code will break again.

The only way to ensure this will not happen is to do what we do on x86_64 and map the new page table page into our address space before we write to it. Assuming the page we allocate is already mapped is simply not robust.

Eric

#22 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 4 lines · Eric W. Biederman

I happened to be looking at this stretch of code and I have realized that this is quite simply the wrong fix.

Quoted 9 lines

The problem is that it depends intimately on the details of alloc_bootmem_pages_low. Essentially the problem is that when we are setting up the identity mappings in paging_init we assume the identity mappings already exist.

If there are holes in the memory map or someone changes the way pages are returned from alloc_bootmem_pages_low() this code will break again.

How would holes in the memory map affect it?

-hpa

#23 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to Eric W. Biederman
Quoted 5 lines · Eric W. Biederman

The only way to ensure this will not happen is to do what we do on x86_64 and map the new page table page into our address space before we write to it. Assuming the page we allocate is already mapped is simply not robust.

So you mean make alloc_bootmem make sure there's a mapping for the returned page? That would be simple enough, though it might interact strangely with the actual construction of the memory mappings (what if the page alloc_bootmem just allocated for the pagetable is the page pagetable_init is actually trying to map?).

I'm not sure about the issue with holes. I assume you mean that just because head.S maps a chunk of memory, it isn't necessarily available for allocation because its a hole. But does alloc_bootmem know to avoid them anyway? Has it already parsed e820 at that point?

J

#24 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 13 lines · Jeremy Fitzhardinge
Quoted 5 lines · Eric W. Biederman

The only way to ensure this will not happen is to do what we do on x86_64 and map the new page table page into our address space before we write to it. Assuming the page we allocate is already mapped is simply not robust.

So you mean make alloc_bootmem make sure there's a mapping for the returned page? That would be simple enough, though it might interact strangely with the actual construction of the memory mappings (what if the page alloc_bootmem just allocated for the pagetable is the page pagetable_init is actually trying to map?).

Quoted 5 lines

I'm not sure about the issue with holes. I assume you mean that just because head.S maps a chunk of memory, it isn't necessarily available for allocation because its a hole. But does alloc_bootmem know to avoid them anyway? Has it already parsed e820 at that point?

Since we allocate the maximum possible memory statically, I fail to see how holes could make the situation any worse, or better.

-hpa

#25 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 2 lines · H. Peter Anvin

Since we allocate the maximum possible memory statically, I fail to see how holes could make the situation any worse, or better.

No, we map enough space to map 4G (~4 pages), but we don't actually map 4G. If a hole happened to start within that 4 page mapping, then the memory still wouldn't be available for allocation.

I think this is a bit of a spurious argument though, since if it were really a problem we'd have to worry about holes hitting the kernel image too. As far as I can see, that's not considered to be a problem.

I think the real point is that there's currently a subtle dependency between head.S and bootmem allocation which happens between start_kernel and pagetable_init. Your patch preventing over-mapping should make them easier to smoke out as it currently stands, but eliminating the problem by making alloc_bootmem create the mappings for itself does have appeal. There would still be the dependency on head.S to map the kernel itself and the bootmem allocator bitmap.

J

#26 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 12 lines · Jeremy Fitzhardinge
Quoted 2 lines · H. Peter Anvin

Since we allocate the maximum possible memory statically, I fail to see how holes could make the situation any worse, or better.

No, we map enough space to map 4G (~4 pages), but we don't actually map 4G. If a hole happened to start within that 4 page mapping, then the memory still wouldn't be available for allocation.

I think this is a bit of a spurious argument though, since if it were really a problem we'd have to worry about holes hitting the kernel image too. As far as I can see, that's not considered to be a problem.

Quoted 8 lines

I think the real point is that there's currently a subtle dependency between head.S and bootmem allocation which happens between start_kernel and pagetable_init. Your patch preventing over-mapping should make them easier to smoke out as it currently stands, but eliminating the problem by making alloc_bootmem create the mappings for itself does have appeal. There would still be the dependency on head.S to map the kernel itself and the bootmem allocator bitmap.

Agreed. However, saying that your patch shouldn't go into the mainline kernel until that has been fixed is spurious and wrong. It fixes a real problem with minimal risk.

-hpa

#27 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines · H. Peter Anvin

Agreed. However, saying that your patch shouldn't go into the mainline kernel until that has been fixed is spurious and wrong. It fixes a real problem with minimal risk.

For a stable and frozen kernel it is probably the best we can do.

However the code is based on confusion of what should be happening and it increases the complexity of the code and pushes us towards the edge of code that is so crufty it is impossible to maintain.

We need to simplify this mess not make it more convoluted.

Did anyone else notice that if we don't have PAE we continue to use the initial page tables 4K page mappings of the kernel?

This probably explains why experiments to 4MB align the i386 kernel to improve performance failed.

Eric

#28 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to Jeremy Fitzhardinge
Quoted 11 lines · Jeremy Fitzhardinge
Quoted 2 lines · H. Peter Anvin

Since we allocate the maximum possible memory statically, I fail to see how holes could make the situation any worse, or better.

No, we map enough space to map 4G (~4 pages), but we don't actually map 4G. If a hole happened to start within that 4 page mapping, then the memory still wouldn't be available for allocation.

I think this is a bit of a spurious argument though, since if it were really a problem we'd have to worry about holes hitting the kernel image too. As far as I can see, that's not considered to be a problem.

- holes hitting the kernel image is not something we can do anything about.

- I have always believed we need to export enough information so the bootloader can verify that we don't hit a hole in the initial kernel image.

- I know of one system that had BIOS tables at 16MB I believe (and thus had a fairly low hole).

- Given that we are actually increasing (not decreasing) the number of scenarios where we boot the kernel it probably makes sense to be as robust as we can.

- If we support putting the ramdisk immediately after the kernel image in memory we will actually hit this case in practice.

Quoted 7 lines

I think the real point is that there's currently a subtle dependency between head.S and bootmem allocation which happens between start_kernel and pagetable_init. Your patch preventing over-mapping should make them easier to smoke out as it currently stands, but eliminating the problem by making alloc_bootmem create the mappings for itself does have appeal. There would still be the dependency on head.S to map the kernel itself and the bootmem allocator bitmap.

Agreed. However that is essentially all statically allocated memory and the best we can do at this point in time.

Eric

#29 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 4 lines · Eric W. Biederman

- I know of one system that had BIOS tables at 16MB I believe (and thus had a fairly low hole).

Please name names, otherwise this is just rumouring. Seriously. We have enough cargo-cult programming as it is.

A lot of old ISA systems had an option to put a memory hole at 15-16 MB to support ISA video cards with linear frame buffer. It very rarely got used, since VLB took over around the same time.

-hpa

#30 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 8 lines · H. Peter Anvin
Quoted 4 lines · Eric W. Biederman

- I know of one system that had BIOS tables at 16MB I believe (and thus had a fairly low hole).

Please name names, otherwise this is just rumouring. Seriously. We have enough cargo-cult programming as it is.

The unisys systems. I don't remember exactly where they put there ACPI tables but it was low enough that the default address for the kdump kernels conflicted with it. So this is likely 16MB or 32MB

Quoted 3 lines

A lot of old ISA systems had an option to put a memory hole at 15-16 MB to support ISA video cards with linear frame buffer. It very rarely got used, since VLB took over around the same time.

No this was a much larger and more modern system.

Eric

#31 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to H. Peter Anvin
Quoted 8 lines · On Monday 23 April 2007 19:45:41 H. Peter Anvin wrote:
Quoted 4 lines · Eric W. Biederman

- I know of one system that had BIOS tables at 16MB I believe (and thus had a fairly low hole).

Please name names, otherwise this is just rumouring. Seriously. We have enough cargo-cult programming as it is.

Unisys did this at some point in their large machines, but they fixed that in a later BIOS.

Anyways, boot up should be robust against any holes if possible.

-Andi

#32 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to H. Peter Anvin
Quoted 2 lines · H. Peter Anvin

Since we allocate the maximum possible memory statically, I fail to see how holes could make the situation any worse, or better.

Consider a memory hole of size 8M immediately after our bootmem bitmap. head.S which knows nothing of holes will map the pages of the hole into the initial page tables assuming that is where the page tables will live.

alloc_bootmem_low_pages which understands holes will skip those pages and return a higher address, which is outside of our initial mapping of pages.

As for how to fix this, we need to call boot_ioremap or better bt_ioremap on the address returned from alloc_bootmem_pages_low to force the allocated page into the page table. After we have setup the page table page we can call unmap the page to free up the scarce early mapping resource.

Eric

#33 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 5 lines · Eric W. Biederman

Consider a memory hole of size 8M immediately after our bootmem bitmap. head.S which knows nothing of holes will map the pages of the hole into the initial page tables assuming that is where the page tables will live.

Sure, but considering we're only talking about mapping an additional *2* pages, the chances are that it will affect a kernel as much as the early head.S mapping. It's well within the variability of fairly similar kernel configs.

Quoted 6 lines

As for how to fix this, we need to call boot_ioremap or better bt_ioremap on the address returned from alloc_bootmem_pages_low to force the allocated page into the page table. After we have setup the page table page we can call unmap the page to free up the scarce early mapping resource.

Would that be necessary? Is there any need to remap it? Couldn't you just do the straightforward phys->virt mapping and use the page where it lies?

J

#34 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 11 lines · Jeremy Fitzhardinge
Quoted 5 lines · Eric W. Biederman

Consider a memory hole of size 8M immediately after our bootmem bitmap. head.S which knows nothing of holes will map the pages of the hole into the initial page tables assuming that is where the page tables will live.

Sure, but considering we're only talking about mapping an additional *2* pages, the chances are that it will affect a kernel as much as the early head.S mapping. It's well within the variability of fairly similar kernel configs.

If (cpu_has_pse) it may only be an additional two pages. INIT_MAP_BEYOND is currently mapping a lot more then that.

Quoted 10 lines
Quoted 6 lines

As for how to fix this, we need to call boot_ioremap or better bt_ioremap on the address returned from alloc_bootmem_pages_low to force the allocated page into the page table. After we have setup the page table page we can call unmap the page to free up the scarce early mapping resource.

Would that be necessary? Is there any need to remap it? Couldn't you just do the straightforward phys->virt mapping and use the page where it lies?

Catch-22. If the page is not mapped yet you can't use it where it lies.

Eric

#35 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines · Eric W. Biederman

If (cpu_has_pse) it may only be an additional two pages. INIT_MAP_BEYOND is currently mapping a lot more then that.

Ah, yes. It allocates an extra two pages for pagetables, and then maps an extra 8MB or so.

Quoted 6 lines
Quoted 4 lines

Would that be necessary? Is there any need to remap it? Couldn't you just do the straightforward phys->virt mapping and use the page where it lies?

Catch-22. If the page is not mapped yet you can't use it where it lies.

Why not? Er, except in the case where the page is needed to map itself
- but that can be dealt with with a transient fixmap mapping.

J

#36 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 4 lines · Jeremy Fitzhardinge

Why not? Er, except in the case where the page is needed to map itself
- but that can be dealt with with a transient fixmap mapping.

It would be *trivial* to make a certain number of page table slots available at the end of the head.S-generated map.

-hpa

#37 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 2 lines · H. Peter Anvin

It would be *trivial* to make a certain number of page table slots available at the end of the head.S-generated map.

Or you could use a fixmap.

J

#38 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 5 lines · Jeremy Fitzhardinge
Quoted 2 lines · H. Peter Anvin

It would be *trivial* to make a certain number of page table slots available at the end of the head.S-generated map.

Or you could use a fixmap.

That certain number of page table slots should be the fixmap slots. If you do that bt_ioremap works, we remove a boatload of special cases and the code becomes simpler.

Eric

#39 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 10 lines · Eric W. Biederman
Quoted 4 lines · Jeremy Fitzhardinge
Quoted 2 lines · H. Peter Anvin

It would be *trivial* to make a certain number of page table slots available at the end of the head.S-generated map.

Or you could use a fixmap.

That certain number of page table slots should be the fixmap slots. If you do that bt_ioremap works, we remove a boatload of special cases and the code becomes simpler.

It's probably worth noting that several of the non-PC ports already do exactly this, although in a hacky way. Grep for pg0 and you'll find it.

-hpa

#40 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to Jeremy Fitzhardinge
Quoted 23 lines · Jeremy Fitzhardinge
Quoted 6 lines · Chuck Ebbert
Quoted 4 lines · H. Peter Anvin
Quoted 2 lines · Andi Kleen

Then we would have seen reports surely?

Yes, I would have thought so. It surprised me that such an obvious bug could be there, apparently for a long time. But it's real, and potentially affects everyone. It probably doesn't affect highly modular distros much, since the kernel itself will be relatively small.

Quoted 3 lines

I never saw a description of the symptoms of encountering this bug. Does it just hang, or what?

You get an early-fault message on-screen, assuming that's enabled; otherwise it will just appear to hang. It happens in pagetable_init, when it allocates a new pagetable above the head.S mapping (8M in my case). It will only hit if the kernel size approaches a 4M boundary, since it won't leave enough space mapped to construct the lowmem mappings.

Quoted 5 lines

It only affects native booting, since under Xen all those mappings have already been constructed. It happened to me with a paravirt kernel that happened to Xen compiled into it, but that was irrelevent (though misleading; the 40k difference in kernel size was enough to make it not happen in a non-Xen kernel).

Jeremy did your kernel have PAE enabled?

It just occurred to me that we have at all of the memory below 1M (say about 512K) mapped and available to setup new mappings.

The only way I can see a page fault happening is if you were using a PAE enabled kernel (so you were not updating the current page tables) and you have more than 256M of low memory, and we don't get any much extra from always mapping 4M at a time.

Eric

#41 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 9 lines · Eric W. Biederman

Jeremy did your kernel have PAE enabled?

It just occurred to me that we have at all of the memory below 1M (say about 512K) mapped and available to setup new mappings.

The only way I can see a page fault happening is if you were using a PAE enabled kernel (so you were not updating the current page tables) and you have more than 256M of low memory, and we don't get any much extra from always mapping 4M at a time.

Yes, that's the situation. PAE enabled, ~768MB of memory and a large kernel which mostly fills the 8M mapping.

J

#42 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 6 lines · Jeremy Fitzhardinge
Quoted 5 lines · Eric W. Biederman

Jeremy did your kernel have PAE enabled?

It just occurred to me that we have at all of the memory below 1M (say about 512K) mapped and available to setup new mappings.

Quoted 7 lines
Quoted 4 lines

The only way I can see a page fault happening is if you were using a PAE enabled kernel (so you were not updating the current page tables) and you have more than 256M of low memory, and we don't get any much extra from always mapping 4M at a time.

Yes, that's the situation. PAE enabled, ~768MB of memory and a large kernel which mostly fills the 8M mapping.

And it just occurred to me PSE disabled, otherwise you would not have needed more than 4 pages. I supposed you were testing the Xen case.

That said I know for certain now why we have never seen this in production because all cpus that fit this configuration also support PSE.

So the way I think we should properly fix this is:
- Enable PAE early so we are updating our current page table.
- Pass a limit to the bootmem allocator so we do not get pages pass the point where we have setup page tables.
- Handle memory allocation failures.

However only the first part is necessary to solve the problem, in practice even if you are using 4K pages.

Eric

#43 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines · Eric W. Biederman

And it just occurred to me PSE disabled, otherwise you would not have needed more than 4 pages. I supposed you were testing the Xen case.

No, actually, I wasn't. It was booting native (the Xen boot path doesn't go that way), and it should have booted like a normal native kernel despite having Xen compiled in. Hm, not sure why it wouldn't be using the PSE path.

J

#44 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 10 lines · Jeremy Fitzhardinge
Quoted 3 lines · Eric W. Biederman

And it just occurred to me PSE disabled, otherwise you would not have needed more than 4 pages. I supposed you were testing the Xen case.

No, actually, I wasn't. It was booting native (the Xen boot path doesn't go that way), and it should have booted like a normal native kernel despite having Xen compiled in. Hm, not sure why it wouldn't be using the PSE path.

Since you have PSE disabled for Xen my hunch is that somehow that got left on for your test boot.

Eric

#45 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 2 lines · Eric W. Biederman

Since you have PSE disabled for Xen my hunch is that somehow that got left on for your test boot.

No. Under Xen cpuid masks out PSE (and complains if you try to set it in a pte), but when booting native it will just use a plain unadorned cpuid.

J

#46 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 6 lines · Jeremy Fitzhardinge
Quoted 2 lines · Eric W. Biederman

Since you have PSE disabled for Xen my hunch is that somehow that got left on for your test boot.

No. Under Xen cpuid masks out PSE (and complains if you try to set it in a pte), but when booting native it will just use a plain unadorned cpuid.

Then why you had to allocate enough pages to cause a failure has me stumped. Perhaps there is some other bug?

Eric

#47 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 3 lines · Eric W. Biederman

Then why you had to allocate enough pages to cause a failure has me stumped. Perhaps there is some other bug?

Perhaps, but nothing comes to mind. I'll see what happens when I boot this kernel on real hardware (rather than kvm).

J

#48 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 6 lines · * Jeremy Fitzhardinge ([email protected]) wrote:
Quoted 2 lines · Eric W. Biederman

Then why you had to allocate enough pages to cause a failure has me stumped. Perhaps there is some other bug?

Perhaps, but nothing comes to mind. I'll see what happens when I boot this kernel on real hardware (rather than kvm).

I was using real hardware with your .config when I reproduced it.

#49 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 2 lines · Chris Wright

I was using real hardware with your .config when I reproduced it.

Yes, I first found it on real hardware. I haven't tested my fix on real hardware yet, but it seems OK on kvm.

J

#50 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

In reply to Jeremy Fitzhardinge
Quoted 9 lines · Jeremy Fitzhardinge
Quoted 3 lines · Eric W. Biederman

And it just occurred to me PSE disabled, otherwise you would not have needed more than 4 pages. I supposed you were testing the Xen case.

No, actually, I wasn't. It was booting native (the Xen boot path doesn't go that way), and it should have booted like a normal native kernel despite having Xen compiled in. Hm, not sure why it wouldn't be using the PSE path.

Any chance you had CONFIG_DEBUG_PAGEALLOC selected?

Apparently that also disabled PSE...

Eric

#51 lore raw

Re: [PATCH 10/28] i386: map enough initial memory to create lowmem mappings

Quoted 2 lines · Eric W. Biederman

Any chance you had CONFIG_DEBUG_PAGEALLOC selected?

Yes, that's quite likely.

J

#52 lore raw

PATCH 11/28 x86: incremental update for i386 and x86-64 check_bugs

In reply to Jeremy Fitzhardinge

i386 bugs.c shouldn't refer to identify_boot_cpu yet, since it doesn't get introduced until the identify_cpu patch.

Remove spurious comments, headers and keywords from x86-64 bugs.[ch].

---
 arch/i386/kernel/cpu/bugs.c |    2 +-
 arch/x86_64/kernel/bugs.c   |    9 +--------
 include/asm-i386/bugs.h     |    2 +-
 3 files changed, 3 insertions(+), 10 deletions(-)

===================================================================

arch/i386/kernel/cpu/bugs.c +1 -1
@@ -177,7 +177,7 @@ static void __init check_config(void)
 
 void __init check_bugs(void)
 {
-	identify_boot_cpu();
+	identify_cpu(&boot_cpu_data);
 #ifndef CONFIG_SMP
 	printk("CPU: ");
 	print_cpu_info(&boot_cpu_data);

===================================================================

arch/x86_64/kernel/bugs.c +1 -8
@@ -3,19 +3,12 @@
  *
  *  Copyright (C) 1994  Linus Torvalds
  *  Copyright (C) 2000  SuSE
- *
- * This is included by init/main.c to check for architecture-dependent bugs.
- *
- * Needs:
- *	void check_bugs(void);
  */
 
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/alternative.h>
 #include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/msr.h>
-#include <asm/pda.h>
 
 void __init check_bugs(void)
 {

===================================================================

include/asm-i386/bugs.h +1 -1
@@ -7,6 +7,6 @@
 #ifndef _ASM_I386_BUG_H
 #define _ASM_I386_BUG_H
 
-extern void __init check_bugs(void);
+void check_bugs(void);
 
 #endif	/* _ASM_I386_BUG_H */
#53 lore raw

PATCH 12/28 i386: now its ok to use identify_boot_cpu

In reply to Jeremy Fitzhardinge
---
 arch/i386/kernel/cpu/bugs.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

arch/i386/kernel/cpu/bugs.c +1 -1
@@ -177,7 +177,7 @@ static void __init check_config(void)
 
 void __init check_bugs(void)
 {
-	identify_cpu(&boot_cpu_data);
+	identify_boot_cpu();
 #ifndef CONFIG_SMP
 	printk("CPU: ");
 	print_cpu_info(&boot_cpu_data);
#54 lore raw

PATCH 13/28 paravirt: flush lazy mmu updates on kunmap_atomic

In reply to Jeremy Fitzhardinge

kunmap_atomic should flush any pending lazy mmu updates, mainly to be consistent with kmap_atomic, and to preserve its normal behaviour.

---
 arch/i386/mm/highmem.c |    1 +
 1 file changed, 1 insertion(+)

===================================================================

arch/i386/mm/highmem.c +1 -0
@@ -72,6 +72,7 @@ void kunmap_atomic(void *kvaddr, enum km
 #endif
 	}
 
+	arch_flush_lazy_mmu_mode();
 	pagefault_enable();
 }
 
#55 lore raw

PATCH 14/28 fix paravirt-documentation

In reply to Jeremy Fitzhardinge

Remove #defines, add enum for PARAVIRT_LAZY_FLUSH.

---
 include/asm-i386/paravirt.h |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

===================================================================

include/asm-i386/paravirt.h +1 -6
@@ -30,6 +30,7 @@ enum paravirt_lazy_mode {
 	PARAVIRT_LAZY_NONE = 0,
 	PARAVIRT_LAZY_MMU = 1,
 	PARAVIRT_LAZY_CPU = 2,
+	PARAVIRT_LAZY_FLUSH = 3,
 };
 
 struct paravirt_ops
@@ -906,12 +907,6 @@ static inline void set_pmd(pmd_t *pmdp, 
 }
 #endif	/* CONFIG_X86_PAE */
 
-/* Lazy mode for batching updates / context switch */
-#define PARAVIRT_LAZY_NONE 0
-#define PARAVIRT_LAZY_MMU  1
-#define PARAVIRT_LAZY_CPU  2
-#define PARAVIRT_LAZY_FLUSH 3
-
 #define  __HAVE_ARCH_ENTER_LAZY_CPU_MODE
 static inline void arch_enter_lazy_cpu_mode(void)
 {
#56 lore raw

PATCH 15/28 In compat mode, the return value here was uninitialized.

In reply to Jeremy Fitzhardinge

From: Zachary Amsden <[email protected]>

---
 arch/i386/kernel/sysenter.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

arch/i386/kernel/sysenter.c +1 -1
@@ -254,7 +254,7 @@ int arch_setup_additional_pages(struct l
 {
 	struct mm_struct *mm = current->mm;
 	unsigned long addr;
-	int ret;
+	int ret = 0;
 	bool compat;
 
 	down_write(&mm->mmap_sem);
#57 lore raw

PATCH 16/28 kRemove a warning about unused variable in !CONFIG_ACPI compilation.

In reply to Jeremy Fitzhardinge

From: Zachary Amsden <[email protected]>

---
 arch/i386/kernel/acpi/earlyquirk.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

arch/i386/kernel/acpi/earlyquirk.c +1 -1
@@ -21,8 +21,8 @@ static int __init nvidia_hpet_check(stru
 
 static int __init check_bridge(int vendor, int device)
 {
+#ifdef CONFIG_ACPI
 	static int warned;
-#ifdef CONFIG_ACPI
 	/* According to Nvidia all timer overrides are bogus unless HPET
 	   is enabled. */
 	if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) {
#58 lore raw

PATCH 17/28 x86: cleanup arch/i386/kernel/cpu/mcheck/p4.c

In reply to Jeremy Fitzhardinge

No, just no. You do not use goto to skip a code block. You do not return an obvious variable from a singly-inlined function and give the function a return value. You don't put unexplained comments about kmalloc in code which doesn't do dynamic allocation. And you don't leave stray warnings around for no good reason.

Also, when possible, it is better to use block scoped variables because gcc can sometime generate better code.

From: Zachary Amsden <[email protected]>

---
 arch/i386/kernel/cpu/mcheck/p4.c |   16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

===================================================================

arch/i386/kernel/cpu/mcheck/p4.c +4 -12
@@ -124,12 +124,9 @@ static void intel_init_thermal(struct cp
 
 
 /* P4/Xeon Extended MCE MSR retrieval, return 0 if unsupported */
-static inline int intel_get_extended_msrs(struct intel_mce_extended_msrs *r)
+static inline void intel_get_extended_msrs(struct intel_mce_extended_msrs *r)
 {
 	u32 h;
-
-	if (mce_num_extended_msrs == 0)
-		goto done;
 
 	rdmsr (MSR_IA32_MCG_EAX, r->eax, h);
 	rdmsr (MSR_IA32_MCG_EBX, r->ebx, h);
@@ -141,12 +138,6 @@ static inline int intel_get_extended_msr
 	rdmsr (MSR_IA32_MCG_ESP, r->esp, h);
 	rdmsr (MSR_IA32_MCG_EFLAGS, r->eflags, h);
 	rdmsr (MSR_IA32_MCG_EIP, r->eip, h);
-
-	/* can we rely on kmalloc to do a dynamic
-	 * allocation for the reserved registers?
-	 */
-done:
-	return mce_num_extended_msrs;
 }
 
 static fastcall void intel_machine_check(struct pt_regs * regs, long error_code)
@@ -155,7 +146,6 @@ static fastcall void intel_machine_check
 	u32 alow, ahigh, high, low;
 	u32 mcgstl, mcgsth;
 	int i;
-	struct intel_mce_extended_msrs dbg;
 
 	rdmsr (MSR_IA32_MCG_STATUS, mcgstl, mcgsth);
 	if (mcgstl & (1<<0))	/* Recoverable ? */
@@ -164,7 +154,9 @@ static fastcall void intel_machine_check
 	printk (KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n",
 		smp_processor_id(), mcgsth, mcgstl);
 
-	if (intel_get_extended_msrs(&dbg)) {
+	if (mce_num_extended_msrs > 0) {
+		struct intel_mce_extended_msrs dbg;
+		intel_get_extended_msrs(&dbg);
 		printk (KERN_DEBUG "CPU %d: EIP: %08x EFLAGS: %08x\n",
 			smp_processor_id(), dbg.eip, dbg.eflags);
 		printk (KERN_DEBUG "\teax: %08x ebx: %08x ecx: %08x edx: %08x\n",
#59 lore raw

PATCH 18/28 Copying of the pgd range must happen under the pgd_lock

In reply to Jeremy Fitzhardinge

Copying of the pgd range must happen under the pgd_lock. This got broken by the paravirt changes in the -mm tree. Badness can result if you copy the pgd before being added to the list when splitting or rejoining large pages.

From: Zachary Amsden <[email protected]>

---
 arch/i386/mm/pgtable.c |    8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

===================================================================

arch/i386/mm/pgtable.c +3 -5
@@ -241,18 +241,16 @@ void pgd_ctor(void *pgd, struct kmem_cac
 	/* !PAE, no pagetable sharing */
 	memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t));
 
+	spin_lock_irqsave(&pgd_lock, flags);
+
+	/* must happen under lock */
 	clone_pgd_range((pgd_t *)pgd + USER_PTRS_PER_PGD,
 			swapper_pg_dir + USER_PTRS_PER_PGD,
 			KERNEL_PGD_PTRS);
-
-	spin_lock_irqsave(&pgd_lock, flags);
-
-	/* must happen under lock */
 	paravirt_alloc_pd_clone(__pa(pgd) >> PAGE_SHIFT,
 				__pa(swapper_pg_dir) >> PAGE_SHIFT,
 				USER_PTRS_PER_PGD,
 				KERNEL_PGD_PTRS);
-
 	pgd_list_add(pgd);
 	spin_unlock_irqrestore(&pgd_lock, flags);
 }
#60 lore raw

PATCH 19/28 Dont implement native_kmap_atomic_pte for !HIGHPTE

In reply to Jeremy Fitzhardinge

Don't implement native_kmap_atomic_pte for !HIGHPTE case; it is never needed, never called, and leaving it in is just plain confusing. Making it isolated to the config where it is used may help find bugs.

From: Zachary Amsden <[email protected]>

---
 arch/i386/kernel/paravirt.c |    4 +---
 include/asm-i386/highmem.h  |    5 -----
 include/asm-i386/paravirt.h |    4 ++++
 3 files changed, 5 insertions(+), 8 deletions(-)

===================================================================

arch/i386/kernel/paravirt.c +1 -3
@@ -318,9 +318,7 @@ struct paravirt_ops paravirt_ops = {
 	.ptep_get_and_clear = native_ptep_get_and_clear,
 
 #ifdef CONFIG_HIGHPTE
-	.kmap_atomic_pte = native_kmap_atomic_pte,
-#else
-	.kmap_atomic_pte = paravirt_nop,
+	.kmap_atomic_pte = kmap_atomic,
 #endif
 
 #ifdef CONFIG_X86_PAE

===================================================================

include/asm-i386/highmem.h +0 -5
@@ -74,11 +74,6 @@ void *kmap_atomic_pfn(unsigned long pfn,
 void *kmap_atomic_pfn(unsigned long pfn, enum km_type type);
 struct page *kmap_atomic_to_page(void *ptr);
 
-static inline void *native_kmap_atomic_pte(struct page *page, enum km_type type)
-{
-	return kmap_atomic(page, type);
-}
-
 #ifndef CONFIG_PARAVIRT
 #define kmap_atomic_pte(page, type)	kmap_atomic(page, type)
 #endif

===================================================================

include/asm-i386/paravirt.h +4 -0
@@ -190,7 +190,9 @@ struct paravirt_ops
 
  	pte_t (*ptep_get_and_clear)(pte_t *ptep);
 
+#ifdef CONFIG_HIGHPTE
 	void *(*kmap_atomic_pte)(struct page *page, enum km_type type);
+#endif
 
 #ifdef CONFIG_X86_PAE
 	void (*set_pte_atomic)(pte_t *ptep, pte_t pteval);
@@ -759,12 +761,14 @@ static inline void paravirt_release_pd(u
 	PVOP_VCALL1(release_pd, pfn);
 }
 
+#ifdef CONFIG_HIGHPTE
 static inline void *kmap_atomic_pte(struct page *page, enum km_type type)
 {
 	unsigned long ret;
 	ret = PVOP_CALL2(unsigned long, kmap_atomic_pte, page, type);
 	return (void *)ret;
 }
+#endif
 
 static inline void pte_update(struct mm_struct *mm, unsigned long addr,
 			      pte_t *ptep)
#61 lore raw

PATCH 20/28 Now that the VDSO can be relocated, we can support it in VMI configurations.

In reply to Jeremy Fitzhardinge

From: Zachary Amsden <[email protected]>

---
 arch/i386/Kconfig |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

===================================================================

arch/i386/Kconfig +1 -1
@@ -220,7 +220,7 @@ config PARAVIRT
 
 config VMI
 	bool "VMI Paravirt-ops support"
-	depends on PARAVIRT && !COMPAT_VDSO
+	depends on PARAVIRT
 	help
 	  VMI provides a paravirtualized interface to the VMware ESX server
 	  (it could be used by other hypervisors in theory too, but is not
#62 lore raw

PATCH 21/28 Implement vmi_kmap_atomic_pte

In reply to Jeremy Fitzhardinge

Implement vmi_kmap_atomic_pte in terms of the backend set_linear_mapping operation. The conversion is rather straighforward; call kmap_atomic and then inform the hypervisor of the page mapping.

The _flush_tlb damage is due to macros being pulled in from highmem.h.

From: Zachary Amsden <[email protected]>

---
 arch/i386/kernel/vmi.c |   38 ++++++++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 14 deletions(-)

===================================================================

arch/i386/kernel/vmi.c +24 -14
@@ -26,6 +26,7 @@
 #include <linux/cpu.h>
 #include <linux/bootmem.h>
 #include <linux/mm.h>
+#include <linux/highmem.h>
 #include <asm/vmi.h>
 #include <asm/io.h>
 #include <asm/fixmap.h>
@@ -65,8 +66,8 @@ static struct {
 	void (*release_page)(u32, u32);
 	void (*set_pte)(pte_t, pte_t *, unsigned);
 	void (*update_pte)(pte_t *, unsigned);
-	void (*set_linear_mapping)(int, u32, u32, u32);
-	void (*flush_tlb)(int);
+	void (*set_linear_mapping)(int, void *, u32, u32);
+	void (*_flush_tlb)(int);
 	void (*set_initial_ap_state)(int, int);
 	void (*halt)(void);
   	void (*set_lazy_mode)(int mode);
@@ -217,12 +218,12 @@ static void vmi_load_esp0(struct tss_str
 
 static void vmi_flush_tlb_user(void)
 {
-	vmi_ops.flush_tlb(VMI_FLUSH_TLB);
+	vmi_ops._flush_tlb(VMI_FLUSH_TLB);
 }
 
 static void vmi_flush_tlb_kernel(void)
 {
-	vmi_ops.flush_tlb(VMI_FLUSH_TLB | VMI_FLUSH_GLOBAL);
+	vmi_ops._flush_tlb(VMI_FLUSH_TLB | VMI_FLUSH_GLOBAL);
 }
 
 /* Stub to do nothing at all; used for delays and unimplemented calls */
@@ -345,8 +346,11 @@ static void vmi_check_page_type(u32 pfn,
 #define vmi_check_page_type(p,t) do { } while (0)
 #endif
 
-static void vmi_map_pt_hook(int type, pte_t *va, u32 pfn)
-{
+#ifdef CONFIG_HIGHPTE
+static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type)
+{
+	void *va = kmap_atomic(page, type);
+
 	/*
 	 * Internally, the VMI ROM must map virtual addresses to physical
 	 * addresses for processing MMU updates.  By the time MMU updates
@@ -360,8 +364,11 @@ static void vmi_map_pt_hook(int type, pt
 	 *  args:                 SLOT                 VA    COUNT PFN
 	 */
 	BUG_ON(type != KM_PTE0 && type != KM_PTE1);
-	vmi_ops.set_linear_mapping((type - KM_PTE0)+1, (u32)va, 1, pfn);
-}
+	vmi_ops.set_linear_mapping((type - KM_PTE0)+1, va, 1, page_to_pfn(page));
+
+	return va;
+}
+#endif
 
 static void vmi_allocate_pt(u32 pfn)
 {
@@ -656,7 +663,7 @@ void vmi_bringup(void)
 {
  	/* We must establish the lowmem mapping for MMU ops to work */
 	if (vmi_ops.set_linear_mapping)
-		vmi_ops.set_linear_mapping(0, __PAGE_OFFSET, max_low_pfn, 0);
+		vmi_ops.set_linear_mapping(0, (void *)__PAGE_OFFSET, max_low_pfn, 0);
 }
 
 /*
@@ -793,8 +800,8 @@ static inline int __init activate_vmi(vo
 	para_wrap(set_lazy_mode, vmi_set_lazy_mode, set_lazy_mode, SetLazyMode);
 
 	/* user and kernel flush are just handled with different flags to FlushTLB */
-	para_wrap(flush_tlb_user, vmi_flush_tlb_user, flush_tlb, FlushTLB);
-	para_wrap(flush_tlb_kernel, vmi_flush_tlb_kernel, flush_tlb, FlushTLB);
+	para_wrap(flush_tlb_user, vmi_flush_tlb_user, _flush_tlb, FlushTLB);
+	para_wrap(flush_tlb_kernel, vmi_flush_tlb_kernel, _flush_tlb, FlushTLB);
 	para_fill(flush_tlb_single, InvalPage);
 
 	/*
@@ -840,9 +847,12 @@ static inline int __init activate_vmi(vo
 		paravirt_ops.release_pt = vmi_release_pt;
 		paravirt_ops.release_pd = vmi_release_pd;
 	}
-#if 0
-	para_wrap(map_pt_hook, vmi_map_pt_hook, set_linear_mapping,
-		  SetLinearMapping);
+
+	/* Set linear is needed in all cases */
+	vmi_ops.set_linear_mapping = vmi_get_function(VMI_CALL_SetLinearMapping);
+#ifdef CONFIG_HIGHPTE
+	if (vmi_ops.set_linear_mapping)
+		paravirt_ops.kmap_atomic_pte = vmi_kmap_atomic_pte;
 #endif
 
 	/*
#63 lore raw

PATCH 22/28 Convert VMI timer to use clock events

In reply to Jeremy Fitzhardinge

Convert VMI timer to use clock events, making it properly able to use the NO_HZ infrastructure. On UP systems, with no local APIC, we just continue to route these events through the PIT. On systems with a local APIC, or SMP, we provide a single source interrupt chip which creates the local timer IRQ. It actually gets delivered by the APIC hardware, but we don't want to use the same local APIC clocksource processing, so we create our own handler here.

From: Zachary Amsden <[email protected]>

CC Dan Hecht <[email protected]>
CC Ingo Molnar <[email protected]>
CC Thomas Gleixner <[email protected]>
---
 arch/i386/kernel/Makefile   |    2 
 arch/i386/kernel/entry.S    |    5 
 arch/i386/kernel/vmi.c      |   26 --
 arch/i386/kernel/vmiclock.c |  318 ++++++++++++++++++++++++++++
 arch/i386/kernel/vmitime.c  |  482 -------------------------------------------
 include/asm-i386/vmi_time.h |   18 -
 6 files changed, 327 insertions(+), 524 deletions(-)

===================================================================

arch/i386/kernel/Makefile +1 -1
@@ -41,7 +41,7 @@ obj-$(CONFIG_K8_NB)		+= k8.o
 obj-$(CONFIG_K8_NB)		+= k8.o
 obj-$(CONFIG_STACK_UNWIND)	+= unwind.o
 
-obj-$(CONFIG_VMI)		+= vmi.o vmitime.o
+obj-$(CONFIG_VMI)		+= vmi.o vmiclock.o
 obj-$(CONFIG_PARAVIRT)		+= paravirt.o
 obj-y				+= pcspeaker.o
 

===================================================================

arch/i386/kernel/vmi.c +6 -20
@@ -73,6 +73,9 @@ static struct {
   	void (*set_lazy_mode)(int mode);
 } vmi_ops;
 
+/* Cached VMI operations */
+struct vmi_timer_ops vmi_timer_ops;
+
 /*
  * VMI patching routines.
  */
@@ -231,18 +234,6 @@ static void vmi_nop(void)
 {
 }
 
-/* For NO_IDLE_HZ, we stop the clock when halting the kernel */
-static fastcall void vmi_safe_halt(void)
-{
-	int idle = vmi_stop_hz_timer();
-	vmi_ops.halt();
-	if (idle) {
-		local_irq_disable();
-		vmi_account_time_restart_hz_timer();
-		local_irq_enable();
-	}
-}
-
 #ifdef CONFIG_DEBUG_PAGE_TYPE
 
 #ifdef CONFIG_X86_PAE
@@ -714,7 +705,6 @@ do {								\
 		vmi_ops.cache = (void *)rel->eip;		\
 	}							\
 } while (0)
-
 
 /*
  * Activate the VMI interface and switch into paravirtualized mode
@@ -894,8 +884,8 @@ static inline int __init activate_vmi(vo
 		paravirt_ops.get_wallclock = vmi_get_wallclock;
 		paravirt_ops.set_wallclock = vmi_set_wallclock;
 #ifdef CONFIG_X86_LOCAL_APIC
-		paravirt_ops.setup_boot_clock = vmi_timer_setup_boot_alarm;
-		paravirt_ops.setup_secondary_clock = vmi_timer_setup_secondary_alarm;
+		paravirt_ops.setup_boot_clock = vmi_time_bsp_init;
+		paravirt_ops.setup_secondary_clock = vmi_time_ap_init;
 #endif
 		paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles;
  		paravirt_ops.get_cpu_khz = vmi_cpu_khz;
@@ -907,11 +897,7 @@ static inline int __init activate_vmi(vo
 		disable_vmi_timer = 1;
 	}
 
-	/* No idle HZ mode only works if VMI timer and no idle is enabled */
-	if (disable_noidle || disable_vmi_timer)
-		para_fill(safe_halt, Halt);
-	else
-		para_wrap(safe_halt, vmi_safe_halt, halt, Halt);
+	para_fill(safe_halt, Halt);
 
 	/*
 	 * Alternative instruction rewriting doesn't happen soon enough

===================================================================

include/asm-i386/vmi_time.h +2 -16
@@ -53,22 +53,8 @@ extern unsigned long vmi_cpu_khz(void);
 extern unsigned long vmi_cpu_khz(void);
 
 #ifdef CONFIG_X86_LOCAL_APIC
-extern void __init vmi_timer_setup_boot_alarm(void);
-extern void __devinit vmi_timer_setup_secondary_alarm(void);
-extern void apic_vmi_timer_interrupt(void);
-#endif
-
-#ifdef CONFIG_NO_IDLE_HZ
-extern int vmi_stop_hz_timer(void);
-extern void vmi_account_time_restart_hz_timer(void);
-#else
-static inline int vmi_stop_hz_timer(void)
-{
-	return 0;
-}
-static inline void vmi_account_time_restart_hz_timer(void)
-{
-}
+extern void __devinit vmi_time_bsp_init(void);
+extern void __devinit vmi_time_ap_init(void);
 #endif
 
 /*

===================================================================

arch/i386/kernel/entry.S +0 -5
@@ -637,11 +637,6 @@ ENDPROC(name)
 /* The include is where all of the SMP etc. interrupts come from */
 #include "entry_arch.h"
 
-/* This alternate entry is needed because we hijack the apic LVTT */
-#if defined(CONFIG_VMI) && defined(CONFIG_X86_LOCAL_APIC)
-BUILD_INTERRUPT(apic_vmi_timer_interrupt,LOCAL_TIMER_VECTOR)
-#endif
-
 KPROBE_ENTRY(page_fault)
 	RING0_EC_FRAME
 	pushl $do_page_fault

===================================================================

arch/i386/kernel/vmiclock.c new file +318 -0

318 lines not on this page · raw message

===================================================================

arch/i386/kernel/vmitime.c deleted +0 -482

482 lines not on this page · raw message

#64 lore raw

PATCH 23/28 Fix BusLogic to stop using check_region

In reply to Jeremy Fitzhardinge

I got so sick of seing the check_region warnings from BusLogic.c I actually fixed it properly. Never use check region, reserve it before the probe with request region instead and check the error result; free region if setup fails. Should be functionally identical to the original except for fixing the potential race.

From: Zachary Amsden <[email protected]>

CC Lenoard N. Zubkoff <[email protected]>
CC Michael Clay <[email protected]>
---
 drivers/scsi/BusLogic.c |   73 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 48 insertions(+), 25 deletions(-)

===================================================================

drivers/scsi/BusLogic.c +48 -25
@@ -579,17 +579,17 @@ static void __init BusLogic_InitializePr
 	/*
 	   Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
 	 */
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe330)
 		BusLogic_AppendProbeAddressISA(0x330);
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe334)
 		BusLogic_AppendProbeAddressISA(0x334);
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe230)
 		BusLogic_AppendProbeAddressISA(0x230);
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe234)
 		BusLogic_AppendProbeAddressISA(0x234);
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe130)
 		BusLogic_AppendProbeAddressISA(0x130);
-	if (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0)
+	if (!BusLogic_ProbeOptions.LimitedProbeISA || BusLogic_ProbeOptions.Probe134)
 		BusLogic_AppendProbeAddressISA(0x134);
 }
 
@@ -795,7 +795,9 @@ static int __init BusLogic_InitializeMul
 	   host adapters are probed.
 	 */
 	if (!BusLogic_ProbeOptions.NoProbeISA)
-		if (PrimaryProbeInfo->IO_Address == 0 && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe330 : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)) {
+		if (PrimaryProbeInfo->IO_Address == 0 &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe330)) {
 			PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
 			PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
 			PrimaryProbeInfo->IO_Address = 0x330;
@@ -805,15 +807,25 @@ static int __init BusLogic_InitializeMul
 	   omitting the Primary I/O Address which has already been handled.
 	 */
 	if (!BusLogic_ProbeOptions.NoProbeISA) {
-		if (!StandardAddressSeen[1] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe334 : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0))
+		if (!StandardAddressSeen[1] &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe334))
 			BusLogic_AppendProbeAddressISA(0x334);
-		if (!StandardAddressSeen[2] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe230 : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0))
+		if (!StandardAddressSeen[2] &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe230))
 			BusLogic_AppendProbeAddressISA(0x230);
-		if (!StandardAddressSeen[3] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe234 : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0))
+		if (!StandardAddressSeen[3] &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe234))
 			BusLogic_AppendProbeAddressISA(0x234);
-		if (!StandardAddressSeen[4] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe130 : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0))
+		if (!StandardAddressSeen[4] &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe130))
 			BusLogic_AppendProbeAddressISA(0x130);
-		if (!StandardAddressSeen[5] && (BusLogic_ProbeOptions.LimitedProbeISA ? BusLogic_ProbeOptions.Probe134 : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0))
+		if (!StandardAddressSeen[5] &&
+				(!BusLogic_ProbeOptions.LimitedProbeISA ||
+				 BusLogic_ProbeOptions.Probe134))
 			BusLogic_AppendProbeAddressISA(0x134);
 	}
 	/*
@@ -2220,22 +2232,35 @@ static int __init BusLogic_init(void)
 		HostAdapter->PCI_Device = ProbeInfo->PCI_Device;
 		HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel;
 		HostAdapter->AddressCount = BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType];
+
+		/*
+		   Make sure region is free prior to probing.
+		 */
+		if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount,
+					"BusLogic"))
+			continue;
 		/*
 		   Probe the Host Adapter.  If unsuccessful, abort further initialization.
 		 */
-		if (!BusLogic_ProbeHostAdapter(HostAdapter))
+		if (!BusLogic_ProbeHostAdapter(HostAdapter)) {
+			release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
 			continue;
+		}
 		/*
 		   Hard Reset the Host Adapter.  If unsuccessful, abort further
 		   initialization.
 		 */
-		if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true))
+		if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true)) {
+			release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
 			continue;
+		}
 		/*
 		   Check the Host Adapter.  If unsuccessful, abort further initialization.
 		 */
-		if (!BusLogic_CheckHostAdapter(HostAdapter))
+		if (!BusLogic_CheckHostAdapter(HostAdapter)) {
+			release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
 			continue;
+		}
 		/*
 		   Initialize the Driver Options field if provided.
 		 */
@@ -2247,16 +2272,6 @@ static int __init BusLogic_init(void)
 		 */
 		BusLogic_AnnounceDriver(HostAdapter);
 		/*
-		   Register usage of the I/O Address range.  From this point onward, any
-		   failure will be assumed to be due to a problem with the Host Adapter,
-		   rather than due to having mistakenly identified this port as belonging
-		   to a BusLogic Host Adapter.  The I/O Address range will not be
-		   released, thereby preventing it from being incorrectly identified as
-		   any other type of Host Adapter.
-		 */
-		if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, "BusLogic"))
-			continue;
-		/*
 		   Register the SCSI Host structure.
 		 */
 
@@ -2280,6 +2295,12 @@ static int __init BusLogic_init(void)
 		   Acquire the System Resources necessary to use the Host Adapter, then
 		   Create the Initial CCBs, Initialize the Host Adapter, and finally
 		   perform Target Device Inquiry.
+
+		   From this point onward, any failure will be assumed to be due to a
+		   problem with the Host Adapter, rather than due to having mistakenly
+		   identified this port as belonging to a BusLogic Host Adapter.  The
+		   I/O Address range will not be released, thereby preventing it from
+		   being incorrectly identified as any other type of Host Adapter.
 		 */
 		if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
 		    BusLogic_ReportHostAdapterConfiguration(HostAdapter) &&
@@ -3598,6 +3619,7 @@ static void __exit BusLogic_exit(void)
 
 __setup("BusLogic=", BusLogic_Setup);
 
+#ifdef MODULE
 static struct pci_device_id BusLogic_pci_tbl[] __devinitdata = {
 	{ PCI_VENDOR_ID_BUSLOGIC, PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -3607,6 +3629,7 @@ static struct pci_device_id BusLogic_pci
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
 	{ }
 };
+#endif
 MODULE_DEVICE_TABLE(pci, BusLogic_pci_tbl);
 
 module_init(BusLogic_init);
#65 lore raw

PATCH 24/28 paravirt: drop unused ptep_get_and_clear

In reply to Jeremy Fitzhardinge

In shadow mode hypervisors, ptep_get_and_clear achieves the desired purpose of keeping the shadows in sync by issuing a native_get_and_clear, followed by a call to pte_update, which indicates the PTE has been modified.

Direct mode hypervisors (Xen) have no need for this anyway, and will trap the update using writable pagetables.

This means no hypervisor makes use of ptep_get_and_clear; there is no reason to have it in the paravirt-ops structure. Change confusing terminology about raw vs. native functions into consistent use of native_pte_xxx for operations which do not invoke paravirt-ops.

---
 arch/i386/kernel/paravirt.c |    2 --
 include/asm-i386/paravirt.h |   13 +------------
 include/asm-i386/pgtable.h  |    4 +---
 3 files changed, 2 insertions(+), 17 deletions(-)

===================================================================

arch/i386/kernel/paravirt.c +0 -2
@@ -315,8 +315,6 @@ struct paravirt_ops paravirt_ops = {
 	.pte_update = paravirt_nop,
 	.pte_update_defer = paravirt_nop,
 
-	.ptep_get_and_clear = native_ptep_get_and_clear,
-
 #ifdef CONFIG_HIGHPTE
 	.kmap_atomic_pte = kmap_atomic,
 #endif

===================================================================

include/asm-i386/paravirt.h +1 -12
@@ -187,8 +187,6 @@ struct paravirt_ops
 	void (*pte_update)(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
 	void (*pte_update_defer)(struct mm_struct *mm,
 				 unsigned long addr, pte_t *ptep);
-
- 	pte_t (*ptep_get_and_clear)(pte_t *ptep);
 
 #ifdef CONFIG_HIGHPTE
 	void *(*kmap_atomic_pte)(struct page *page, enum km_type type);
@@ -859,12 +857,8 @@ static inline void pmd_clear(pmd_t *pmdp
 	PVOP_VCALL1(pmd_clear, pmdp);
 }
 
-static inline pte_t raw_ptep_get_and_clear(pte_t *p)
-{
-	unsigned long long val = PVOP_CALL1(unsigned long long, ptep_get_and_clear, p);
-	return (pte_t) { val, val >> 32 };
-}
 #else  /* !CONFIG_X86_PAE */
+
 static inline pte_t __pte(unsigned long val)
 {
 	return (pte_t) { PVOP_CALL1(unsigned long, make_pte, val) };
@@ -899,11 +893,6 @@ static inline void set_pmd(pmd_t *pmdp, 
 static inline void set_pmd(pmd_t *pmdp, pmd_t pmdval)
 {
 	PVOP_VCALL2(set_pmd, pmdp, pmdval.pud.pgd.pgd);
-}
-
-static inline pte_t raw_ptep_get_and_clear(pte_t *p)
-{
-	return (pte_t) { PVOP_CALL1(unsigned long, ptep_get_and_clear, p) };
 }
 #endif	/* CONFIG_X86_PAE */
 

===================================================================

include/asm-i386/pgtable.h +1 -3
@@ -265,8 +265,6 @@ static inline pte_t pte_mkhuge(pte_t pte
  */
 #define pte_update(mm, addr, ptep)		do { } while (0)
 #define pte_update_defer(mm, addr, ptep)	do { } while (0)
-
-#define raw_ptep_get_and_clear(xp)     native_ptep_get_and_clear(xp)
 #endif
 
 /*
@@ -340,7 +338,7 @@ do {									\
 #define __HAVE_ARCH_PTEP_GET_AND_CLEAR
 static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
 {
-	pte_t pte = raw_ptep_get_and_clear(ptep);
+	pte_t pte = native_ptep_get_and_clear(ptep);
 	pte_update(mm, addr, ptep);
 	return pte;
 }
#66 lore raw

PATCH 25/28 From: Jeremy Fitzhardinge <[email protected]>

In reply to Jeremy Fitzhardinge

The other symbols used to delineate the alt-instructions sections have the form __foo/__foo_end. Rename parainstructions to match.

---
 arch/i386/kernel/alternative.c |    2 +-
 arch/i386/kernel/vmi.c         |    6 +++---
 arch/i386/kernel/vmlinux.lds.S |    4 ++--
 include/asm-i386/alternative.h |    4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

===================================================================

arch/i386/kernel/alternative.c +1 -1
@@ -386,6 +386,6 @@ void __init alternative_instructions(voi
 		alternatives_smp_switch(0);
 	}
 #endif
- 	apply_paravirt(__start_parainstructions, __stop_parainstructions);
+ 	apply_paravirt(__parainstructions, __parainstructions_end);
 	local_irq_restore(flags);
 }

===================================================================

arch/i386/kernel/vmi.c +3 -3
@@ -74,8 +74,8 @@ static struct {
 } vmi_ops;
 
 /* XXX move this to alternative.h */
-extern struct paravirt_patch __start_parainstructions[],
-	__stop_parainstructions[];
+extern struct paravirt_patch __parainstructions[],
+	__parainstructions_end[];
 
 /* Cached VMI operations */
 struct vmi_timer_ops vmi_timer_ops;
@@ -909,7 +909,7 @@ static inline int __init activate_vmi(vo
 	 * to do this before IRQs get reenabled.  Fortunately, it is
 	 * idempotent.
 	 */
-	apply_paravirt(__start_parainstructions, __stop_parainstructions);
+	apply_paravirt(__parainstructions, __parainstructions_end);
 
 	vmi_bringup();
 

===================================================================

arch/i386/kernel/vmlinux.lds.S +2 -2
@@ -166,9 +166,9 @@ SECTIONS
   }
   . = ALIGN(4);
   .parainstructions : AT(ADDR(.parainstructions) - LOAD_OFFSET) {
-  	__start_parainstructions = .;
+  	__parainstructions = .;
 	*(.parainstructions)
-  	__stop_parainstructions = .;
+  	__parainstructions_end = .;
   }
   /* .exit.text is discard at runtime, not link time, to deal with references
      from .altinstructions and .eh_frame */

===================================================================

include/asm-i386/alternative.h +2 -2
@@ -124,8 +124,8 @@ apply_paravirt(struct paravirt_patch_sit
 apply_paravirt(struct paravirt_patch_site *start,
 	       struct paravirt_patch_site *end)
 {}
-#define __start_parainstructions NULL
-#define __stop_parainstructions NULL
+#define __parainstructions	NULL
+#define __parainstructions_end	NULL
 #endif
 
 #endif /* _I386_ALTERNATIVE_H */
#67 lore raw

PATCH 26/28 From: Andrew Morton <[email protected]>

In reply to Jeremy Fitzhardinge

x86_64:

arch/x86_64/kernel/../../i386/kernel/alternative.c: In function 'alternative_instructions':
arch/x86_64/kernel/../../i386/kernel/alternative.c:374: error: '__parainstructions' undeclared (first use in this function) arch/x86_64/kernel/../../i386/kernel/alternative.c:374: error: (Each undeclared identifier is reported only once arch/x86_64/kernel/../../i386/kernel/alternative.c:374: error: for each function it appears in.) arch/x86_64/kernel/../../i386/kernel/alternative.c:374: error: '__parainstructions_end' undeclared (first use in this function)

Cc Andi Kleen <[email protected]>
Cc Jeremy Fitzhardinge <[email protected]>
Cc Rusty Russell <[email protected]>
---
 include/asm-x86_64/alternative.h |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

===================================================================

include/asm-x86_64/alternative.h renamed from include/asm-x86_64/alternative.h~rename-the-parainstructions-symbols-to-be-consistent-with-the-others-fix +2 -2
@@ -141,8 +141,8 @@ void apply_paravirt(struct paravirt_patc
 static inline void
 apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end)
 {}
-#define __start_parainstructions NULL
-#define __stop_parainstructions NULL
+#define __parainstructions NULL
+#define __parainstructions_end NULL
 #endif
 
 #endif /* _X86_64_ALTERNATIVE_H */

_

#68 lore raw

PATCH 27/28 paravirt: little compile fixes for vmi.c

In reply to Jeremy Fitzhardinge
---
 arch/i386/kernel/vmi.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

===================================================================

arch/i386/kernel/vmi.c +3 -7
@@ -73,10 +73,6 @@ static struct {
   	void (*set_lazy_mode)(int mode);
 } vmi_ops;
 
-/* XXX move this to alternative.h */
-extern struct paravirt_patch __parainstructions[],
-	__parainstructions_end[];
-
 /* Cached VMI operations */
 struct vmi_timer_ops vmi_timer_ops;
 
@@ -548,9 +544,9 @@ vmi_startup_ipi_hook(int phys_apicid, un
 }
 #endif
 
-static void vmi_set_lazy_mode(int mode)
-{
-	static DEFINE_PER_CPU(int, lazy_mode);
+static void vmi_set_lazy_mode(enum paravirt_lazy_mode mode)
+{
+	static DEFINE_PER_CPU(enum paravirt_lazy_mode, lazy_mode);
 
 	if (!vmi_ops.set_lazy_mode)
 		return;
#69 lore raw

PATCH 28/28 Add a sched_clock paravirt_op

In reply to Jeremy Fitzhardinge

The tsc-based get_scheduled_cycles interface is not a good match for Xen's runstate accounting, which reports everything in nanoseconds.

This patch replaces this interface with a sched_clock interface, which matches both Xen and VMI's requirements.

In order to do this, we:
1. replace get_scheduled_cycles with sched_clock
2. hoist cycles_2_ns into a common header
3. update vmi accordingly

One thing to note: because sched_clock is implemented as a weak function in kernel/sched.c, we must define a real function in order to override this weak binding. This means the usual paravirt_ops technique of using an inline function won't work in this case.

Cc Zachary Amsden <[email protected]>
Cc Dan Hecht <[email protected]>
Cc john stultz <[email protected]>
---
 arch/i386/kernel/paravirt.c    |    2 -
 arch/i386/kernel/sched-clock.c |   43 ++++++++++++++-----------------------
 arch/i386/kernel/vmi.c         |    2 -
 arch/i386/kernel/vmiclock.c    |    6 ++---
 include/asm-i386/paravirt.h    |    7 ++++--
 include/asm-i386/timer.h       |   46 +++++++++++++++++++++++++++++++++++++++-
 include/asm-i386/vmi_time.h    |    2 -
 7 files changed, 73 insertions(+), 35 deletions(-)

===================================================================

arch/i386/kernel/paravirt.c +1 -1
@@ -268,7 +268,7 @@ struct paravirt_ops paravirt_ops = {
 	.write_msr = native_write_msr_safe,
 	.read_tsc = native_read_tsc,
 	.read_pmc = native_read_pmc,
-	.get_scheduled_cycles = native_read_tsc,
+	.sched_clock = native_sched_clock,
 	.get_cpu_khz = native_calculate_cpu_khz,
 	.load_tr_desc = native_load_tr_desc,
 	.set_ldt = native_set_ldt,

===================================================================

arch/i386/kernel/sched-clock.c +17 -26

73 lines not on this page · raw message

===================================================================

arch/i386/kernel/vmi.c +1 -1
@@ -887,7 +887,7 @@ static inline int __init activate_vmi(vo
 		paravirt_ops.setup_boot_clock = vmi_time_bsp_init;
 		paravirt_ops.setup_secondary_clock = vmi_time_ap_init;
 #endif
-		paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles;
+		paravirt_ops.sched_clock = vmi_sched_clock;
  		paravirt_ops.get_cpu_khz = vmi_cpu_khz;
 
 		/* We have true wallclock functions; disable CMOS clock sync */

===================================================================

arch/i386/kernel/vmiclock.c +3 -3
@@ -65,9 +65,9 @@ int vmi_set_wallclock(unsigned long now)
 }
 
 /* paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles */
-unsigned long long vmi_get_sched_cycles(void)
-{
-	return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE);
+unsigned long long vmi_sched_clock(void)
+{
+	return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE));
 }
 
 /* paravirt_ops.get_cpu_khz = vmi_cpu_khz */

===================================================================

include/asm-i386/paravirt.h +5 -2
@@ -116,7 +116,7 @@ struct paravirt_ops
 
 	u64 (*read_tsc)(void);
 	u64 (*read_pmc)(void);
- 	u64 (*get_scheduled_cycles)(void);
+ 	unsigned long long (*sched_clock)(void);
 	unsigned long (*get_cpu_khz)(void);
 
 	/* Segment descriptor handling */
@@ -573,7 +573,10 @@ static inline u64 paravirt_read_tsc(void
 
 #define rdtscll(val) (val = paravirt_read_tsc())
 
-#define get_scheduled_cycles(val) (val = paravirt_ops.get_scheduled_cycles())
+static inline unsigned long long paravirt_sched_clock(void)
+{
+	return PVOP_CALL0(unsigned long long, sched_clock);
+}
 #define calculate_cpu_khz() (paravirt_ops.get_cpu_khz())
 
 #define write_tsc(val1,val2) wrmsr(0x10, val1, val2)

===================================================================

include/asm-i386/timer.h +45 -1

53 lines not on this page · raw message

===================================================================

include/asm-i386/vmi_time.h +1 -1
@@ -49,7 +49,7 @@ extern void __init vmi_time_init(void);
 extern void __init vmi_time_init(void);
 extern unsigned long vmi_get_wallclock(void);
 extern int vmi_set_wallclock(unsigned long now);
-extern unsigned long long vmi_get_sched_cycles(void);
+extern unsigned long long vmi_sched_clock(void);
 extern unsigned long vmi_cpu_khz(void);
 
 #ifdef CONFIG_X86_LOCAL_APIC