Skip to contentSkip to conversation

x86 boot, pda and gdt cleanups

PATCH In review Rusty Russell 5 participants 36 messages Mar 6, 2007 – Mar 15, 2007

Subsystems (12)
Arm (32-bit) PowerPC MIPS s390 +8 more subsystems
lore
Share & tools
lore
#0 lore raw

cover x86 boot, pda and gdt cleanups

Hi all,

The GDT stuff on x86 is a little more complex than it need be, but
playing with boot code is always dangerous. These compile and boot on UP and SMP for me, but Andrew should let the cook in -mm for a while.

Thanks!
Rusty.

#1 lore raw

PATCH 1/8 Remove cpu_gdt_table: use boot_gdt_table until migration to per-cpu

Linux uses three GDTs to boot: the boot_gdt_table, which contains only the __BOOT_CS and __BOOT_DS entries is used first up, before kernel is mapped at PAGE_OFFSET. Then we transition to cpu_gdt_table during boot, and finally we allocate a per-cpu GDT and switch to that.

We can simplify this by using the boot_gdt_table until switching to the per-cpu GDT table. As a bonus, this gets rid of the horribly-named "cpu_gdt_table" (it's not per-cpu, and the T in GDT already stands for table).

Finally, some old bogus comments are deleted.

arch/i386/kernel/cpu/common.c +1 -1
diff -r 9cf03cbf8bde arch/i386/kernel/cpu/common.c
@@ -694,7 +694,7 @@ __cpuinit int init_gdt(int cpu, struct t
 	 * Initialize the per-CPU GDT with the boot GDT,
 	 * and set up the GDT descriptor:
 	 */
- 	memcpy(gdt, cpu_gdt_table, GDT_SIZE);
+ 	memcpy(gdt, boot_gdt_table, GDT_SIZE);
 	cpu_gdt_descr->size = GDT_SIZE - 1;
 
 	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PDA].a,
arch/i386/kernel/head.S +6 -18
diff -r 9cf03cbf8bde arch/i386/kernel/head.S
@@ -595,31 +595,19 @@ idt_descr:
 	.word IDT_ENTRIES*8-1		# idt contains 256 entries
 	.long idt_table
 
-# boot GDT descriptor (later on used by CPU#0):
+# The boot GDT descriptor once paging is enabled.
 	.word 0				# 32 bit align gdt_desc.address
 ENTRY(early_gdt_descr)
 	.word GDT_ENTRIES*8-1
-	.long cpu_gdt_table
-
-/*
- * The boot_gdt_table must mirror the equivalent in setup.S and is
- * used only for booting.
- */
+	.long boot_gdt_table
+
+/* The boot Global Descriptor Table: after boot we allocate a per-cpu copy */
 	.align L1_CACHE_BYTES
 ENTRY(boot_gdt_table)
-	.fill GDT_ENTRY_BOOT_CS,8,0
-	.quad 0x00cf9a000000ffff	/* kernel 4GB code at 0x00000000 */
-	.quad 0x00cf92000000ffff	/* kernel 4GB data at 0x00000000 */
-
-/*
- * The Global Descriptor Table contains 28 quadwords, per-CPU.
- */
-	.align L1_CACHE_BYTES
-ENTRY(cpu_gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
 	.quad 0x0000000000000000	/* 0x0b reserved */
-	.quad 0x0000000000000000	/* 0x13 reserved */
-	.quad 0x0000000000000000	/* 0x1b reserved */
+	.quad 0x00cf9a000000ffff	/* boot: 4GB code at 0x00000000 */
+	.quad 0x00cf92000000ffff	/* boot: 4GB data at 0x00000000 */
 	.quad 0x0000000000000000	/* 0x20 unused */
 	.quad 0x0000000000000000	/* 0x28 unused */
 	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */
include/asm-i386/desc.h +1 -1
diff -r 9cf03cbf8bde include/asm-i386/desc.h
@@ -12,7 +12,7 @@
 
 #include <asm/mmu.h>
 
-extern struct desc_struct cpu_gdt_table[GDT_ENTRIES];
+extern struct desc_struct boot_gdt_table[GDT_ENTRIES];
 
 struct Xgt_desc_struct {
 	unsigned short size;
#2 lore raw

PATCH 2/8 Remove NR_CPUS from asm-generic/percpu.h

I managed to get an error about NR_CPUS undefined after a make allyesconfig. Admittedly this was a patched kernel, but it's easy to remove it and avoid the error in future even if it's OK at the moment.

include/asm-generic/percpu.h +1 -1
diff -r 4519e3475c4f include/asm-generic/percpu.h
@@ -5,7 +5,7 @@
 #define __GENERIC_PER_CPU
 #ifdef CONFIG_SMP
 
-extern unsigned long __per_cpu_offset[NR_CPUS];
+extern unsigned long __per_cpu_offset[];
 
 #define per_cpu_offset(x) (__per_cpu_offset[x])
 
#3 lore raw

PATCH 3/8 Use per-cpu variables for GDT, PDA

Allocating PDA and GDT at boot is a pain. Using simple per-cpu variables adds happiness (although we need the GDT page-aligned for Xen, see later).

Finally, we can simply call it "cpu_gdt" rather than enduring the superfluous and unnecessarily redundant tautology of "cpu_gdt_table".

arch/i386/kernel/cpu/common.c +17 -84
diff -r c2b61e13394d arch/i386/kernel/cpu/common.c
@@ -25,8 +25,10 @@ DEFINE_PER_CPU(struct Xgt_desc_struct, c
 DEFINE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
 EXPORT_PER_CPU_SYMBOL(cpu_gdt_descr);
 
-struct i386_pda *_cpu_pda[NR_CPUS] __read_mostly;
-EXPORT_SYMBOL(_cpu_pda);
+DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
+
+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;
@@ -609,52 +611,6 @@ struct pt_regs * __devinit idle_regs(str
 	return regs;
 }
 
-static __cpuinit int alloc_gdt(int cpu)
-{
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
-	struct desc_struct *gdt;
-	struct i386_pda *pda;
-
-	gdt = (struct desc_struct *)cpu_gdt_descr->address;
-	pda = cpu_pda(cpu);
-
-	/*
-	 * This is a horrible hack to allocate the GDT.  The problem
-	 * is that cpu_init() is called really early for the boot CPU
-	 * (and hence needs bootmem) but much later for the secondary
-	 * CPUs, when bootmem will have gone away
-	 */
-	if (NODE_DATA(0)->bdata->node_bootmem_map) {
-		BUG_ON(gdt != NULL || pda != NULL);
-
-		gdt = alloc_bootmem_pages(PAGE_SIZE);
-		pda = alloc_bootmem(sizeof(*pda));
-		/* alloc_bootmem(_pages) panics on failure, so no check */
-
-		memset(gdt, 0, PAGE_SIZE);
-		memset(pda, 0, sizeof(*pda));
-	} else {
-		/* GDT and PDA might already have been allocated if
-		   this is a CPU hotplug re-insertion. */
-		if (gdt == NULL)
-			gdt = (struct desc_struct *)get_zeroed_page(GFP_KERNEL);
-
-		if (pda == NULL)
-			pda = kmalloc_node(sizeof(*pda), GFP_KERNEL, cpu_to_node(cpu));
-
-		if (unlikely(!gdt || !pda)) {
-			free_pages((unsigned long)gdt, 0);
-			kfree(pda);
-			return 0;
-		}
-	}
-
- 	cpu_gdt_descr->address = (unsigned long)gdt;
-	cpu_pda(cpu) = pda;
-
-	return 1;
-}
-
 /* Initial PDA used by boot CPU */
 struct i386_pda boot_pda = {
 	._pda = &boot_pda,
@@ -670,31 +626,17 @@ static inline void set_kernel_fs(void)
 	asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_PDA) : "memory");
 }
 
-/* Initialize the CPU's GDT and PDA.  The boot CPU does this for
-   itself, but secondaries find this done for them. */
-__cpuinit int init_gdt(int cpu, struct task_struct *idle)
+/* Initialize the CPU's GDT and PDA.  This is either the boot CPU doing itself
+   (still using boot_gdt_table), or a CPU doing it for a secondary which
+   will soon come up. */
+__cpuinit void init_gdt(int cpu, struct task_struct *idle)
 {
 	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
-	struct desc_struct *gdt;
-	struct i386_pda *pda;
-
-	/* For non-boot CPUs, the GDT and PDA should already have been
-	   allocated. */
-	if (!alloc_gdt(cpu)) {
-		printk(KERN_CRIT "CPU%d failed to allocate GDT or PDA\n", cpu);
-		return 0;
-	}
-
-	gdt = (struct desc_struct *)cpu_gdt_descr->address;
-	pda = cpu_pda(cpu);
-
-	BUG_ON(gdt == NULL || pda == NULL);
-
-	/*
-	 * Initialize the per-CPU GDT with the boot GDT,
-	 * and set up the GDT descriptor:
-	 */
+	struct desc_struct *gdt = per_cpu(cpu_gdt, cpu);
+	struct i386_pda *pda = &per_cpu(_cpu_pda, cpu);
+
  	memcpy(gdt, boot_gdt_table, GDT_SIZE);
+ 	cpu_gdt_descr->address = (unsigned long)gdt;
 	cpu_gdt_descr->size = GDT_SIZE - 1;
 
 	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PDA].a,
@@ -706,17 +648,13 @@ __cpuinit int init_gdt(int cpu, struct t
 	pda->_pda = pda;
 	pda->cpu_number = cpu;
 	pda->pcurrent = idle;
-
-	return 1;
-}
-
+}
+
+/* Move this CPU from boot_gdt_table & boot_pda to this cpu's proper one. */
 void __cpuinit cpu_set_gdt(int cpu)
 {
 	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
 
-	/* Reinit these anyway, even if they've already been done (on
-	   the boot CPU, this will transition from the boot gdt+pda to
-	   the real ones). */
 	load_gdt(cpu_gdt_descr);
 	set_kernel_fs();
 }
@@ -804,13 +742,8 @@ void __cpuinit cpu_init(void)
 	struct task_struct *curr = current;
 
 	/* Set up the real GDT and PDA, so we can transition from the
-	   boot versions. */
-	if (!init_gdt(cpu, curr)) {
-		/* failed to allocate something; not much we can do... */
-		for (;;)
-			local_irq_enable();
-	}
-
+	   boot_gdt_table & boot_pda. */
+	init_gdt(cpu, curr);
 	cpu_set_gdt(cpu);
 	_cpu_init(cpu, curr);
 }
