Skip to contentSkip to conversation

x86-64: Convert the PDA to percpu.

PATCH In review Brian Gerst 3 participants 10 messages Dec 23, 2008 – Dec 27, 2008

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

PATCH 1/3 x86-64: Convert the PDA to percpu.

This patch makes the PDA a normal per-cpu variable, allowing the removal of the special allocator code. %gs still points to the base of the PDA.

Tested on a dual-core AMD64 system.

---
 arch/x86/include/asm/pda.h     |    3 --
 arch/x86/include/asm/percpu.h  |    3 --
 arch/x86/include/asm/setup.h   |    1 -
 arch/x86/kernel/cpu/common.c   |    6 ++--
 arch/x86/kernel/dumpstack_64.c |    8 ++--
 arch/x86/kernel/head64.c       |   23 +------------
 arch/x86/kernel/irq.c          |    2 +-
 arch/x86/kernel/nmi.c          |    2 +-
 arch/x86/kernel/setup_percpu.c |   70 ++++++++--------------------------------
 arch/x86/kernel/smpboot.c      |   58 +--------------------------------
 arch/x86/xen/enlighten.c       |    2 +-
 arch/x86/xen/smp.c             |   12 +------
 12 files changed, 27 insertions(+), 163 deletions(-)
arch/x86/include/asm/pda.h +0 -3
index 2fbfff8..b008127 100644
@@ -39,11 +39,8 @@ struct x8664_pda {
 	unsigned irq_spurious_count;
 } ____cacheline_aligned_in_smp;
 
-extern struct x8664_pda **_cpu_pda;
 extern void pda_init(int);
 
-#define cpu_pda(i) (_cpu_pda[i])
-
 /*
  * There is no fast way to get the base address of the PDA, all the accesses
  * have to mention %fs/%gs.  So it needs to be done this Torvaldian way.
arch/x86/include/asm/percpu.h +0 -3
index ece7205..6f866fd 100644
@@ -12,11 +12,8 @@
 #ifdef CONFIG_SMP
 #include <asm/pda.h>
 
-#define __per_cpu_offset(cpu) (cpu_pda(cpu)->data_offset)
 #define __my_cpu_offset read_pda(data_offset)
 
-#define per_cpu_offset(x) (__per_cpu_offset(x))
-
 #endif
 #include <asm-generic/percpu.h>
 
arch/x86/include/asm/setup.h +0 -1
index f12d372..b751aaf 100644
@@ -93,7 +93,6 @@ extern unsigned long init_pg_tables_start;
 extern unsigned long init_pg_tables_end;
 
 #else
-void __init x86_64_init_pda(void);
 void __init x86_64_start_kernel(char *real_mode);
 void __init x86_64_start_reservations(char *real_mode_data);
 
arch/x86/kernel/cpu/common.c +3 -3
index b9c9ea0..7d741ef 100644
@@ -857,8 +857,8 @@ __setup("clearcpuid=", setup_disablecpuid);
 cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE;
 
 #ifdef CONFIG_X86_64
-struct x8664_pda **_cpu_pda __read_mostly;
-EXPORT_SYMBOL(_cpu_pda);
+DEFINE_PER_CPU_PAGE_ALIGNED(struct x8664_pda, pda);
+EXPORT_PER_CPU_SYMBOL(pda);
 
 struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table };
 
@@ -866,7 +866,7 @@ char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss;
 
 void __cpuinit pda_init(int cpu)
 {
-	struct x8664_pda *pda = cpu_pda(cpu);
+	struct x8664_pda *pda = &per_cpu(pda, cpu);
 
 	/* Setup up data that may be needed in __get_free_pages early */
 	loadsegment(fs, 0);