arch/i386/kernel/smpboot.c +1 -20
diff -r c2b61e13394d arch/i386/kernel/smpboot.c
@@ -813,13 +813,7 @@ static int __cpuinit do_boot_cpu(int api
 	if (IS_ERR(idle))
 		panic("failed fork for CPU %d", cpu);
 
-	/* Pre-allocate and initialize the CPU's GDT and PDA so it
-	   doesn't have to do any memory allocation during the
-	   delicate CPU-bringup phase. */
-	if (!init_gdt(cpu, idle)) {
-		printk(KERN_INFO "Couldn't allocate GDT/PDA for CPU %d\n", cpu);
-		return -1;	/* ? */
-	}
+	init_gdt(cpu, idle);
 
 	idle->thread.eip = (unsigned long) start_secondary;
 	/* start_eip had better be page-aligned! */
@@ -945,24 +939,11 @@ static int __cpuinit __smp_prepare_cpu(i
 	DECLARE_COMPLETION_ONSTACK(done);
 	struct warm_boot_cpu_info info;
 	int	apicid, ret;
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
 
 	apicid = x86_cpu_to_apicid[cpu];
 	if (apicid == BAD_APICID) {
 		ret = -ENODEV;
 		goto exit;
-	}
-
-	/*
-	 * the CPU isn't initialized at boot time, allocate gdt table here.
-	 * cpu_init will initialize it
-	 */
-	if (!cpu_gdt_descr->address) {
-		cpu_gdt_descr->address = get_zeroed_page(GFP_KERNEL);
-		if (!cpu_gdt_descr->address)
-			printk(KERN_CRIT "CPU%d failed to allocate GDT\n", cpu);
-			ret = -ENOMEM;
-			goto exit;
 	}
 
 	info.complete = &done;
arch/i386/mach-voyager/voyager_smp.c +1 -9
diff -r c2b61e13394d arch/i386/mach-voyager/voyager_smp.c
@@ -580,15 +580,7 @@ do_boot_cpu(__u8 cpu)
 	/* init_tasks (in sched.c) is indexed logically */
 	stack_start.esp = (void *) idle->thread.esp;
 
-	/* Pre-allocate and initialize the CPU's GDT and PDA so it
-	   doesn't have to do any memory allocation during the
-	   delicate CPU-bringup phase. */
-	if (!init_gdt(cpu, idle)) {
-		printk(KERN_INFO "Couldn't allocate GDT/PDA for CPU %d\n", cpu);
-		cpucount--;
-		return;
-	}
-
+	init_gdt(cpu, idle);
 	irq_ctx_init(cpu);
 
 	/* Note: Don't modify initial ss override */
include/asm-i386/desc.h +1 -0
diff -r c2b61e13394d include/asm-i386/desc.h
@@ -22,6 +22,7 @@ struct Xgt_desc_struct {
 
 extern struct Xgt_desc_struct idt_descr;
 DECLARE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
+DECLARE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
 extern struct Xgt_desc_struct early_gdt_descr;
 
 static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
include/asm-i386/pda.h +3 -2
diff -r c2b61e13394d include/asm-i386/pda.h
@@ -8,6 +8,7 @@
 
 #include <linux/stddef.h>
 #include <linux/types.h>
+#include <asm/percpu.h>
 
 struct i386_pda
 {
@@ -18,9 +19,9 @@ struct i386_pda
 	struct pt_regs *irq_regs;
 };
 
-extern struct i386_pda *_cpu_pda[];
+DECLARE_PER_CPU(struct i386_pda, _cpu_pda);
 
-#define cpu_pda(i)	(_cpu_pda[i])
+#define cpu_pda(i)	(&per_cpu(_cpu_pda, (i)))
 
 #define pda_offset(field) offsetof(struct i386_pda, field)
 
include/asm-i386/processor.h +1 -1
diff -r c2b61e13394d include/asm-i386/processor.h
@@ -750,7 +750,7 @@ extern void enable_sep_cpu(void);
 extern void enable_sep_cpu(void);
 extern int sysenter_setup(void);
 
-extern int init_gdt(int cpu, struct task_struct *idle);
+extern void init_gdt(int cpu, struct task_struct *idle);
 extern void cpu_set_gdt(int);
 extern void secondary_cpu_init(void);
 
#4 lore raw

PATCH 4/8 Cleanup setup_pda

The comment above this function has never been true in mainline:
talking to Jeremy it's from previous patch churn. What setup_pda actually does is to write the segment entry for boot_pda into the boot_gdt_table. Ideally, this would be a static initialization, but because the addresses are split over multiple parts in the funk bit layout of the GDT entry (thanks Intel!) that's not a valid C initializer.

We in fact call this routine on every CPU bringup, but that's harmless. Non-boot CPUs switch to their own GDTs immediately in initialize_secondary.

VMI certainly doesn't need to call it explicitly.

arch/i386/kernel/head.S +4 -14
diff -r 2d9aa53572e2 arch/i386/kernel/head.S
@@ -365,17 +365,10 @@ 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
+/* Initialize the boot_gdt_table's GDT_ENTRY_PDA (can't be done statically) */
+setup_pda:
+	movl $boot_pda, %eax
+	movl $boot_gdt_table, %ecx
 	mov %ax, (__KERNEL_PDA+0+2)(%ecx)		/* base & 0x0000ffff */
 	shr $16, %eax
 	mov %al, (__KERNEL_PDA+4+0)(%ecx)		/* base & 0x00ff0000 */
@@ -554,9 +547,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/vmi.c +0 -4
diff -r 2d9aa53572e2 arch/i386/kernel/vmi.c
@@ -525,8 +525,6 @@ 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)
@@ -555,8 +553,6 @@ vmi_startup_ipi_hook(int phys_apicid, un
 	ap.gs = 0;
 
 	ap.eflags = 0;
-
-	setup_pda();
 
 #ifdef CONFIG_X86_PAE
 	/* efer should match BSP efer. */
#5 lore raw

PATCH 5/8 Cleanup GDT access

Now we have an explicit per-cpu GDT variable, we don't need to keep the descriptors around to use them to find the GDT. Also remove the cpu_pda() accessor: it's just a per-cpu variable.

arch/i386/kernel/cpu/common.c +8 -11
diff -r e9d5b8e6f5ee arch/i386/kernel/cpu/common.c
@@ -22,11 +22,8 @@
 
 #include "cpu.h"
 
-DEFINE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
-EXPORT_PER_CPU_SYMBOL(cpu_gdt_descr);
-
 DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
-
+EXPORT_PER_CPU_SYMBOL_GPL(cpu_gdt);
 DEFINE_PER_CPU(struct i386_pda, _cpu_pda);
 EXPORT_PER_CPU_SYMBOL(_cpu_pda);
 
@@ -631,14 +628,11 @@ static inline void set_kernel_fs(void)
    will soon come up. */
 __cpuinit void init_gdt(int cpu, struct task_struct *idle)
 {
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
 	struct desc_struct *gdt = per_cpu(cpu_gdt, cpu);
 	struct i386_pda *pda = &per_cpu(_cpu_pda, cpu);
 
+	/* Based on boot_gdt_table: set PDA so it can be used immediately */
  	memcpy(gdt, boot_gdt_table, GDT_SIZE);
- 	cpu_gdt_descr->address = (unsigned long)gdt;
-	cpu_gdt_descr->size = GDT_SIZE - 1;
-
 	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PDA].a,
 			(u32 *)&gdt[GDT_ENTRY_PDA].b,
 			(unsigned long)pda, sizeof(*pda) - 1,
@@ -653,9 +647,12 @@ __cpuinit void init_gdt(int cpu, struct 
 /* Move this CPU from boot_gdt_table & boot_pda to this cpu's proper one. */
 void __cpuinit cpu_set_gdt(int cpu)
 {
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
-
-	load_gdt(cpu_gdt_descr);
+	struct Xgt_desc_struct gdt_descr;
+
+ 	gdt_descr.address = (unsigned long)per_cpu(cpu_gdt, cpu);
+	gdt_descr.size = GDT_SIZE - 1;
+
+	load_gdt(&gdt_descr);
 	set_kernel_fs();
 }
 
arch/i386/kernel/efi.c +9 -9
diff -r e9d5b8e6f5ee arch/i386/kernel/efi.c
@@ -69,12 +69,10 @@ static void efi_call_phys_prelog(void) _
 {
 	unsigned long cr4;
 	unsigned long temp;
-	struct Xgt_desc_struct *cpu_gdt_descr;
+	struct Xgt_desc_struct gdt_descr;
 
 	spin_lock(&efi_rt_lock);
 	local_irq_save(efi_rt_eflags);
-
-	cpu_gdt_descr = &per_cpu(cpu_gdt_descr, 0);
 
 	/*
 	 * If I don't have PSE, I should just duplicate two entries in page
@@ -105,17 +103,19 @@ static void efi_call_phys_prelog(void) _
 	 */
 	local_flush_tlb();
 
-	cpu_gdt_descr->address = __pa(cpu_gdt_descr->address);
-	load_gdt(cpu_gdt_descr);
+	gdt_descr.address = __pa(get_cpu_gdt_table(0));
+	gdt_descr.size = GDT_SIZE - 1;
+	load_gdt(&gdt_descr);
 }
 
 static void efi_call_phys_epilog(void) __releases(efi_rt_lock)
 {
 	unsigned long cr4;
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, 0);
-
-	cpu_gdt_descr->address = (unsigned long)__va(cpu_gdt_descr->address);
-	load_gdt(cpu_gdt_descr);
+	struct Xgt_desc_struct gdt_descr;
+
+	gdt_descr.address = (unsigned long)get_cpu_gdt_table(0);
+	gdt_descr.size = GDT_SIZE - 1;
+	load_gdt(&gdt_descr);
 
 	cr4 = read_cr4();
 
arch/i386/kernel/entry.S +1 -2
diff -r e9d5b8e6f5ee arch/i386/kernel/entry.S
@@ -561,8 +561,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_descr, %ebx); \
-	movl GDS_address(%ebx), %ebx; \
+	PER_CPU(cpu_gdt, %ebx); \
 	GET_DESC_BASE(GDT_ENTRY_ESPFIX_SS, %ebx, %eax, %ax, %al, %ah); \
 	addl %esp, %eax; \
 	pushl $__KERNEL_DS; \
arch/i386/kernel/traps.c +1 -3
diff -r e9d5b8e6f5ee arch/i386/kernel/traps.c
@@ -1018,9 +1018,7 @@ fastcall unsigned long patch_espfix_desc
 fastcall unsigned long patch_espfix_desc(unsigned long uesp,
 					  unsigned long kesp)
 {
-	int cpu = smp_processor_id();
-	struct Xgt_desc_struct *cpu_gdt_descr = &per_cpu(cpu_gdt_descr, cpu);
-	struct desc_struct *gdt = (struct desc_struct *)cpu_gdt_descr->address;
+	struct desc_struct *gdt = __get_cpu_var(cpu_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 +3 -7
diff -r e9d5b8e6f5ee include/asm-i386/desc.h
@@ -13,6 +13,7 @@
 #include <asm/mmu.h>
 
 extern struct desc_struct boot_gdt_table[GDT_ENTRIES];
+DECLARE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
 
 struct Xgt_desc_struct {
 	unsigned short size;
@@ -21,13 +22,9 @@ struct Xgt_desc_struct {
 } __attribute__ ((packed));
 
 extern struct Xgt_desc_struct idt_descr;
-DECLARE_PER_CPU(struct Xgt_desc_struct, cpu_gdt_descr);
-DECLARE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
-extern struct Xgt_desc_struct early_gdt_descr;
-
 static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
 {
-	return (struct desc_struct *)per_cpu(cpu_gdt_descr, cpu).address;
+	return per_cpu(cpu_gdt, cpu);
 }
 
 extern struct desc_struct idt_table[];
@@ -102,13 +99,12 @@ static inline fastcall void native_set_l
 	if (likely(entries == 0))
 		__asm__ __volatile__("lldt %w0"::"q" (0));
 	else {
-		unsigned cpu = smp_processor_id();
 		__u32 a, b;
 
 		pack_descriptor(&a, &b, (unsigned long)addr,
 				entries * sizeof(struct desc_struct) - 1,
 				DESCTYPE_LDT, 0);
-		write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_LDT, a, b);
+		write_gdt_entry(__get_cpu_var(cpu_gdt), GDT_ENTRY_LDT, a, b);
 		__asm__ __volatile__("lldt %w0"::"q" (GDT_ENTRY_LDT*8));
 	}
 }
include/asm-i386/pda.h +0 -2
diff -r e9d5b8e6f5ee include/asm-i386/pda.h
@@ -20,8 +20,6 @@ struct i386_pda
 };
 
 DECLARE_PER_CPU(struct i386_pda, _cpu_pda);
-
-#define cpu_pda(i)	(&per_cpu(_cpu_pda, (i)))
 
 #define pda_offset(field) offsetof(struct i386_pda, field)
 
#6 lore raw

PATCH 6/8 Allow per-cpu variables to be page-aligned

Xen wants page-aligned GDT (and PDA must not cross a page-boundary, but that doesn't happen at the moment since it's so close to start of page). Let's allow page-alignment in general for per-cpu data.

Because larger alignments can use more room, we increase the max per-cpu memory to 64k rather than 32k: it's getting a little tight.

arch/alpha/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/alpha/kernel/vmlinux.lds.S
@@ -69,7 +69,7 @@ SECTIONS
   . = ALIGN(8);
   SECURITY_INIT
 
-  . = ALIGN(64);
+  . = ALIGN(8192);
   __per_cpu_start = .;
   .data.percpu : { *(.data.percpu) }
   __per_cpu_end = .;
arch/arm/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/arm/kernel/vmlinux.lds.S
@@ -59,7 +59,7 @@ SECTIONS
 			usr/built-in.o(.init.ramfs)
 		__initramfs_end = .;
 #endif
-		. = ALIGN(64);
+		. = ALIGN(4096);
 		__per_cpu_start = .;
 			*(.data.percpu)
 		__per_cpu_end = .;
arch/cris/arch-v32/vmlinux.lds.S +1 -0
diff -r 213b1ec27001 arch/cris/arch-v32/vmlinux.lds.S
@@ -91,6 +91,7 @@ SECTIONS
 	}
 	SECURITY_INIT
 
+	. =  ALIGN (8192);
 	__per_cpu_start = .;
 	.data.percpu  : { *(.data.percpu) }
 	__per_cpu_end = .;
arch/frv/kernel/vmlinux.lds.S +1 -0
diff -r 213b1ec27001 arch/frv/kernel/vmlinux.lds.S
@@ -57,6 +57,7 @@ SECTIONS
   __alt_instructions_end = .;
  .altinstr_replacement : { *(.altinstr_replacement) }
 
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/i386/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/i386/kernel/vmlinux.lds.S
@@ -196,7 +196,7 @@ SECTIONS
 	__initramfs_end = .;
   }
 #endif
-  . = ALIGN(L1_CACHE_BYTES);
+  . = ALIGN(4096);
   .data.percpu  : AT(ADDR(.data.percpu) - LOAD_OFFSET) {
 	__per_cpu_start = .;
 	*(.data.percpu)
arch/m32r/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/m32r/kernel/vmlinux.lds.S
@@ -110,7 +110,7 @@ SECTIONS
   __initramfs_end = .;
 #endif
 
-  . = ALIGN(32);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/mips/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/mips/kernel/vmlinux.lds.S
@@ -119,7 +119,7 @@ SECTIONS
   .init.ramfs : { *(.init.ramfs) }
   __initramfs_end = .;
 #endif
-  . = ALIGN(32);
+  . = ALIGN(_PAGE_SIZE);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/parisc/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/parisc/kernel/vmlinux.lds.S
@@ -181,7 +181,7 @@ SECTIONS
   .init.ramfs : { *(.init.ramfs) }
   __initramfs_end = .;
 #endif
-  . = ALIGN(32);
+  . = ALIGN(ASM_PAGE_SIZE);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/powerpc/kernel/vmlinux.lds.S +1 -5
diff -r 213b1ec27001 arch/powerpc/kernel/vmlinux.lds.S
@@ -139,11 +139,7 @@ SECTIONS
 		__initramfs_end = .;
 	}
 #endif
-#ifdef CONFIG_PPC32
-	. = ALIGN(32);
-#else
-	. = ALIGN(128);
-#endif
+	. = ALIGN(PAGE_SIZE);
 	.data.percpu : {
 		__per_cpu_start = .;
 		*(.data.percpu)
arch/ppc/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/ppc/kernel/vmlinux.lds.S
@@ -130,7 +130,7 @@ SECTIONS
   __ftr_fixup : { *(__ftr_fixup) }
   __stop___ftr_fixup = .;
 
-  . = ALIGN(32);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/s390/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/s390/kernel/vmlinux.lds.S
@@ -99,7 +99,7 @@ SECTIONS
   . = ALIGN(2);
   __initramfs_end = .;
 #endif
-  . = ALIGN(256);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/sh/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/sh/kernel/vmlinux.lds.S
@@ -54,7 +54,7 @@ SECTIONS
   . = ALIGN(PAGE_SIZE);
   .data.page_aligned : { *(.data.page_aligned) }
 
-  . = ALIGN(L1_CACHE_BYTES);
+  . = ALIGN(PAGE_SIZE);
   __per_cpu_start = .;
   .data.percpu : { *(.data.percpu) }
   __per_cpu_end = .;
arch/sh64/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/sh64/kernel/vmlinux.lds.S
@@ -85,7 +85,7 @@ SECTIONS
   . = ALIGN(PAGE_SIZE);
   .data.page_aligned : C_PHYS(.data.page_aligned) { *(.data.page_aligned) }
 
-  . = ALIGN(L1_CACHE_BYTES);
+  . = ALIGN(PAGE_SIZE);
   __per_cpu_start = .;
   .data.percpu : C_PHYS(.data.percpu) { *(.data.percpu) }
   __per_cpu_end = . ;
arch/sparc/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/sparc/kernel/vmlinux.lds.S
@@ -65,7 +65,7 @@ SECTIONS
   __initramfs_end = .;
 #endif
 
-  . = ALIGN(32);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
arch/x86_64/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/x86_64/kernel/vmlinux.lds.S
@@ -194,7 +194,7 @@ SECTIONS
   __initramfs_end = .;
 #endif
 
-    . = ALIGN(CONFIG_X86_L1_CACHE_BYTES);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : AT(ADDR(.data.percpu) - LOAD_OFFSET) { *(.data.percpu) }
   __per_cpu_end = .;
arch/xtensa/kernel/vmlinux.lds.S +1 -1
diff -r 213b1ec27001 arch/xtensa/kernel/vmlinux.lds.S
@@ -198,7 +198,7 @@ SECTIONS
   __ftr_fixup : { *(__ftr_fixup) }
   __stop___ftr_fixup = .;
 
-  . = ALIGN(32);
+  . = ALIGN(4096);
   __per_cpu_start = .;
   .data.percpu  : { *(.data.percpu) }
   __per_cpu_end = .;
include/linux/percpu.h +1 -1
diff -r 213b1ec27001 include/linux/percpu.h
@@ -11,7 +11,7 @@
 
 /* Enough to cover all DEFINE_PER_CPUs in kernel, including modules. */
 #ifndef PERCPU_ENOUGH_ROOM
-#define PERCPU_ENOUGH_ROOM 32768
+#define PERCPU_ENOUGH_ROOM 65536
 #endif
 
 /*
init/main.c +2 -2
diff -r 213b1ec27001 init/main.c
@@ -368,12 +368,12 @@ static void __init setup_per_cpu_areas(v
 	unsigned long nr_possible_cpus = num_possible_cpus();
 
 	/* Copy section for each CPU (we discard the original) */
-	size = ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES);
+	size = ALIGN(__per_cpu_end - __per_cpu_start, PAGE_SIZE);
 #ifdef CONFIG_MODULES
 	if (size < PERCPU_ENOUGH_ROOM)
 		size = PERCPU_ENOUGH_ROOM;
 #endif
-	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;
kernel/module.c +5 -5
diff -r 213b1ec27001 kernel/module.c
@@ -544,10 +544,10 @@ static void *percpu_modalloc(unsigned lo
 	unsigned int i;
 	void *ptr;
 
-	if (align > SMP_CACHE_BYTES) {
-		printk(KERN_WARNING "%s: per-cpu alignment %li > %i\n",
-		       name, align, SMP_CACHE_BYTES);
-		align = SMP_CACHE_BYTES;
+	if (align > PAGE_SIZE) {
+		printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n",
+		       name, align, PAGE_SIZE);
+		align = PAGE_SIZE;
 	}
 
 	ptr = __per_cpu_start;
@@ -628,7 +628,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] = -ALIGN(__per_cpu_end-__per_cpu_start, PAGE_SIZE);
 	/* Free room. */
 	pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
 	if (pcpu_size[1] < 0) {
#7 lore raw

PATCH 7/8 Page-align the GDT

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 +4 -4
diff -r 576929b5b43f arch/i386/kernel/cpu/common.c
@@ -22,8 +22,8 @@
 
 #include "cpu.h"
 
-DEFINE_PER_CPU(struct desc_struct, cpu_gdt[GDT_ENTRIES]);
-EXPORT_PER_CPU_SYMBOL_GPL(cpu_gdt);
+DEFINE_PER_CPU(struct gdt_page, gdt_page);
+EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
 DEFINE_PER_CPU(struct i386_pda, _cpu_pda);
 EXPORT_PER_CPU_SYMBOL(_cpu_pda);
 
@@ -628,7 +628,7 @@ static inline void set_kernel_fs(void)
    will soon come up. */
 __cpuinit void init_gdt(int cpu, struct task_struct *idle)
 {
-	struct desc_struct *gdt = per_cpu(cpu_gdt, cpu);
+	struct desc_struct *gdt = per_cpu(gdt_page, cpu).gdt;
 	struct i386_pda *pda = &per_cpu(_cpu_pda, cpu);
 
 	/* Based on boot_gdt_table: set PDA so it can be used immediately */
@@ -649,7 +649,7 @@ void __cpuinit cpu_set_gdt(int cpu)
 {
 	struct Xgt_desc_struct gdt_descr;
 
- 	gdt_descr.address = (unsigned long)per_cpu(cpu_gdt, cpu);
+ 	gdt_descr.address = (unsigned long)per_cpu(gdt_page, cpu).gdt;
 	gdt_descr.size = GDT_SIZE - 1;
 
 	load_gdt(&gdt_descr);
arch/i386/kernel/entry.S +1 -1
diff -r 576929b5b43f arch/i386/kernel/entry.S
@@ -561,7 +561,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
diff -r 576929b5b43f arch/i386/kernel/head.S
@@ -592,7 +592,7 @@ ENTRY(early_gdt_descr)
 	.long boot_gdt_table
 
 /* The boot Global Descriptor Table: after boot we allocate a per-cpu copy */
-	.align L1_CACHE_BYTES
+	.p2align PAGE_SHIFT
 ENTRY(boot_gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
 	.quad 0x0000000000000000	/* 0x0b reserved */
arch/i386/kernel/traps.c +1 -1
diff -r 576929b5b43f arch/i386/kernel/traps.c
@@ -1018,7 +1018,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 +8 -3
diff -r 576929b5b43f include/asm-i386/desc.h
@@ -13,7 +13,11 @@
 #include <asm/mmu.h>
 
 extern struct desc_struct boot_gdt_table[GDT_ENTRIES];
-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);
 
 struct Xgt_desc_struct {
 	unsigned short size;
@@ -24,7 +28,7 @@ extern struct Xgt_desc_struct idt_descr;
 extern struct Xgt_desc_struct idt_descr;
 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 desc_struct idt_table[];
@@ -104,7 +108,8 @@ static inline fastcall void native_set_l
 		pack_descriptor(&a, &b, (unsigned long)addr,
 				entries * sizeof(struct desc_struct) - 1,
 				DESCTYPE_LDT, 0);
-		write_gdt_entry(__get_cpu_var(cpu_gdt), GDT_ENTRY_LDT, a, b);
+		write_gdt_entry(__get_cpu_var(gdt_page).gdt, GDT_ENTRY_LDT,
+				a, b);
 		__asm__ __volatile__("lldt %w0"::"q" (GDT_ENTRY_LDT*8));
 	}
 }
#8 lore raw

PATCH 8/8 Convert PDA into the percpu section

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) Moves the boot cpu's GDT/percpu setup to smp_prepare_boot_cpu(), immediately after the per-cpu areas are allocated.

The result is one less x86-specific concept.

arch/i386/kernel/asm-offsets.c +0 -5
diff -r b30bbd45ba64 arch/i386/kernel/asm-offsets.c
@@ -15,7 +15,6 @@
 #include <asm/processor.h>
 #include <asm/thread_info.h>
 #include <asm/elf.h>
-#include <asm/pda.h>
 #ifdef CONFIG_LGUEST_GUEST
 #include <asm/lguest.h>
 #include "../lguest/lg.h"
@@ -105,10 +104,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 +24 -35

97 lines not on this page · raw message

arch/i386/kernel/entry.S +2 -3
diff -r b30bbd45ba64 arch/i386/kernel/entry.S
@@ -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 \
@@ -557,7 +557,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; \
@@ -682,7 +681,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 +2 -13
diff -r b30bbd45ba64 arch/i386/kernel/head.S
@@ -318,7 +318,6 @@ 2:	movl %cr0,%eax
 	movl %eax,%cr0
 
 	call check_x87
-	call setup_pda
 	lgdt early_gdt_descr
 	lidt idt_descr
 	ljmp $(__KERNEL_CS),$1f
@@ -333,7 +332,7 @@ 1:	movl $(__KERNEL_DS),%eax	# reload all
 	movl %eax,%gs
 	lldt %ax
 
-	movl $(__KERNEL_PDA),%eax
+	movl $(__KERNEL_PERCPU),%eax
 	mov  %eax,%fs
 
 	cld			# gcc2 wants the direction flag cleared at all times
@@ -363,16 +362,6 @@ check_x87:
 	ALIGN
 1:	movb $1,X86_HARD_MATH
 	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */
-	ret
-
-/* Initialize the boot_gdt_table's GDT_ENTRY_PDA (can't be done statically) */
-setup_pda:
-	movl $boot_pda, %eax
-	movl $boot_gdt_table, %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
 
 /*
@@ -635,7 +624,7 @@ ENTRY(boot_gdt_table)
 	.quad 0x004092000000ffff	/* 0xc8 APM DS    data */
 
 	.quad 0x00c0920000000000	/* 0xd0 - ESPFIX SS */
-	.quad 0x00cf92000000ffff	/* 0xd8 - PDA */
+	.quad 0x00cf92000000ffff	/* 0xd8 - per-cpu */
 	.quad 0x0000000000000000	/* 0xe0 - unused */
 	.quad 0x0000000000000000	/* 0xe8 - unused */
 	.quad 0x0000000000000000	/* 0xf0 - unused */
arch/i386/kernel/irq.c +3 -0
diff -r b30bbd45ba64 arch/i386/kernel/irq.c
@@ -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
diff -r b30bbd45ba64 arch/i386/kernel/process.c
@@ -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.
@@ -343,7 +349,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();
@@ -712,7 +718,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 +7 -2
diff -r b30bbd45ba64 arch/i386/kernel/smpboot.c
@@ -52,7 +52,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>
@@ -97,6 +96,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.
@@ -461,7 +463,6 @@ extern struct {
 	void * esp;
 	unsigned short ss;
 } stack_start;
-extern struct i386_pda *start_pda;
 
 #ifdef CONFIG_NUMA
 
@@ -1167,6 +1168,10 @@ void __devinit smp_prepare_boot_cpu(void
 	cpu_set(smp_processor_id(), cpu_present_map);
 	cpu_set(smp_processor_id(), cpu_possible_map);
 	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+
+	/* Set up %fs to point to our per-CPU area now it's allocated */
+	init_gdt(smp_processor_id(), &init_task);
+	cpu_set_gdt(smp_processor_id());
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
arch/i386/kernel/vmi.c +1 -1
diff -r b30bbd45ba64 arch/i386/kernel/vmi.c
@@ -549,7 +549,7 @@ 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;
arch/i386/lguest/core.c +1 -1
diff -r b30bbd45ba64 arch/i386/lguest/core.c
@@ -302,7 +302,7 @@ int run_guest(struct lguest *lg, char *_
 
 		/* Host state to be restored after the guest returns. */
 		asm("sidt %0":"=m"(lg->state->host.idt));
-		lg->state->host.gdt = __get_cpu_var(cpu_gdt_descr);
+		asm("sgdt %0":"=m"(lg->state->host.gdt));
 
 		/* Even if *we* don't want FPU trap, guest might... */
 		set_ts(lg->ts);
arch/i386/lguest/lguest.c +2 -8
diff -r b30bbd45ba64 arch/i386/lguest/lguest.c
@@ -34,7 +34,6 @@
 #include <asm/desc.h>
 #include <asm/setup.h>
 #include <asm/e820.h>
-#include <asm/pda.h>
 #include <asm/asm-offsets.h>
 #include <asm/mce.h>
 
@@ -509,13 +508,8 @@ static __attribute_used__ __init void lg
 	/* We use top of mem for initial pagetables. */
 	init_pg_tables_end = __pa(pg0);
 
-	/* set up PDA descriptor */
-	pack_descriptor((u32 *)&cpu_gdt_table[GDT_ENTRY_PDA].a,
-			(u32 *)&cpu_gdt_table[GDT_ENTRY_PDA].b,
-			(unsigned)&boot_pda, sizeof(boot_pda)-1,
-			0x80 | DESCTYPE_S | 0x02, 0);
-	load_gdt(&early_gdt_descr);
-	asm volatile ("mov %0, %%fs" : : "r" (__KERNEL_PDA) : "memory");
+	init_gdt(0, &init_task);
+	cpu_set_gdt(0);
 
 	reserve_top_address(lguest_data.reserve_mem);
 
include/asm-i386/current.h +3 -2
diff -r b30bbd45ba64 include/asm-i386/current.h
@@ -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
diff -r b30bbd45ba64 include/asm-i386/irq_regs.h
@@ -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 -100

100 lines not on this page · raw message

include/asm-i386/percpu.h +112 -10

143 lines not on this page · raw message

include/asm-i386/processor.h +1 -1
diff -r b30bbd45ba64 include/asm-i386/processor.h
@@ -425,7 +425,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
diff -r b30bbd45ba64 include/asm-i386/segment.h
@@ -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
diff -r b30bbd45ba64 include/asm-i386/smp.h
@@ -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__)
@@ -59,7 +58,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;
#9 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 8 lines · Rusty Russell

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.

hmm ... i very much like this, but its needs performance and kernel-size testing before it can move from -mm into mainline. We are now exposing wide ranges of the kernel to segment prefixes again. (Btw., i'd expect there to be a kernel size reduction.)

Ingo

#10 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 8 lines · Ingo Molnar
Quoted 6 lines · Rusty Russell

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.

Quoted 7 lines
Quoted 2 lines

So this patch
(1) Removes the PDA and uses per-cpu variables for each current member.

hmm ... i very much like this, but its needs performance and kernel-size testing before it can move from -mm into mainline. We are now exposing wide ranges of the kernel to segment prefixes again. (Btw., i'd expect there to be a kernel size reduction.)

Hi Ingo,

Thanks! There are some interesting issues. Because __get_cpu_var()
returns an lvalue, we don't use the %fs:value directly, but calculate offset (%fs:this_cpu_off + &value). So previously there was only a tiny code reduction.

If we used __thread, then gcc could do this optimization for us when it
knows an rvalue is needed, however:

1) gcc wants to use %gs, not %fs, which is measurably slower for the kernel,
2) gcc wants to use huge offsets to store the address of the per-cpu space, and this breaks Xen (and current lguest, but new lguest no longer uses segments for protection)

One solution would be to expose x86_read_percpu() as read_percpu() and implement it in asm-generic/percpu.h as well, then use it in places where only an rvalue is required.

Cheers!
Rusty.

#11 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 9 lines · Rusty Russell

If we used __thread, then gcc could do this optimization for us when it
knows an rvalue is needed, however:

1) gcc wants to use %gs, not %fs, which is measurably slower for the kernel,
2) gcc wants to use huge offsets to store the address of the per-cpu space, and this breaks Xen (and current lguest, but new lguest no longer uses segments for protection)

Well, if we go to the effort of teaching gcc how to use %fs, we can probably convince it to generate positive offset TLS relocs too.

J

#12 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

In reply to Rusty Russell
Quoted 14 lines · Rusty Russell

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) Moves the boot cpu's GDT/percpu setup to smp_prepare_boot_cpu(), immediately after the per-cpu areas are allocated.