arch/x86/kernel/dumpstack_64.c +4 -4
index 96a5db7..1098b21 100644
@@ -163,7 +163,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
 		const struct stacktrace_ops *ops, void *data)
 {
 	const unsigned cpu = get_cpu();
-	unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
+	unsigned long *irqstack_end = (unsigned long *)per_cpu(pda, cpu).irqstackptr;
 	unsigned used = 0;
 	struct thread_info *tinfo;
 
@@ -306,9 +306,9 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
 	int i;
 	const int cpu = smp_processor_id();
 	unsigned long *irqstack_end =
-		(unsigned long *) (cpu_pda(cpu)->irqstackptr);
+		(unsigned long *) (per_cpu(pda, cpu).irqstackptr);
 	unsigned long *irqstack =
-		(unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE);
+		(unsigned long *) (per_cpu(pda, cpu).irqstackptr - IRQSTACKSIZE);
 
 	/*
 	 * debugging aid: "show_stack(NULL, NULL);" prints the
@@ -374,7 +374,7 @@ void show_registers(struct pt_regs *regs)
 	int i;
 	unsigned long sp;
 	const int cpu = smp_processor_id();
-	struct task_struct *cur = cpu_pda(cpu)->pcurrent;
+	struct task_struct *cur = per_cpu(pda, cpu).pcurrent;
 
 	sp = regs->sp;
 	printk("CPU %d ", cpu);
arch/x86/kernel/head64.c +1 -22
index d16084f..274e2a9 100644
@@ -25,27 +25,6 @@
 #include <asm/e820.h>
 #include <asm/bios_ebda.h>
 
-/* boot cpu pda */
-static struct x8664_pda _boot_cpu_pda __read_mostly;
-
-#ifdef CONFIG_SMP
-/*
- * We install an empty cpu_pda pointer table to indicate to early users
- * (numa_set_node) that the cpu_pda pointer table for cpus other than
- * the boot cpu is not yet setup.
- */
-static struct x8664_pda *__cpu_pda[NR_CPUS] __initdata;
-#else
-static struct x8664_pda *__cpu_pda[NR_CPUS] __read_mostly;
-#endif
-
-void __init x86_64_init_pda(void)
-{
-	_cpu_pda = __cpu_pda;
-	cpu_pda(0) = &_boot_cpu_pda;
-	pda_init(0);
-}
-
 static void __init zap_identity_mappings(void)
 {
 	pgd_t *pgd = pgd_offset_k(0UL);
@@ -111,7 +90,7 @@ void __init x86_64_start_kernel(char * real_mode_data)
 	if (console_loglevel == 10)
 		early_printk("Kernel alive\n");
 
-	x86_64_init_pda();
+	pda_init(0);
 
 	x86_64_start_reservations(real_mode_data);
 }
arch/x86/kernel/irq.c +1 -1
index d1d4dc5..066e680 100644
@@ -38,7 +38,7 @@ void ack_bad_irq(unsigned int irq)
 #ifdef CONFIG_X86_32
 # define irq_stats(x)		(&per_cpu(irq_stat, x))
 #else
-# define irq_stats(x)		cpu_pda(x)
+# define irq_stats(x)		(&per_cpu(pda, x))
 #endif
 /*
  * /proc/interrupts printing:
arch/x86/kernel/nmi.c +1 -1
index 2c97f07..4a5bb40 100644
@@ -64,7 +64,7 @@ static int endflag __initdata;
 static inline unsigned int get_nmi_count(int cpu)
 {
 #ifdef CONFIG_X86_64
-	return cpu_pda(cpu)->__nmi_count;
+	return per_cpu(pda, cpu).__nmi_count;
 #else
 	return nmi_count(cpu);
 #endif
arch/x86/kernel/setup_percpu.c +14 -56

99 lines not on this page · raw message

arch/x86/kernel/smpboot.c +1 -57

76 lines not on this page · raw message

arch/x86/xen/enlighten.c +1 -1
index 5e4686d..0160bb6 100644
@@ -1649,7 +1649,7 @@ asmlinkage void __init xen_start_kernel(void)
 #ifdef CONFIG_X86_64
 	/* Disable until direct per-cpu data access. */
 	have_vcpu_info_placement = 0;
-	x86_64_init_pda();
+	pda_init(0);
 #endif
 
 	xen_smp_init();
arch/x86/xen/smp.c +1 -11
index acd9b67..17823cb 100644
@@ -280,22 +280,12 @@ static int __cpuinit xen_cpu_up(unsigned int cpu)
 	struct task_struct *idle = idle_task(cpu);
 	int rc;
 
-#ifdef CONFIG_X86_64
-	/* Allocate node local memory for AP pdas */
-	WARN_ON(cpu == 0);
-	if (cpu > 0) {
-		rc = get_local_pda(cpu);
-		if (rc)
-			return rc;
-	}
-#endif
-
 #ifdef CONFIG_X86_32
 	init_gdt(cpu);
 	per_cpu(current_task, cpu) = idle;
 	irq_ctx_init(cpu);
 #else
-	cpu_pda(cpu)->pcurrent = idle;
+	per_cpu(pda, cpu).pcurrent = idle;
 	clear_tsk_thread_flag(idle, TIF_FORK);
 #endif
 	xen_setup_timer(cpu);
Signature · 1 line
1.6.1.rc1
#1 lore raw

PATCH 2/3 x86-64: Unify x86_*_percpu() functions.

Merge the 32-bit and 64-bit versions of these functions. Unlike 32-bit, the segment base is the current cpu's PDA instead of the offset from the original per-cpu area. This is because GCC hardcodes the stackprotector canary at %gs:40. Since the assembler is incapable of relocating against multiple symbols, the code ends up looking like:

movq $per_cpu__var, reg
subq $per_cpu__pda, reg
movq %gs:(reg), reg

This is still atomic since the offset is a constant (just calculated at runtime) and not dependant on the cpu number.

---
 arch/x86/include/asm/percpu.h |   92 +++++++++++++++++-----------------------
 1 files changed, 39 insertions(+), 53 deletions(-)
arch/x86/include/asm/percpu.h +39 -53
index 6f866fd..f704243 100644
@@ -1,54 +1,9 @@
 #ifndef _ASM_X86_PERCPU_H
 #define _ASM_X86_PERCPU_H
 
-#ifdef CONFIG_X86_64
-#include <linux/compiler.h>
-
-/* Same as asm-generic/percpu.h, except that we store the per cpu offset
-   in the PDA. Longer term the PDA and every per cpu variable
-   should be just put into a single section and referenced directly
-   from %gs */
-
-#ifdef CONFIG_SMP
-#include <asm/pda.h>
-
-#define __my_cpu_offset read_pda(data_offset)
-
-#endif
-#include <asm-generic/percpu.h>
-
-DECLARE_PER_CPU(struct x8664_pda, pda);
-
-/*
- * These are supposed to be implemented as a single instruction which
- * operates on the per-cpu data base segment.  x86-64 doesn't have
- * that yet, so this is a fairly inefficient workaround for the
- * meantime.  The single instruction is atomic with respect to
- * preemption and interrupts, so we need to explicitly disable
- * interrupts here to achieve the same effect.  However, because it
- * can be used from within interrupt-disable/enable, we can't actually
- * disable interrupts; disabling preemption is enough.
- */
-#define x86_read_percpu(var)						\
-	({								\
-		typeof(per_cpu_var(var)) __tmp;				\
-		preempt_disable();					\
-		__tmp = __get_cpu_var(var);				\
-		preempt_enable();					\
-		__tmp;							\
-	})
-
-#define x86_write_percpu(var, val)					\
-	do {								\
-		preempt_disable();					\
-		__get_cpu_var(var) = (val);				\
-		preempt_enable();					\
-	} while(0)
-
-#else /* CONFIG_X86_64 */
-
 #ifdef __ASSEMBLY__
 
+#ifdef CONFIG_X86_32
 /*
  * PER_CPU finds an address of a per-cpu variable.
  *
@@ -72,6 +27,8 @@ DECLARE_PER_CPU(struct x8664_pda, pda);
 #define PER_CPU_VAR(var)	per_cpu__##var
 #endif	/* SMP */
 
+#endif	/* X86_32 */
+
 #else /* ...!ASSEMBLY */
 
 /*
@@ -88,19 +45,37 @@ DECLARE_PER_CPU(struct x8664_pda, pda);
  */
 #ifdef CONFIG_SMP
 
+#ifdef CONFIG_X86_32
+
 #define __my_cpu_offset x86_read_percpu(this_cpu_off)
 
 /* fs segment starts at (positive) offset == __per_cpu_offset[cpu] */
 #define __percpu_seg "%%fs:"
+#define __percpu_seg_off(x) (x)
+
+#else
+
+#define __my_cpu_offset read_pda(data_offset)
+
+#define __percpu_seg "%%gs:"
+#define __percpu_seg_off(x) RELOC_HIDE((x), -(unsigned long)&per_cpu__pda)
+
+#endif
 
 #else  /* !SMP */
 
 #define __percpu_seg ""
+#define __percpu_seg_off(x) (x)
 
 #endif	/* SMP */
 
 #include <asm-generic/percpu.h>
 
+#ifdef CONFIG_X86_64
+#include <asm/pda.h>
+DECLARE_PER_CPU(struct x8664_pda, pda);
+#endif
+
 /* We can use this directly for local CPU (faster). */
 DECLARE_PER_CPU(unsigned long, this_cpu_off);
 
@@ -111,6 +86,7 @@ extern void __bad_percpu_size(void);
 #define percpu_to_op(op, var, val)			\
 do {							\
 	typedef typeof(var) T__;			\
+	typeof(var) *var__ = __percpu_seg_off(&var);	\
 	if (0) {					\
 		T__ tmp__;				\
 		tmp__ = (val);				\
@@ -118,17 +94,22 @@ do {							\
 	switch (sizeof(var)) {				\
 	case 1:						\
 		asm(op "b %1,"__percpu_seg"%0"		\
-		    : "+m" (var)			\
+		    : "+m" (*var__)			\
 		    : "ri" ((T__)val));			\
 		break;					\
 	case 2:						\
 		asm(op "w %1,"__percpu_seg"%0"		\
-		    : "+m" (var)			\
+		    : "+m" (*var__)			\
 		    : "ri" ((T__)val));			\
 		break;					\
 	case 4:						\
 		asm(op "l %1,"__percpu_seg"%0"		\
-		    : "+m" (var)			\
+		    : "+m" (*var__)			\
+		    : "ri" ((T__)val));			\
+		break;					\
+	case 8:						\
+		asm(op "q %1,"__percpu_seg"%0"		\
+		    : "+m" (*var__)			\
 		    : "ri" ((T__)val));			\
 		break;					\
 	default: __bad_percpu_size();			\
@@ -138,21 +119,27 @@ do {							\
 #define percpu_from_op(op, var)				\
 ({							\
 	typeof(var) ret__;				\
+	typeof(var) *var__ = __percpu_seg_off(&var);	\
 	switch (sizeof(var)) {				\
 	case 1:						\
 		asm(op "b "__percpu_seg"%1,%0"		\
 		    : "=r" (ret__)			\
-		    : "m" (var));			\
+		    : "m" (*var__));			\
 		break;					\
 	case 2:						\
 		asm(op "w "__percpu_seg"%1,%0"		\
 		    : "=r" (ret__)			\
-		    : "m" (var));			\
+		    : "m" (*var__));			\
 		break;					\
 	case 4:						\
 		asm(op "l "__percpu_seg"%1,%0"		\
 		    : "=r" (ret__)			\
-		    : "m" (var));			\
+		    : "m" (*var__));			\
+		break;					\
+	case 8:						\
+		asm(op "q "__percpu_seg"%1,%0"		\
+		    : "=r" (ret__)			\
+		    : "m" (*var__));			\
 		break;					\
 	default: __bad_percpu_size();			\
 	}						\
@@ -165,7 +152,6 @@ do {							\
 #define x86_sub_percpu(var, val) percpu_to_op("sub", per_cpu__##var, val)
 #define x86_or_percpu(var, val) percpu_to_op("or", per_cpu__##var, val)
 #endif /* !__ASSEMBLY__ */
-#endif /* !CONFIG_X86_64 */
 
 #ifdef CONFIG_SMP
 
Signature · 1 line
1.6.1.rc1
#2 lore raw

PATCH 3/3 x86-64: Move cpu number from PDA to per-cpu and consolidate with 32-bit.

A simple example of what can now be done to consolidate with the 32-bit code.

---
 arch/x86/include/asm/pda.h       |    2 +-
 arch/x86/include/asm/smp.h       |    4 +---
 arch/x86/kernel/asm-offsets_64.c |    1 -
 arch/x86/kernel/cpu/common.c     |    1 -
 arch/x86/kernel/process_32.c     |    3 ---
 arch/x86/kernel/setup_percpu.c   |    4 ++++
 arch/x86/kernel/smpcommon.c      |    1 -
 7 files changed, 6 insertions(+), 10 deletions(-)
arch/x86/include/asm/pda.h +1 -1
index b008127..3ace2b9 100644
@@ -15,7 +15,7 @@ struct x8664_pda {
 	unsigned long kernelstack;	/* 16 top of kernel stack for current */
 	unsigned long oldrsp;		/* 24 user rsp for system call */
 	int irqcount;			/* 32 Irq nesting counter. Starts -1 */
-	unsigned int cpunumber;		/* 36 Logical CPU number */
+	unsigned int unused1;		/* 36 was cpunumber */
 #ifdef CONFIG_CC_STACKPROTECTOR
 	unsigned long stack_canary;	/* 40 stack canary value */
 					/* gcc-ABI: this canary MUST be at
arch/x86/include/asm/smp.h +1 -3
index d12811c..f2e4b9c 100644
@@ -34,9 +34,7 @@ extern cpumask_t cpu_initialized;
 DECLARE_PER_CPU(cpumask_t, cpu_sibling_map);
 DECLARE_PER_CPU(cpumask_t, cpu_core_map);
 DECLARE_PER_CPU(u16, cpu_llc_id);
-#ifdef CONFIG_X86_32
 DECLARE_PER_CPU(int, cpu_number);
-#endif
 
 DECLARE_EARLY_PER_CPU(u16, x86_cpu_to_apicid);
 DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid);
@@ -169,7 +167,7 @@ extern unsigned disabled_cpus __cpuinitdata;
 extern int safe_smp_processor_id(void);
 
 #elif defined(CONFIG_X86_64_SMP)
-#define raw_smp_processor_id()	read_pda(cpunumber)
+#define raw_smp_processor_id() (x86_read_percpu(cpu_number))
 
 #define stack_smp_processor_id()					\
 ({								\
arch/x86/kernel/asm-offsets_64.c +0 -1
index 7fcf63d..b14bb4e 100644
@@ -51,7 +51,6 @@ int main(void)
 	ENTRY(oldrsp); 
 	ENTRY(pcurrent); 
 	ENTRY(irqcount);
-	ENTRY(cpunumber);
 	ENTRY(irqstackptr);
 	ENTRY(data_offset);
 	BLANK();
arch/x86/kernel/cpu/common.c +0 -1
index 7d741ef..82ad755 100644
@@ -876,7 +876,6 @@ void __cpuinit pda_init(int cpu)
 	wrmsrl(MSR_GS_BASE, pda);
 	mb();
 
-	pda->cpunumber = cpu;
 	pda->irqcount = -1;
 	pda->kernelstack = (unsigned long)stack_thread_info() -
 				 PDA_STACKOFFSET + THREAD_SIZE;
arch/x86/kernel/process_32.c +0 -3
index 0a1302f..4bb0a7d 100644
@@ -65,9 +65,6 @@ asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
 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.
  */
arch/x86/kernel/setup_percpu.c +4 -0
index fb0ccdc..f013781 100644
@@ -15,6 +15,9 @@
 #include <asm/apicdef.h>
 #include <asm/highmem.h>
 
+DEFINE_PER_CPU(int, cpu_number);
+EXPORT_PER_CPU_SYMBOL(cpu_number);
+
 #ifdef CONFIG_X86_LOCAL_APIC
 unsigned int num_processors;
 unsigned disabled_cpus __cpuinitdata;
@@ -133,6 +136,7 @@ void __init setup_per_cpu_areas(void)
 			memset(&per_cpu(pda, cpu), 0, sizeof(struct x8664_pda));
 		per_cpu(pda, cpu).data_offset = __per_cpu_offset[cpu];
 #endif
+		per_cpu(cpu_number, cpu) = cpu;
 	}
 
 #ifdef CONFIG_X86_64
arch/x86/kernel/smpcommon.c +0 -1
index 397e309..db7c5a6 100644
@@ -25,6 +25,5 @@ __cpuinit void init_gdt(int cpu)
 			GDT_ENTRY_PERCPU, &gdt, DESCTYPE_S);
 
 	per_cpu(this_cpu_off, cpu) = __per_cpu_offset[cpu];
-	per_cpu(cpu_number, cpu) = cpu;
 }
 #endif
Signature · 1 line
1.6.1.rc1
#3 lore raw

Re: [PATCH 2/3] x86-64: Unify x86_*_percpu() functions.

In reply to Brian Gerst
Quoted 6 lines · Brian Gerst

Merge the 32-bit and 64-bit versions of these functions. Unlike 32-bit, the segment base is the current cpu's PDA instead of the offset from the original per-cpu area. This is because GCC hardcodes the stackprotector canary at %gs:40. Since the assembler is incapable of relocating against multiple symbols, the code ends up looking like:

Quoted 7 lines

movq $per_cpu__var, reg
subq $per_cpu__pda, reg
movq %gs:(reg), reg

This is still atomic since the offset is a constant (just calculated at runtime) and not dependant on the cpu number.

Yeah, it's a real pity we can't convince the linker to do this simple computation as a single %gs:ADDR addressing mode. On the other hand, if the compiler can reuse the computation of %reg 2-3 times, then the generated code could well end up being denser.

J

#4 lore raw

Re: [PATCH 2/3] x86-64: Unify x86_*_percpu() functions.

Quoted 11 lines · Jeremy Fitzhardinge
Quoted 10 lines · Brian Gerst

Merge the 32-bit and 64-bit versions of these functions. Unlike 32-bit, the segment base is the current cpu's PDA instead of the offset from the original per-cpu area. This is because GCC hardcodes the stackprotector canary at %gs:40. Since the assembler is incapable of relocating against multiple symbols, the code ends up looking like:

movq $per_cpu__var, reg
subq $per_cpu__pda, reg
movq %gs:(reg), reg

Quoted 8 lines
Quoted 3 lines

This is still atomic since the offset is a constant (just calculated at runtime) and not dependant on the cpu number.

Yeah, it's a real pity we can't convince the linker to do this simple computation as a single %gs:ADDR addressing mode. On the other hand, if the compiler can reuse the computation of %reg 2-3 times, then the generated code could well end up being denser.

There's a nice project for linker hackers?

I'd like to see some kernel image size measurements done on x86 defconfig to see how much real impact this has on code density. Unless the impact is horribly unacceptable, removing ~200 lines of weird x86-specific APIs is a definitive plus.

Ingo

#5 lore raw

Re: [PATCH 2/3] x86-64: Unify x86_*_percpu() functions.

Quoted 19 lines · Ingo Molnar
Quoted 16 lines · Jeremy Fitzhardinge
Quoted 14 lines · Brian Gerst

Merge the 32-bit and 64-bit versions of these functions. Unlike 32-bit, the segment base is the current cpu's PDA instead of the offset from the original per-cpu area. This is because GCC hardcodes the stackprotector canary at %gs:40. Since the assembler is incapable of relocating against multiple symbols, the code ends up looking like:

movq $per_cpu__var, reg
subq $per_cpu__pda, reg
movq %gs:(reg), reg

This is still atomic since the offset is a constant (just calculated at runtime) and not dependant on the cpu number.

Quoted 6 lines
Quoted 2 lines

Yeah, it's a real pity we can't convince the linker to do this simple computation as a single %gs:ADDR addressing mode. On the other hand, if the compiler can reuse the computation of %reg 2-3 times, then the generated code could well end up being denser.

There's a nice project for linker hackers?

I'd like to see some kernel image size measurements done on x86 defconfig to see how much real impact this has on code density. Unless the impact is horribly unacceptable, removing ~200 lines of weird x86-specific APIs is a definitive plus.

Yep, I'm all for it. I don't think there'll be much of a size impact at all.

J

#6 lore raw
In reply to Brian Gerst

(Cc:-ed a few more people who might be interested in this)

Quoted 7 lines · Brian Gerst

This patch makes the PDA a normal per-cpu variable, allowing the removal of the special allocator code. %gs still points to the base of the PDA.

Tested on a dual-core AMD64 system.

Quoted 14 lines
---
 arch/x86/include/asm/pda.h     |    3 --
 arch/x86/include/asm/percpu.h  |    3 --
 arch/x86/include/asm/setup.h   |    1 -
 arch/x86/kernel/cpu/common.c   |    6 ++--
 arch/x86/kernel/dumpstack_64.c |    8 ++--
 arch/x86/kernel/head64.c       |   23 +------------
 arch/x86/kernel/irq.c          |    2 +-
 arch/x86/kernel/nmi.c          |    2 +-
 arch/x86/kernel/setup_percpu.c |   70 ++++++++--------------------------------
 arch/x86/kernel/smpboot.c      |   58 +--------------------------------
 arch/x86/xen/enlighten.c       |    2 +-
 arch/x86/xen/smp.c             |   12 +------
 12 files changed, 27 insertions(+), 163 deletions(-)

the simplification factor is significant. I'm wondering, have you measured the code size impact of this on say the defconfig x86 kernel? That will generally tell us how much worse optimizations the compiler does under this scheme.

Ingo

#7 lore raw
Quoted 27 lines · Ingo Molnar

(Cc:-ed a few more people who might be interested in this)

Quoted 7 lines · Brian Gerst

This patch makes the PDA a normal per-cpu variable, allowing the removal of the special allocator code. %gs still points to the base of the PDA.

Tested on a dual-core AMD64 system.

Quoted 14 lines
---
 arch/x86/include/asm/pda.h     |    3 --
 arch/x86/include/asm/percpu.h  |    3 --
 arch/x86/include/asm/setup.h   |    1 -
 arch/x86/kernel/cpu/common.c   |    6 ++--
 arch/x86/kernel/dumpstack_64.c |    8 ++--
 arch/x86/kernel/head64.c       |   23 +------------
 arch/x86/kernel/irq.c          |    2 +-
 arch/x86/kernel/nmi.c          |    2 +-
 arch/x86/kernel/setup_percpu.c |   70 ++++++++--------------------------------
 arch/x86/kernel/smpboot.c      |   58 +--------------------------------
 arch/x86/xen/enlighten.c       |    2 +-
 arch/x86/xen/smp.c             |   12 +------
 12 files changed, 27 insertions(+), 163 deletions(-)
Quoted 7 lines

the simplification factor is significant. I'm wondering, have you measured the code size impact of this on say the defconfig x86 kernel? That will generally tell us how much worse optimizations the compiler does under this scheme.

Ingo

Patch #1 by itself doesn't change how the PDA is accessed, only how it is allocated. The text size goes down significantly with patch #1, but data goes up. Changing the PDA to cacheline-aligned (1a) brings it back in line.

text data bss dec hex filename

7033648	1754476	 758508	9546632	 91ab88	vmlinux.0   (vanilla 2.6.28)
7029563	1758428	 758508	9546499	 91ab03	vmlinux.1   (with patch #1)
7029563	1754460	 758508	9542531	 919b83	vmlinux.1a  (with patch #1 cache align)
7036694	1758428	 758508	9553630	 91c6de	vmlinux.3   (with all three patches)

I think the first patch (with the alignment fix) is a clear win. As for the other patches, they add about 8 bytes per use of a PDA variable. cpu_number is used 903 times in this compile, so this is likely the most extreme example. I have an idea to optimize this particular case further that I'd like to look at which would lessen the impact.

--
Brian Gerst

#8 lore raw
Quoted 47 lines · Brian Gerst
Quoted 27 lines · Ingo Molnar

(Cc:-ed a few more people who might be interested in this)

Quoted 7 lines · Brian Gerst

This patch makes the PDA a normal per-cpu variable, allowing the removal of the special allocator code. %gs still points to the base of the PDA.

Tested on a dual-core AMD64 system.

Quoted 14 lines
---
 arch/x86/include/asm/pda.h     |    3 --
 arch/x86/include/asm/percpu.h  |    3 --
 arch/x86/include/asm/setup.h   |    1 -
 arch/x86/kernel/cpu/common.c   |    6 ++--
 arch/x86/kernel/dumpstack_64.c |    8 ++--
 arch/x86/kernel/head64.c       |   23 +------------
 arch/x86/kernel/irq.c          |    2 +-
 arch/x86/kernel/nmi.c          |    2 +-
 arch/x86/kernel/setup_percpu.c |   70 ++++++++--------------------------------
 arch/x86/kernel/smpboot.c      |   58 +--------------------------------
 arch/x86/xen/enlighten.c       |    2 +-
 arch/x86/xen/smp.c             |   12 +------
 12 files changed, 27 insertions(+), 163 deletions(-)
Quoted 7 lines

the simplification factor is significant. I'm wondering, have you measured the code size impact of this on say the defconfig x86 kernel? That will generally tell us how much worse optimizations the compiler does under this scheme.

Ingo

Patch #1 by itself doesn't change how the PDA is accessed, only how it is allocated. The text size goes down significantly with patch #1, but data goes up. Changing the PDA to cacheline-aligned (1a) brings it back in line.

text data bss dec hex filename

7033648	1754476	 758508	9546632	 91ab88	vmlinux.0   (vanilla 2.6.28)
7029563	1758428	 758508	9546499	 91ab03	vmlinux.1   (with patch #1)
7029563	1754460	 758508	9542531	 919b83	vmlinux.1a  (with patch #1 cache align)
7036694	1758428	 758508	9553630	 91c6de	vmlinux.3   (with all three patches)
Quoted 5 lines

I think the first patch (with the alignment fix) is a clear win. As for the other patches, they add about 8 bytes per use of a PDA variable. cpu_number is used 903 times in this compile, so this is likely the most extreme example. I have an idea to optimize this particular case further that I'd like to look at which would lessen the impact.

curious, what idea is that?

Ingo

#9 lore raw
Quoted 56 lines · Ingo Molnar
Quoted 47 lines · Brian Gerst
Quoted 27 lines · Ingo Molnar

(Cc:-ed a few more people who might be interested in this)

Quoted 7 lines · Brian Gerst

This patch makes the PDA a normal per-cpu variable, allowing the removal of the special allocator code. %gs still points to the base of the PDA.

Tested on a dual-core AMD64 system.

Quoted 14 lines
---
 arch/x86/include/asm/pda.h     |    3 --
 arch/x86/include/asm/percpu.h  |    3 --
 arch/x86/include/asm/setup.h   |    1 -
 arch/x86/kernel/cpu/common.c   |    6 ++--
 arch/x86/kernel/dumpstack_64.c |    8 ++--
 arch/x86/kernel/head64.c       |   23 +------------
 arch/x86/kernel/irq.c          |    2 +-
 arch/x86/kernel/nmi.c          |    2 +-
 arch/x86/kernel/setup_percpu.c |   70 ++++++++--------------------------------
 arch/x86/kernel/smpboot.c      |   58 +--------------------------------
 arch/x86/xen/enlighten.c       |    2 +-
 arch/x86/xen/smp.c             |   12 +------
 12 files changed, 27 insertions(+), 163 deletions(-)
Quoted 7 lines

the simplification factor is significant. I'm wondering, have you measured the code size impact of this on say the defconfig x86 kernel? That will generally tell us how much worse optimizations the compiler does under this scheme.

Ingo

Patch #1 by itself doesn't change how the PDA is accessed, only how it is allocated. The text size goes down significantly with patch #1, but data goes up. Changing the PDA to cacheline-aligned (1a) brings it back in line.

text data bss dec hex filename

7033648       1754476  758508 9546632  91ab88 vmlinux.0   (vanilla 2.6.28)
7029563       1758428  758508 9546499  91ab03 vmlinux.1   (with patch #1)
7029563       1754460  758508 9542531  919b83 vmlinux.1a  (with patch #1 cache align)
7036694       1758428  758508 9553630  91c6de vmlinux.3   (with all three patches)
Quoted 5 lines

I think the first patch (with the alignment fix) is a clear win. As for the other patches, they add about 8 bytes per use of a PDA variable. cpu_number is used 903 times in this compile, so this is likely the most extreme example. I have an idea to optimize this particular case further that I'd like to look at which would lessen the impact.

Quoted 4 lines

curious, what idea is that?

Ingo

Something like this:

+#define raw_smp_processor_id()                                         \
+({                                                                     \
+       extern int gsoff__cpu_number;                                   \
+       int cpu;                                                        \
+       __asm__("movl %%gs:%1, %0" : "=r" (cpu)                         \
+                                  : "m" (gsoff__cpu_number);           \
+       cpu;                                                            \

+})

And add this to vmlinux_64.lds.S:
+#define GSOFF(x) gsoff__##x = per_cpu__##x - per_cpu__pda
+ GSOFF(cpu_number);

The trick is that the linker can calculate against multiple symbols, but it must be done in the final link. The problem with this approach is that only a limited set of symbols can be used. There isn't a simple solution for all per-cpu variables. Some post-processing would have to be done, similar to kallsyms.

Looking some more at the usage statistics of the PDA members, there are four heavy hitters:
pda->pcurrent (2719)
pda->kernelstack (1055)
pda->cpunumber (933)
pda->data_offset (327)

The rest of the PDA members have an insignificant number of accesses. I think for now I'll avoid converting the above four fields until an optimal solution can be agreed on, but the others (primarily the TLB and irqstat fields) can be converted without bloating the kernel code alot.

--
Brian Gerst