Quoted 2 lines

The result is one less x86-specific concept.

Looks good. I think you can drop x86_add/sub/or_percpu; there are no users.

J

#13 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

In reply to Rusty Russell

Sigh -- i had hoped this had settled down because it was a merging nightmare last time. Ok.

include/asm-i386/percpu.h line 87

Quoted 9 lines
include/asm-i386/percpu.h +9 -0
+#define percpu_to_op(op,var,val)				\
+	do {							\
+		typedef typeof(var) T__;			\
+		if (0) { T__ tmp__; tmp__ = (val); }		\
+		switch (sizeof(var)) {				\
+		case 1:						\
+			asm(op "b %1,"__percpu_seg"%0"		\
+			    : "+m" (var)			\
+			    :"ri" ((T__)val));			\

Perhaps I'm blind but I can't see where the %fs reference is there. How does this work?

Do you have text size comparisons before/after and possible lmbench?

-Andi

#14 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 4 lines · Andi Kleen

Sigh -- i had hoped this had settled down because it was a merging nightmare last time. Ok.

Quoted 13 lines

include/asm-i386/percpu.h line 87

Quoted 10 lines
include/asm-i386/percpu.h +9 -0
+#define percpu_to_op(op,var,val)				\
+	do {							\
+		typedef typeof(var) T__;			\
+		if (0) { T__ tmp__; tmp__ = (val); }		\
+		switch (sizeof(var)) {				\
+		case 1:						\
+			asm(op "b %1,"__percpu_seg"%0"		\
+			    : "+m" (var)			\
+			    :"ri" ((T__)val));			\

Perhaps I'm blind but I can't see where the %fs reference is there.

__percpu_seg. It's defined to nothing in the UP case.

J

#15 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

In reply to Andi Kleen
Quoted 2 lines · Andi Kleen

Sigh -- i had hoped this had settled down because it was a merging nightmare last time. Ok.

Indeed, that's why I waited until everything else was fully merged and accepted.

Quoted 4 lines
Quoted 4 lines
include/asm-i386/percpu.h +4 -0
+#define percpu_to_op(op,var,val)				\
+	do {							\
+		typedef typeof(var) T__;			\
+		if (0) { T__ tmp__; tmp__ = (val); }		\
Quoted 8 lines

include/asm-i386/percpu.h line 87

Quoted 5 lines
include/asm-i386/percpu.h +5 -0
+		switch (sizeof(var)) {				\
+		case 1:						\
+			asm(op "b %1,"__percpu_seg"%0"		\
+			    : "+m" (var)			\
+			    :"ri" ((T__)val));			\

Perhaps I'm blind but I can't see where the %fs reference is there. How does this work?

Here:

+/* fs segment starts at (positive) offset == __per_cpu_offset[cpu] */ +#define __percpu_seg "%%fs:"
+#else /* !SMP */
+#include <asm-generic/percpu.h>
+#define __percpu_seg ""
+#endif /* SMP */

That's how we get SMP & non-SMP unification in that code.

Quoted 1 line

Do you have text size comparisons before/after and possible lmbench?

No, but I'll run them this evening. Last time the size reduction was slight, and there was no measurable performance improvement in microbenchmarks.

Thanks,
Rusty.

#16 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 6 lines · Rusty Russell
Quoted 1 line · Andi Kleen

Do you have text size comparisons before/after and possible lmbench?

No, but I'll run them this evening. Last time the size reduction was slight, and there was no measurable performance improvement in microbenchmarks.

Here are the size results, for a start:

UP:
Before:
size vmlinux
text data bss dec hex filename
3094881 243110 221184 3559175 364f07 vmlinux

After:
size vmlinux
text data bss dec hex filename
3093409 243142 221184 3557735 364967 vmlinux

SMP:
Before:
size vmlinux
text data bss dec hex filename
3222269 318770 237568 3778607 39a82f vmlinux

After:
size vmlinux
text data bss dec hex filename
3221421 314674 237568 3773663 3994df vmlinux

(The data size changes are moving from pda -> percpu, and on SMP removing the page-aligned PDA).

So, a slight win. lmbench tomorrow...

Rusty.

#17 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

In reply to Rusty Russell

Review

Quoted 5 lines · Rusty Russell
(quoted diff) +3 -0
+	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PERCPU].a,
+			(u32 *)&gdt[GDT_ENTRY_PERCPU].b,
+			__per_cpu_offset[cpu], 0xFFFFF,
 			0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */

Why testing with qemu is not enough.

arch/i386/kernel/cpu/common.c +1 -1
diff -r 8dcd1dc9b298 arch/i386/kernel/cpu/common.c
@@ -627,7 +627,7 @@ __cpuinit void init_gdt(int cpu, struct 
 	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PERCPU].a,
 			(u32 *)&gdt[GDT_ENTRY_PERCPU].b,
 			__per_cpu_offset[cpu], 0xFFFFF,
-			0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */
+			0x80 | DESCTYPE_S | 0x2, 0x8); /* present read-write data segment, G */
 	per_cpu(this_cpu_off, cpu) = __per_cpu_offset[cpu];
 	per_cpu(cpu_number, cpu) = cpu;
 #endif /* SMP*/

J

#18 lore raw

Re: [PATCH 8/8] Convert PDA into the percpu section

Quoted 8 lines · Jeremy Fitzhardinge

Review

Quoted 5 lines · Rusty Russell
(quoted diff) +3 -0
+	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PERCPU].a,
+			(u32 *)&gdt[GDT_ENTRY_PERCPU].b,
+			__per_cpu_offset[cpu], 0xFFFFF,
 			0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */

Why testing with qemu is not enough.

Indeed 8(.

Thanks!
Rusty.

Acked-by on v1 6/8
#19 lore raw

Re: [PATCH 6/8] Allow per-cpu variables to be page-aligned

In reply to Rusty Russell
Quoted 6 lines · Rusty Russell

Xen wants page-aligned GDT (and PDA must not cross a page-boundary, but that doesn't happen at the moment since it's so close to start of page). Let's allow page-alignment in general for per-cpu data.

Because larger alignments can use more room, we increase the max per-cpu memory to 64k rather than 32k: it's getting a little tight.

i recently needed page-aligned per-cpu data too for KVM-paravirt. Btw., what's the size increase of the native kernel?

Ingo

#20 lore raw

Re: [PATCH 6/8] Allow per-cpu variables to be page-aligned

Quoted 11 lines · Ingo Molnar
Quoted 6 lines · Rusty Russell

Xen wants page-aligned GDT (and PDA must not cross a page-boundary, but that doesn't happen at the moment since it's so close to start of page). Let's allow page-alignment in general for per-cpu data.

Because larger alignments can use more room, we increase the max per-cpu memory to 64k rather than 32k: it's getting a little tight.

i recently needed page-aligned per-cpu data too for KVM-paravirt. Btw., what's the size increase of the native kernel?

To clarify for those reading fast, this patch doesn't increase at all, since noone uses page-alignment. For the next patch which aligns the gdt, it's about +12k (21k -> 33k), but then we take away the pda in the next patch and it drops back to +8k (the pda is in the same file as the gdt, and on my system at least it ends up getting its own 4k alignment too).

AFAICT, we could save the wasted partial page in three ways: by trying to link it first, if it's only a case of one file, by having a special page-aligned percpu data section, or by having a smarter linker.

Cheers,
Rusty.

#21 lore raw

Re: [PATCH 6/8] Allow per-cpu variables to be page-aligned

Quoted 5 lines · Rusty Russell

AFAICT, we could save the wasted partial page in three ways: by trying to link it first, if it's only a case of one file, by having a special page-aligned percpu data section, or by having a smarter linker.

We already do the second of those for conventional data, so it would make sense for percpu as well. Smarter linker would be better, but ugh, I don't see that happening any time soon.

-hpa

#22 lore raw

Re: [PATCH 6/8] Allow per-cpu variables to be page-aligned

In reply to Rusty Russell
Quoted 13 lines · Rusty Russell

Xen wants page-aligned GDT (and PDA must not cross a page-boundary, but that doesn't happen at the moment since it's so close to start of page). Let's allow page-alignment in general for per-cpu data.

Because larger alignments can use more room, we increase the max per-cpu memory to 64k rather than 32k: it's getting a little tight.

diff -r 213b1ec27001 arch/alpha/kernel/vmlinux.lds.S

arch/alpha/kernel/vmlinux.lds.S SECTIONS · line 73

Quoted 6 lines
arch/alpha/kernel/vmlinux.lds.S +1 -1
@@ -69,7 +69,7 @@ SECTIONS
   . = ALIGN(8);
   SECURITY_INIT
 
-  . = ALIGN(64);
+  . = ALIGN(8192);
 

Isn't there a PAGE_SIZE we can use here? PAGE_SIZE_asm? (ditto all archs)

J

#23 lore raw

Re: [PATCH 6/8] Allow per-cpu variables to be page-aligned

Quoted 8 lines · Jeremy Fitzhardinge
Quoted 7 lines · Rusty Russell

diff -r 213b1ec27001 arch/alpha/kernel/vmlinux.lds.S

arch/alpha/kernel/vmlinux.lds.S +0 -0
@@ -69,7 +69,7 @@ SECTIONS
   . = ALIGN(8);
   SECURITY_INIT
 
Quoted 6 lines

arch/alpha/kernel/vmlinux.lds.S SECTIONS

Quoted 3 lines
arch/alpha/kernel/vmlinux.lds.S +1 -1
-  . = ALIGN(64);
+  . = ALIGN(8192);
 

Isn't there a PAGE_SIZE we can use here? PAGE_SIZE_asm? (ditto all archs)

Probably, but I carefully copied the style from other parts of the same file for the same arch. So, that cleanup would be another patch 8)

Rusty.

#25 lore raw

Re: [PATCH 3/8] Use per-cpu variables for GDT, PDA

In reply to Rusty Russell
Quoted 4 lines · Rusty Russell

Allocating PDA and GDT at boot is a pain. Using simple per-cpu variables adds happiness (although we need the GDT page-aligned for Xen, see later).

Not just Xen. Apparently it leads to general goodness.

Quoted 3 lines

Finally, we can simply call it "cpu_gdt" rather than enduring the superfluous and unnecessarily redundant tautology of "cpu_gdt_table".

Ah, well, at least it isn't excessive.

J

#26 lore raw

Re: [PATCH 2/8] Remove NR_CPUS from asm-generic/percpu.h

In reply to Rusty Russell

include/asm-generic/percpu.h line 8

Quoted 6 lines · Rusty Russell

I managed to get an error about NR_CPUS undefined after a make allyesconfig. Admittedly this was a patched kernel, but it's easy to remove it and avoid the error in future even if it's OK at the moment.

include/asm-generic/percpu.h +1 -1
-extern unsigned long __per_cpu_offset[NR_CPUS];
+extern unsigned long __per_cpu_offset[];

i'd much rather have us fix that include file dependency problem than to work it around by hiding the true dimension of the __per_cpu_offset array

Ingo

#27 lore raw

Re: [PATCH 1/8] Remove cpu_gdt_table: use boot_gdt_table until migration to per-cpu

In reply to Rusty Russell
Quoted 11 lines · Rusty Russell
arch/i386/kernel/head.S +1 -8
+/* The boot Global Descriptor Table: after boot we allocate a per-cpu copy */
 	.align L1_CACHE_BYTES
 ENTRY(boot_gdt_table)
-	.fill GDT_ENTRY_BOOT_CS,8,0
-	.quad 0x00cf9a000000ffff	/* kernel 4GB code at 0x00000000 */
-	.quad 0x00cf92000000ffff	/* kernel 4GB data at 0x00000000 */
-
-/*
- * The Global Descriptor Table contains 28 quadwords, per-CPU.
- */
-	.align L1_CACHE_BYTES

arch/i386/kernel/head.S idt_descr: · line 613

Quoted 10 lines
arch/i386/kernel/head.S +2 -3
-ENTRY(cpu_gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
 	.quad 0x0000000000000000	/* 0x0b reserved */
-	.quad 0x0000000000000000	/* 0x13 reserved */
-	.quad 0x0000000000000000	/* 0x1b reserved */
+	.quad 0x00cf9a000000ffff	/* boot: 4GB code at 0x00000000 */
+	.quad 0x00cf92000000ffff	/* boot: 4GB data at 0x00000000 */
 	.quad 0x0000000000000000	/* 0x20 unused */
 	.quad 0x0000000000000000	/* 0x28 unused */
 	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */

actually, the reason for the small boot GDT was that some systems wouldnt even boot with a larger GDT. (there was some BIOS interaction, forgot what it was - iirc it was mach-visws and also some other older box)

the 'simplification' you do here is actually how Linux worked before it was fixed for those systems.

so i'd be quite nervous to touch this area of the GDT code. (I'd support a renaming though, gdt_table is indeed ugly.)

Ingo

#28 lore raw

Re: [PATCH 1/8] Remove cpu_gdt_table: use boot_gdt_table until migration to per-cpu

Quoted 11 lines · Ingo Molnar

arch/i386/kernel/head.S idt_descr: · line 613

Quoted 5 lines
arch/i386/kernel/head.S +2 -0
+	.quad 0x00cf9a000000ffff	/* boot: 4GB code at 0x00000000 */
+	.quad 0x00cf92000000ffff	/* boot: 4GB data at 0x00000000 */
 	.quad 0x0000000000000000	/* 0x20 unused */
 	.quad 0x0000000000000000	/* 0x28 unused */
 	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */

actually, the reason for the small boot GDT was that some systems wouldnt even boot with a larger GDT. (there was some BIOS interaction, forgot what it was - iirc it was mach-visws and also some other older box)

Quoted 5 lines

the 'simplification' you do here is actually how Linux worked before it was fixed for those systems.

so i'd be quite nervous to touch this area of the GDT code. (I'd support a renaming though, gdt_table is indeed ugly.)

and this seems to have a ripple effect on the rest of your GDT changes - i'll wait for this to be cleared before doing a line by line review of them. I commented on the mostly-unaffected ones already.

Ingo

#29 lore raw

Re: [PATCH 1/8] Remove cpu_gdt_table: use boot_gdt_table until migration to per-cpu

In reply to Ingo Molnar
Quoted 20 lines · Ingo Molnar
Quoted 18 lines · Rusty Russell
arch/i386/kernel/head.S +3 -11
+/* The boot Global Descriptor Table: after boot we allocate a per-cpu copy */
 	.align L1_CACHE_BYTES
 ENTRY(boot_gdt_table)
-	.fill GDT_ENTRY_BOOT_CS,8,0
-	.quad 0x00cf9a000000ffff	/* kernel 4GB code at 0x00000000 */
-	.quad 0x00cf92000000ffff	/* kernel 4GB data at 0x00000000 */
-
-/*
- * The Global Descriptor Table contains 28 quadwords, per-CPU.
- */
-	.align L1_CACHE_BYTES
-ENTRY(cpu_gdt_table)
 	.quad 0x0000000000000000	/* NULL descriptor */
 	.quad 0x0000000000000000	/* 0x0b reserved */
-	.quad 0x0000000000000000	/* 0x13 reserved */
-	.quad 0x0000000000000000	/* 0x1b reserved */
+	.quad 0x00cf9a000000ffff	/* boot: 4GB code at 0x00000000 */
+	.quad 0x00cf92000000ffff	/* boot: 4GB data at 0x00000000 */
Quoted 8 lines

arch/i386/kernel/head.S idt_descr: · line 613

Quoted 3 lines
arch/i386/kernel/head.S +0 -0
 	.quad 0x0000000000000000	/* 0x20 unused */
 	.quad 0x0000000000000000	/* 0x28 unused */
 	.quad 0x0000000000000000	/* 0x33 TLS entry 1 */

actually, the reason for the small boot GDT was that some systems wouldnt even boot with a larger GDT. (there was some BIOS interaction, forgot what it was - iirc it was mach-visws and also some other older box)

Thanks for the explanation: I did wonder! I left the descriptor truncated to just the first few entries, so I'm *pretty sure* it won't
matter: the layout is identical.

I'd really like to try it and see, though; Andre?
Rusty.

#30 lore raw
In reply to Rusty Russell
Quoted 6 lines · Rusty Russell

Hi all,

The GDT stuff on x86 is a little more complex than it need be, but
playing with boot code is always dangerous. These compile and boot on UP and SMP for me, but Andrew should let the cook in -mm for a while.

Hi Rusty,

This is my rough hacking patch I needed to get things into a Xen-shape state.

There are a few of things here:

* init_gdt should always use write_gdt_entry when touching the gdt; if it doesn't and it ends up touching an already-installed gdt under Xen, it will get a write fault. This happens because init_gdt ends up getting called twice in SMP (see below).
* init_gdt should always be called before bringing up the cpu, rather than by the cpu itself (and therefore, cpu_init() shouldn't call it). Obviously the the boot cpu is an exception.
* secondary_cpu_init stops being necessary.
* On SMP, init_gdt can get called twice: first time in smp_prepare_boot_cpu, and a second time in trap_init. On UP, trap_init is the only caller.

arch/i386/kernel/cpu/common.c +20 -19
diff -r 8dcd1dc9b298 arch/i386/kernel/cpu/common.c
@@ -620,14 +620,24 @@ __cpuinit void init_gdt(int cpu, struct 
 __cpuinit void init_gdt(int cpu, struct task_struct *idle)
 {
 	struct desc_struct *gdt = per_cpu(gdt_page, cpu).gdt;
+	int i;
 
 	/* Based on boot_gdt_table: set percpu so it can be used immediately */
- 	memcpy(gdt, boot_gdt_table, GDT_SIZE);
+	for(i = 0; i < GDT_ENTRIES; i++) {
+		const struct desc_struct *bgdtt = &boot_gdt_table[i];
+		write_gdt_entry(gdt, i, bgdtt->a, bgdtt->b);
+	}
+
 #ifdef CONFIG_SMP
-	pack_descriptor((u32 *)&gdt[GDT_ENTRY_PERCPU].a,
-			(u32 *)&gdt[GDT_ENTRY_PERCPU].b,
-			__per_cpu_offset[cpu], 0xFFFFF,
-			0x80 | DESCTYPE_S | 0x2, 0); /* present read-write data segment */
+	{
+		u32 a, b;
+
+		/* present read-write data segment, pagesize granularity */
+		pack_descriptor(&a, &b, __per_cpu_offset[cpu], 0xFFFFF,
+				0x80 | DESCTYPE_S | 0x2, 0x8);
+		write_gdt_entry(gdt, GDT_ENTRY_PERCPU, a, b);
+	}
+
 	per_cpu(this_cpu_off, cpu) = __per_cpu_offset[cpu];
 	per_cpu(cpu_number, cpu) = cpu;
 #endif /* SMP*/
@@ -709,30 +719,21 @@ static void __cpuinit _cpu_init(int cpu,
 	mxcsr_feature_mask_init();
 }
 
-/* Entrypoint to initialize secondary CPU */
-void __cpuinit secondary_cpu_init(void)
-{
-	int cpu = smp_processor_id();
-	struct task_struct *curr = current;
-
-	_cpu_init(cpu, curr);
-}
-
 /*
  * cpu_init() initializes state that is per-CPU. Some data is already
  * initialized (naturally) in the bootstrap process, such as the GDT
  * and IDT. We reload them nevertheless, this function acts as a
  * 'CPU state barrier', nothing should get across.
+ * 
+ * Note: we expect the gdt itself to be already set up for this
+ * processor; we just point the cpu at it.
  */
 void __cpuinit cpu_init(void)
 {
 	int cpu = smp_processor_id();
-	struct task_struct *curr = current;
-
-	/* Set up the real GDT so we can transition from the boot_gdt_table. */
-	init_gdt(cpu, curr);
+
 	cpu_set_gdt(cpu);
-	_cpu_init(cpu, curr);
+	_cpu_init(cpu, current);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
arch/i386/kernel/smpboot.c +13 -11
diff -r 8dcd1dc9b298 arch/i386/kernel/smpboot.c
@@ -381,14 +381,14 @@ static void __cpuinit start_secondary(vo
 static void __cpuinit start_secondary(void *unused)
 {
 	/*
-	 * Don't put *anything* before secondary_cpu_init(), SMP
-	 * booting is too fragile that we want to limit the
-	 * things done here to the most necessary things.
+	 * Don't put *anything* before cpu_init(), SMP booting is too
+	 * fragile that we want to limit the things done here to the
+	 * most necessary things.
 	 */
 #ifdef CONFIG_VMI
 	vmi_bringup();
 #endif
-	secondary_cpu_init();
+	cpu_init();
 	preempt_disable();
 	smp_callin();
 	while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
@@ -1165,15 +1165,17 @@ void __init native_smp_prepare_cpus(unsi
 
 void __devinit native_smp_prepare_boot_cpu(void)
 {
-	cpu_set(smp_processor_id(), cpu_online_map);
-	cpu_set(smp_processor_id(), cpu_callout_map);
-	cpu_set(smp_processor_id(), cpu_present_map);
-	cpu_set(smp_processor_id(), cpu_possible_map);
-	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+	int cpu = smp_processor_id();
+
+	cpu_set(cpu, cpu_online_map);
+	cpu_set(cpu, cpu_callout_map);
+	cpu_set(cpu, cpu_present_map);
+	cpu_set(cpu, cpu_possible_map);
+	per_cpu(cpu_state, cpu) = CPU_ONLINE;
 
 	/* Set up %fs to point to our per-CPU area now it's allocated */
-	init_gdt(smp_processor_id(), &init_task);
-	cpu_set_gdt(smp_processor_id());
+	init_gdt(cpu, &init_task);
+	cpu_set_gdt(cpu);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
arch/i386/kernel/traps.c +1 -0
diff -r 8dcd1dc9b298 arch/i386/kernel/traps.c
@@ -1181,6 +1181,7 @@ void __init trap_init(void)
 	/*
 	 * Should be a barrier for any external CPU state.
 	 */
+	init_gdt(smp_processor_id(), current);
 	cpu_init();
 
 	trap_init_hook();
include/asm-i386/processor.h +1 -1
diff -r 8dcd1dc9b298 include/asm-i386/processor.h
@@ -744,6 +744,6 @@ extern int sysenter_setup(void);
 
 extern void init_gdt(int cpu, struct task_struct *idle);
 extern void cpu_set_gdt(int);
-extern void secondary_cpu_init(void);
+extern void cpu_init(void);
 
 #endif /* __ASM_I386_PROCESSOR_H */
#31 lore raw
Quoted 11 lines · Jeremy Fitzhardinge
Quoted 6 lines · Rusty Russell

Hi all,

The GDT stuff on x86 is a little more complex than it need be, but
playing with boot code is always dangerous. These compile and boot on UP and SMP for me, but Andrew should let the cook in -mm for a while.

Hi Rusty,

This is my rough hacking patch I needed to get things into a Xen-shape state.

Looks good. Just one thing:

Quoted 11 lines

void __devinit native_smp_prepare_boot_cpu(void)
{

arch/i386/kernel/smpboot.c +4 -5
-	cpu_set(smp_processor_id(), cpu_online_map);
-	cpu_set(smp_processor_id(), cpu_callout_map);
-	cpu_set(smp_processor_id(), cpu_present_map);
-	cpu_set(smp_processor_id(), cpu_possible_map);
-	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+	int cpu = smp_processor_id();
+
+	cpu_set(cpu, cpu_online_map);
+	cpu_set(cpu, cpu_callout_map);

arch/i386/kernel/smpboot.c void __init native_smp_prepare_cpus(unsi · line 1179

Quoted 10 lines
arch/i386/kernel/smpboot.c +5 -2
+	cpu_set(cpu, cpu_present_map);
+	cpu_set(cpu, cpu_possible_map);
+	per_cpu(cpu_state, cpu) = CPU_ONLINE;
 
 	/* Set up %fs to point to our per-CPU area now it's allocated */
-	init_gdt(smp_processor_id(), &init_task);
-	cpu_set_gdt(smp_processor_id());
+	init_gdt(cpu, &init_task);
+	cpu_set_gdt(cpu);
 }

This is called "pissing in the corners". Don't do it: we don't need to touch that code and I actually prefer the original anyway (explicit is *good*).

The habit of extracting cpu number once then using it is an optimization which we should be aiming to get rid of (it simply hurts archs with efficient per-cpu implementations).

Cheers,
Rusty.

#32 lore raw
Quoted 7 lines · Rusty Russell

This is called "pissing in the corners". Don't do it: we don't need to touch that code and I actually prefer the original anyway (explicit is *good*).

The habit of extracting cpu number once then using it is an optimization which we should be aiming to get rid of (it simply hurts archs with efficient per-cpu implementations).

No, that was for a reason. I was worried about smp_processor_id() not returning valid values between init_gdt and cpu_set_gdt. It's not actually a problem, but relying on smp_processor_id() while we're moving the foundations its based on seems fragile.

J

#33 lore raw
Quoted 5 lines · Jeremy Fitzhardinge
Quoted 4 lines · Rusty Russell

This is called "pissing in the corners". Don't do it: we don't need to touch that code and I actually prefer the original anyway (explicit is *good*).

Quoted 8 lines
Quoted 3 lines

The habit of extracting cpu number once then using it is an optimization which we should be aiming to get rid of (it simply hurts archs with efficient per-cpu implementations).

No, that was for a reason. I was worried about smp_processor_id() not returning valid values between init_gdt and cpu_set_gdt. It's not actually a problem, but relying on smp_processor_id() while we're moving the foundations its based on seems fragile.

smp_processor_id() always works, so it's fundamental, not fragile.

However, we *should* remove the arg from cpu_set_gdt, since we have such faith in smp_processor_id() 8)

Cheers,
Rusty.

#34 lore raw
In reply to Jeremy Fitzhardinge
Quoted 4 lines · Jeremy Fitzhardinge

* init_gdt should always use write_gdt_entry when touching the gdt; if it doesn't and it ends up touching an already-installed gdt under Xen, it will get a write fault. This happens because init_gdt ends up getting called twice in SMP (see below).

Hmm, this invalidated my assumption that write_gdt_entry is always a write to this cpu's active gdt. Better fix is not to call it twice anyway...

Quoted 3 lines

* init_gdt should always be called before bringing up the cpu, rather than by the cpu itself (and therefore, cpu_init() shouldn't call it). Obviously the the boot cpu is an exception.

Makes sense.

Quoted 1 line

* secondary_cpu_init stops being necessary.

Indeed.

Quoted 3 lines

* On SMP, init_gdt can get called twice: first time in smp_prepare_boot_cpu, and a second time in trap_init. On UP, trap_init is the only caller.

Getting rid of the call in smp_prepare_boot_cpu currently works, but it's fragile: __get_cpu_var(x) && per_cpu(x, smp_processor_id()) will differ, and changes made to __get_cpu_var(x) will vanish...

Fortunately, UP doesn't have to call init_gdt at all, so I think it's better to place it in smp_prepare_boot_cpu only and then clean up the UP code. I'll try now...

Rusty.

#35 lore raw
Quoted 4 lines · Rusty Russell

Hmm, this invalidated my assumption that write_gdt_entry is always a write to this cpu's active gdt. Better fix is not to call it twice anyway...

No, I don't think that's true. I implemented the write_*_entry functions with the assumption they could be called either on setup or on an in-use entry. I think its good policy to use it all the time anyway, since the pv_ops backend might want to fiddle with the values on the way through.

I tried to avoid calling init_gdt twice, but it seemed cleaner to just let it happen.

Quoted 4 lines

Getting rid of the call in smp_prepare_boot_cpu currently works, but it's fragile: __get_cpu_var(x) && per_cpu(x, smp_processor_id()) will differ, and changes made to __get_cpu_var(x) will vanish...

Yes. I think its definitely a good idea to call init_gdt asap after doing the percpu setup.

Quoted 4 lines

Fortunately, UP doesn't have to call init_gdt at all, so I think it's better to place it in smp_prepare_boot_cpu only and then clean up the UP code. I'll try now...

It doesn't? The per-cpu gdt is the same as the boot gdt?

J