x86, fpu: dynamically allocate 'struct fpu'
Subsystems (6)
RFC PATCH 1/1 x86, fpu: dynamically allocate 'struct fpu'
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the 'task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation at fork(). The only real downside here is that we have to stick everything and the end of the task_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
---
b/arch/x86/include/asm/fpu/types.h | 72 +++++++++++++++++++------------------ b/arch/x86/include/asm/processor.h | 10 +++-- b/arch/x86/kernel/fpu/init.c | 39 ++++++++++++++++++++ b/arch/x86/kernel/process.c | 2 - b/fs/proc/kcore.c | 4 +- b/include/linux/sched.h | 12 +++++- b/kernel/fork.c | 8 +++- 7 files changed, 104 insertions(+), 43 deletions(-)
arch/x86/include/asm/processor.h renamed from arch/x86/include/asm/processor.h~dynamically-allocate-struct-fpu +7 -3
@@ -390,9 +390,6 @@ struct thread_struct {#endif unsigned long gs; -/* Floating point and extended processor state */ -struct fpu fpu; -/* Save middle states of ptrace breakpoints */ struct perf_event *ptrace_bps[HBP_NUM ];/* Debug status used for traps, single steps, etc... */
@@ -418,6 +415,13 @@ struct thread_struct {unsigned long iopl;/* Max allowed port in the bitmap, in bytes: */ unsigned io_bitmap_max; + +/* Floating point and extended processor state */ +struct fpu fpu; +/* +* WARNING: 'fpu' is dynamically-sized. It *MUST* be at +* the end. +*/ };/*
include/linux/sched.h renamed from include/linux/sched.h~dynamically-allocate-struct-fpu +10 -2
@@ -1522,8 +1522,6 @@ struct task_struct {/* hung task detection */ unsigned long last_switch_count;#endif -/* CPU-specific state of this task */ -struct thread_struct thread;/* filesystem information */ struct fs_struct *fs;/* open file information */
@@ -1778,8 +1776,18 @@ struct task_struct {unsigned long task_state_change;#endif int pagefault_disabled; +/* CPU-specific state of this task */ +struct thread_struct thread; +/* +* WARNING: on x86, 'thread_struct' contains a variable-sized +* structure. It *MUST* be at the end of 'task_struct'. +* +* Do not put anything below here! +*/ }; +extern int arch_task_struct_size (void ); +/* Future-safe accessor for struct task_struct's cpus_allowed. */ #define tsk_cpus_allowed (tsk) (&(tsk)->cpus_allowed)
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +39 -0
@@ -136,6 +136,45 @@ static void __init fpu__init_system_geneunsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size); +#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ + +/* +* We append the 'struct fpu' to the task_struct. +*/ +int __weak arch_task_struct_size (void ) +{ +int task_size =sizeof (struct task_struct ); + +/* +* Subtract off the static size of the register state. +* It potentially has a bunch of padding. +*/ + task_size -=sizeof (((struct task_struct *)0 )->thread.fpu.state); + +/* +* Add back the dynamically-calculated register state +* size. +*/ + task_size += xstate_size; + +/* +* We dynamically size 'struct fpu', so we require that +* it be at the end of 'thread_struct' and that +* 'thread_struct' be at the end of 'task_struct'. If +* you hit a compile error here, check the structure to +* see if something got added to the end. +*/ + CHECK_MEMBER_AT_END_OF(struct fpu, state); + CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu); + CHECK_MEMBER_AT_END_OF(struct task_struct, thread); + +return task_size; +} +/* * Set up the xstate_size based on the legacy FPU context size. *
kernel/fork.c renamed from kernel/fork.c~dynamically-allocate-struct-fpu +7 -1
@@ -287,15 +287,21 @@ static void set_max_threads(unsigned int max_threads =clamp_t (u64, threads,MIN_THREADS ,MAX_THREADS ); } +int __weak arch_task_struct_size (void ) +{ +return sizeof (struct task_struct ); +} +void __init fork_init (void ) { +int task_struct_size =arch_task_struct_size ();#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN L1_CACHE_BYTES#endif /* create a slab on which task_structs can be allocated */ task_struct_cachep = -kmem_cache_create ("task_struct" ,sizeof (struct task_struct ), +kmem_cache_create ("task_struct" , task_struct_size,ARCH_MIN_TASKALIGN ,SLAB_PANIC |SLAB_NOTRACK ,NULL );#endif
arch/x86/include/asm/fpu/types.h renamed from arch/x86/include/asm/fpu/types.h~dynamically-allocate-struct-fpu +38 -34
90 lines not on this page · raw message
arch/x86/kernel/process.c renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu +1 -1
@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregist*/ int arch_dup_task_struct (struct task_struct *dst,struct task_struct *src) { - *dst = *src; +memcpy (dst, src,arch_task_struct_size ());return fpu__copy (&dst->thread.fpu, &src->thread.fpu); }
fs/proc/kcore.c renamed from fs/proc/kcore.c~dynamically-allocate-struct-fpu +2 -2
@@ -92,7 +92,7 @@ static size_t get_kcore_size(int *nphdr, roundup(sizeof(CORE_STR), 4)) +roundup (sizeof (struct elf_prstatus ),4 ) +roundup (sizeof (struct elf_prpsinfo ),4 ) + -roundup (sizeof (struct task_struct ),4 ); +roundup (arch_task_struct_size (),4 ); *elf_buflen =PAGE_ALIGN (*elf_buflen);return size + *elf_buflen; }
@@ -415,7 +415,7 @@ static void elf_kcore_store_hdr(char *bu/* set up the task structure */ notes[2 ].name =CORE_STR ; notes[2 ].type =NT_TASKSTRUCT ; - notes[2 ].datasz =sizeof (struct task_struct ); + notes[2 ].datasz =arch_task_struct_size (); notes[2 ].data = current; nhdr->p_filesz +=notesize (¬es[2 ]);
_
Quoted 13 lines · Dave Hansen
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the '
task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation atfork(). The only real downside here is that we have to stick everything and the end of thetask_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
Quoted 2 lines
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
Looks generally sensible. Minor nitpicking below.
arch/x86/kernel/fpu/init.c static void __init fpu__init_system_gene · line 144
Quoted 6 lines
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +6
-0+#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ +
You could save a bit of typing by using offsetofend here. Something along the lines of BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER));
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 EXPORT_SYMBOL_GPL(idle_notifier_unregist · line 84
Quoted 10 lines
#endif /*
_ASM_X86_FPU_H*/
diff -puNarch/x86/kernel/process.c~dynamically-allocate-struct-fpuarch/x86/kernel/process.carch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +1
-1@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregist */ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { - *dst = *src; + memcpy(dst, src, arch_task_struct_size());
This is actually vaguely performance-critical, which makes me thing that using some kind of inline or other real way (config macro, ifdef, etc) to detect whether there's an arch override would be better than a weak function.
--Andy
Quoted 11 lines · Andy Lutomirski
arch/x86/kernel/fpu/init.c static void __init fpu__init_system_gene · line 144
Quoted 6 lines · Dave Hansen
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +6
-0+#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ +You could save a bit of typing by using offsetofend here. Something along the lines of
BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER));
Good point.
Quoted 7 lines
Quoted 7 lines
#endif /*
_ASM_X86_FPU_H*/
diff -puNarch/x86/kernel/process.c~dynamically-allocate-struct-fpuarch/x86/kernel/process.carch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +0
-0@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregist */ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
Quoted 8 lines
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 EXPORT_SYMBOL_GPL(idle_notifier_unregist · line 84
Quoted 3 lines
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +1
-1{ - *dst = *src; + memcpy(dst, src, arch_task_struct_size());This is actually vaguely performance-critical, which makes me thing that using some kind of inline or other real way (config macro, ifdef, etc) to detect whether there's an arch override would be better than a weak function.
Fair enough. I'll send out another version in a bit if there are no more comments.
Quoted 16 lines · Dave Hansen
Quoted 7 lines
Quoted 7 lines
#endif /*
_ASM_X86_FPU_H*/
diff -puNarch/x86/kernel/process.c~dynamically-allocate-struct-fpuarch/x86/kernel/process.carch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +0
-0@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregist */ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)Quoted 8 lines
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 EXPORT_SYMBOL_GPL(idle_notifier_unregist · line 84
Quoted 3 lines
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +1
-1{ - *dst = *src; + memcpy(dst, src, arch_task_struct_size());This is actually vaguely performance-critical, which makes me thing that using some kind of inline or other real way (config macro, ifdef, etc) to detect whether there's an arch override would be better than a weak function.
Quoted 2 lines
Fair enough. I'll send out another version in a bit if there are no more comments.
Beyond making it a build time switch for other architectures, I'd also suggest introducing a __read_mostly task_struct_size variable on x86, so that we can write:
memcpy(dst, src, task_struct_size);
Thanks,
Ingo
PATCH 1/1 x86/fpu, sched: Introduce CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT and use it on x86
Quoted 19 lines · Ingo Molnar
Quoted 16 lines · Dave Hansen
Quoted 15 lines
arch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 EXPORT_SYMBOL_GPL(idle_notifier_unregist · line 84
Quoted 10 lines
#endif /*
_ASM_X86_FPU_H*/
diff -puNarch/x86/kernel/process.c~dynamically-allocate-struct-fpuarch/x86/kernel/process.carch/x86/kernel/process.c 2015-07-16 12:00:59.204808551 -0700 renamed from arch/x86/kernel/process.c~dynamically-allocate-struct-fpu 2015-07-16 10:50:42.360571875 -0700 +1
-1@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregist */ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { - *dst = *src; + memcpy(dst, src, arch_task_struct_size());This is actually vaguely performance-critical, which makes me thing that using some kind of inline or other real way (config macro, ifdef, etc) to detect whether there's an arch override would be better than a weak function.
Quoted 8 lines
Quoted 2 lines
Fair enough. I'll send out another version in a bit if there are no more comments.
Beyond making it a build time switch for other architectures, I'd also suggest introducing a
__read_mostlytask_struct_sizevariable on x86, so that we can write:memcpy(dst, src, task_struct_size);
I.e. like the patch below, on top of your patch plus the bug.h patch I sent.
Only build tested.
Thanks,
Ingo
======================>
Quoted 1 line
From
ab5da866792e4297de2308b42121633419f6d06dMon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Fri, 17 Jul 2015 11:27:36 +0200
Subject: [PATCH] x86/fpu, sched: Introduce CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT and use it on x86
Don't burden architectures without dynamic task_struct sizing with the overhead of dynamic sizing.
Also optimize the x86 code a bit by caching task_struct_size.
--- arch/Kconfig | 4 ++++ arch/x86/Kconfig | 1 + arch/x86/kernel/fpu/init.c | 9 ++++++--- arch/x86/kernel/process.c | 2 +- fs/proc/kcore.c | 4 ++-- include/linux/sched.h | 4 +++- kernel/fork.c | 21 ++++++++------------- 7 files changed, 25 insertions(+), 20 deletions(-)
arch/Kconfig +4 -0
@@ -221,6 +221,10 @@ config ARCH_TASK_STRUCT_ALLOCATOR config ARCH_THREAD_INFO_ALLOCATOR bool +# Select if arch wants to size task_struct dynamically +config ARCH_WANTS_DYNAMIC_TASK_STRUCT + bool + config HAVE_REGS_AND_STACK_ACCESS_API bool help
arch/x86/Kconfig +1 -0
@@ -41,6 +41,7 @@ config X86 select ARCH_USE_CMPXCHG_LOCKREF if X86_64 select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS + select ARCH_WANTS_DYNAMIC_TASK_STRUCT select ARCH_WANT_FRAME_POINTERS select ARCH_WANT_IPC_PARSE_VERSION if X86_32 select ARCH_WANT_OPTIONAL_GPIOLIB
arch/x86/kernel/fpu/init.c +6 -3
@@ -4,6 +4,8 @@#include <asm/fpu/internal.h> #include <asm/tlbflush.h> +#include <linux/sched.h> +/* * Initialize the TS bit in CR0 according to the style of context-switches * we are using:
@@ -137,9 +139,9 @@ unsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size);/* -* We append the 'struct fpu' to the task_struct. +* We append the 'struct fpu' to the task_struct: */ -int arch_task_struct_size (void ) +static void __init fpu__init_task_struct_size (void ) {int task_size =sizeof (struct task_struct );
@@ -159,7 +161,7 @@ int arch_task_struct_size(void) CHECK_MEMBER_AT_END_OF(struct fpu, state); CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu); -return task_size; + task_struct_size = task_size; }/*
@@ -313,6 +315,7 @@ void __init fpu__init_system(struct cpuinfo_x86 *c)fpu__init_system_generic ();fpu__init_system_xstate_size_legacy ();fpu__init_system_xstate (); +fpu__init_task_struct_size ();fpu__init_system_ctx_switch (); }
arch/x86/kernel/process.c +1 -1
@@ -81,7 +81,7 @@ EXPORT_SYMBOL_GPL(idle_notifier_unregister);*/ int arch_dup_task_struct (struct task_struct *dst,struct task_struct *src) { -memcpy (dst, src,arch_task_struct_size ()); +memcpy (dst, src, task_struct_size);return fpu__copy (&dst->thread.fpu, &src->thread.fpu); }
fs/proc/kcore.c +2 -2
@@ -92,7 +92,7 @@ static size_t get_kcore_size(int *nphdr, size_t *elf_buflen) roundup(sizeof(CORE_STR), 4)) +roundup (sizeof (struct elf_prstatus ),4 ) +roundup (sizeof (struct elf_prpsinfo ),4 ) + -roundup (arch_task_struct_size (),4 ); +roundup (task_struct_size,4 ); *elf_buflen =PAGE_ALIGN (*elf_buflen);return size + *elf_buflen; }
@@ -415,7 +415,7 @@ static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)/* set up the task structure */ notes[2 ].name =CORE_STR ; notes[2 ].type =NT_TASKSTRUCT ; - notes[2 ].datasz =arch_task_struct_size (); + notes[2 ].datasz = task_struct_size; notes[2 ].data = current; nhdr->p_filesz +=notesize (¬es[2 ]);
include/linux/sched.h +3 -1
@@ -1786,7 +1786,9 @@ struct task_struct {*/ }; -extern int arch_task_struct_size (void ); +#ifdef CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT +extern int task_struct_size__read_mostly ; +#endif /* Future-safe accessor for struct task_struct's cpus_allowed. */ #define tsk_cpus_allowed (tsk) (&(tsk)->cpus_allowed)
kernel/fork.c +8 -13
@@ -287,14 +287,15 @@ static void set_max_threads(unsigned int max_threads_suggested) max_threads =clamp_t (u64, threads,MIN_THREADS ,MAX_THREADS ); } -#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ - BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER)) +#ifdef CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT +/* Initialized by the architecture: */ +int task_struct_size__read_mostly ; +#else +/* Static build time size: */ +static const int task_struct_size =sizeof (struct task_struct ); +#endif -/* -* This function can be overridden by the architecture to support dynamic sizing -* of the task_struct: -*/ -int __weak arch_task_struct_size (void ) +void __init fork_init (void ) {/* * Build-time checks for structure layout constraints:
@@ -307,12 +308,6 @@ int __weak arch_task_struct_size(void)*/ CHECK_MEMBER_AT_END_OF(struct task_struct, thread); -return sizeof (struct task_struct ); -} - -void __init fork_init (void ) -{ -int task_struct_size =arch_task_struct_size ();#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR #ifndef ARCH_MIN_TASKALIGN #define ARCH_MIN_TASKALIGN L1_CACHE_BYTES
Peter Zijlstra
maintainerQuoted 33 lines · Dave Hansen
+++
b/arch/x86/kernel/fpu/init.c2015-07-16 12:02:15.284280976 -0700arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +28
-0@@ -136,6 +136,45 @@ static void __init fpu__init_system_geneunsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size); +#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ + +/* +* We append the 'struct fpu' to the task_struct. +*/ +int __weak arch_task_struct_size (void ) +{ +int task_size =sizeof (struct task_struct ); + +/* +* Subtract off the static size of the register state. +* It potentially has a bunch of padding. +*/ + task_size -=sizeof (((struct task_struct *)0 )->thread.fpu.state); + +/* +* Add back the dynamically-calculated register state +* size. +*/ + task_size += xstate_size; + +/* +* We dynamically size 'struct fpu', so we require that +* it be at the end of 'thread_struct' and that
arch/x86/kernel/fpu/init.c static void __init fpu__init_system_gene · line 176
Quoted 10 lines
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +10
-0+* 'thread_struct' be at the end of 'task_struct'. If +* you hit a compile error here, check the structure to +* see if something got added to the end. +*/ + CHECK_MEMBER_AT_END_OF(struct fpu, state); + CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu); + CHECK_MEMBER_AT_END_OF(struct task_struct, thread); + +return task_size; +}
Since you want these invariants true at all times, maybe put the BUILD_BUG_ON() in generic code instead of x86 specific? That way people poking at other archs are less likely to accidentally break your stuff.
Quoted 40 lines · Peter Zijlstra
Quoted 39 lines · Dave Hansen
+++
b/arch/x86/kernel/fpu/init.c2015-07-16 12:02:15.284280976 -0700arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +34
-0@@ -136,6 +136,45 @@ static void __init fpu__init_system_geneunsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size); +#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ + +/* +* We append the 'struct fpu' to the task_struct. +*/ +int __weak arch_task_struct_size (void ) +{ +int task_size =sizeof (struct task_struct ); + +/* +* Subtract off the static size of the register state. +* It potentially has a bunch of padding. +*/ + task_size -=sizeof (((struct task_struct *)0 )->thread.fpu.state); + +/* +* Add back the dynamically-calculated register state +* size. +*/ + task_size += xstate_size; + +/* +* We dynamically size 'struct fpu', so we require that +* it be at the end of 'thread_struct' and that +* 'thread_struct' be at the end of 'task_struct'. If +* you hit a compile error here, check the structure to +* see if something got added to the end. +*/ + CHECK_MEMBER_AT_END_OF(struct fpu, state); + CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu);
Quoted 8 lines
arch/x86/kernel/fpu/init.c static void __init fpu__init_system_gene · line 176
Quoted 4 lines
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +4
-0+ CHECK_MEMBER_AT_END_OF(struct task_struct, thread); + +return task_size; +}Since you want these invariants true at all times, maybe put the
BUILD_BUG_ON()in generic code instead of x86 specific? That way people poking at other archs are less likely to accidentally break your stuff.
Yeah.
Thanks,
Ingo
PATCH 1/1 x86/fpu, bug.h: Move CHECK_MEMBER_AT_END_OF() to a generic header and use it in generic code
Quoted 52 lines · Ingo Molnar
Quoted 40 lines · Peter Zijlstra
Quoted 39 lines · Dave Hansen
+++
b/arch/x86/kernel/fpu/init.c2015-07-16 12:02:15.284280976 -0700(quoted diff) +34
-0@@ -136,6 +136,45 @@ static void __init fpu__init_system_gene unsigned int xstate_size; EXPORT_SYMBOL_GPL(xstate_size); +#define CHECK_MEMBER_AT_END_OF(TYPE, MEMBER) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ + +/* + * We append the 'struct fpu' to the task_struct. + */ +int __weak arch_task_struct_size(void) +{ + int task_size = sizeof(struct task_struct); + + /* + * Subtract off the static size of the register state. + * It potentially has a bunch of padding. + */ + task_size -= sizeof(((struct task_struct *)0)->thread.fpu.state); + + /* + * Add back the dynamically-calculated register state + * size. + */ + task_size += xstate_size; + + /* + * We dynamically size 'struct fpu', so we require that + * it be at the end of 'thread_struct' and that + * 'thread_struct' be at the end of 'task_struct'. If + * you hit a compile error here, check the structure to + * see if something got added to the end. + */ + CHECK_MEMBER_AT_END_OF(struct fpu, state); + CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu);Quoted 8 lines
Review static void __init fpu__init_system_gene · line 176
Quoted 4 lines
(quoted diff) +4
-0+ CHECK_MEMBER_AT_END_OF(struct task_struct, thread); + + return task_size; +}Since you want these invariants true at all times, maybe put the
BUILD_BUG_ON()in generic code instead of x86 specific? That way people poking at other archs are less likely to accidentally break your stuff.
Quoted 1 line
Yeah.
The patch below implements this. Only build tested.
Thanks,
Ingo
===============>
--- arch/x86/kernel/fpu/init.c | 17 ++--------------- include/linux/bug.h | 4 ++++ kernel/fork.c | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 15 deletions(-)
arch/x86/kernel/fpu/init.c +2 -15
@@ -136,16 +136,10 @@ static void __init fpu__init_system_geneunsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size); -#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ - BUILD_BUG_ON((sizeof(TYPE) - \ - offsetof(TYPE, MEMBER) - \ - sizeof(((TYPE *)0)->MEMBER)) > \ - 0) \ -/* * We append the 'struct fpu' to the task_struct. */ -int __weak arch_task_struct_size (void ) +int arch_task_struct_size (void ) {int task_size =sizeof (struct task_struct );
@@ -161,16 +155,9 @@ int __weak arch_task_struct_size(void)*/ task_size += xstate_size; -/* -* We dynamically size 'struct fpu', so we require that -* it be at the end of 'thread_struct' and that -* 'thread_struct' be at the end of 'task_struct'. If -* you hit a compile error here, check the structure to -* see if something got added to the end. -*/ +/* Build time FPU structure layout debug checks: */ CHECK_MEMBER_AT_END_OF(struct fpu, state); CHECK_MEMBER_AT_END_OF(struct thread_struct, fpu); - CHECK_MEMBER_AT_END_OF(struct task_struct, thread);return task_size; }
include/linux/bug.h +4 -0
@@ -85,6 +85,10 @@ struct pt_regs;#endif /* __CHECKER__ */ +/* Enforce that 'MEMBER' is the last field of 'TYPE': */ +#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER)) +#ifdef CONFIG_GENERIC_BUG #include <asm-generic/bug.h>
kernel/fork.c +18 -0
@@ -287,8 +287,26 @@ static void set_max_threads(unsigned int max_threads =clamp_t (u64, threads,MIN_THREADS ,MAX_THREADS ); } +#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER)) + +/* +* This function can be overridden by the architecture to support dynamic sizing +* of the task_struct: +*/ int __weak arch_task_struct_size (void ) { +/* +* Build-time checks for structure layout constraints: +* +* On some architectures we dynamically size 'struct thread_struct', +* so we require that it be at the end of 'task_struct'. +* +* If you hit a compile error here, check the structure to +* see if something got added to the end. +*/ + CHECK_MEMBER_AT_END_OF(struct task_struct, thread); +return sizeof (struct task_struct ); }
H. Peter Anvin
reviewerQuoted 12 lines · Dave Hansen
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the '
task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation atfork(). The only real downside here is that we have to stick everything and the end of thetask_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
Quoted 3 lines
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
No. I have also long advocated for merging task_struct and thread_info into a common structure and get it off the stack; it would improve security and avoid weird corner cases in the irqstack handling.
-hpa
Quoted 13 lines · H. Peter Anvin
Quoted 12 lines · Dave Hansen
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the '
task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation atfork(). The only real downside here is that we have to stick everything and the end of thetask_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
Quoted 8 lines
Quoted 3 lines
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
No. I have also long advocated for merging
task_structandthread_infointo a common structure and get it off the stack; it would improve security and avoid weird corner cases in the irqstack handling.
In tip/x86/asm, entry_64.S only references thread_info in two places, both in the syscall code. I'm hoping that we can move that code into C soon and that we can do the same thing to the 32-bit code. If we do that, then we could just move ti->flags into thread_struct. Everything else should be easy, and then thread_info would be empty.
--Andy
Quoted 13 lines · H. Peter Anvin
Quoted 12 lines · Dave Hansen
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the '
task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation atfork(). The only real downside here is that we have to stick everything and the end of thetask_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
Quoted 6 lines
Quoted 2 lines
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
No. I have also long advocated for merging
task_structandthread_infointo a common structure and get it off the stack; it would improve security and avoid weird corner cases in the irqstack handling.
Note that we have 3 related 'task state' data structures with overlapping purpose:
task_struct
thread_struct
thread_info
where thread_struct is embedded in task_struct currently.
So to turn it all into a single structure we'd have to merge thread_info into thread_struct. thread_info was put on the kernel stack due to the ESP trick we played long ago - but that is moot these days.
So I think what we want is not some common structure, but to actually merge all of thread_info into thread_struct for arch details and into task_struct for generic fields, and only have:
task_struct /* generic fields */
thread_struct /* arch details */
this can be done gradually, field by field, and in the end thread_info can be eliminated altogether.
The only real complication is that it affects every architecture. The good news is that most of the thread_info layout details are wrapped in various constructs like test_ti_thread_flag() and task_thread_info().
While at it we might as well rename 'thread_struct' to 'arch_task_struct':
task_struct /* generic fields */
arch_task_struct /* arch details */
to make it really clear and easy to understand at a glance - as the current naming is has become ambiguous and slightly confusing the moment we introduced threading.
Thanks,
Ingo
Quoted 12 lines · Dave Hansen
The FPU rewrite removed the dynamic allocations of 'struct fpu'. But, this potentially wastes massive amounts of memory (2k per task on systems that do not have AVX-512 for instance).
Instead of having a separate slab, this patch just appends the space that we need to the '
task_struct' which we dynamically allocate already. This saves from doing an extra slab allocation atfork(). The only real downside here is that we have to stick everything and the end of thetask_struct. But, I think the BUILD_BUG_ON()s I stuck in there should keep that from being too fragile.
Quoted 2 lines
This survives a quick build and boot in a VM. Does anyone see any real downsides to this?
So considering the complexity of the other patch that makes the static allocation, I'd massively prefer this patch as it solves the real bug.
It should also work on future hardware a lot better.
This was the dynamic approach I suggested in our discussion of the big FPU code rework.
Quoted 6 lines
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +0
-0@@ -136,6 +136,45 @@ static void __init fpu__init_system_geneunsigned int xstate_size;EXPORT_SYMBOL_GPL (xstate_size);
arch/x86/kernel/fpu/init.c static void __init fpu__init_system_gene · line 148
Quoted 10 lines
arch/x86/kernel/fpu/init.c renamed from arch/x86/kernel/fpu/init.c~dynamically-allocate-struct-fpu +10
-0+#define CHECK_MEMBER_AT_END_OF (TYPE ,MEMBER ) \ + BUILD_BUG_ON((sizeof(TYPE) - \ + offsetof(TYPE, MEMBER) - \ + sizeof(((TYPE *)0)->MEMBER)) > \ + 0) \ + +/* +* We append the 'struct fpu' to the task_struct. +*/ +int __weak arch_task_struct_size (void )
This should not be __weak, otherwise we risk getting the generic version:
kernel/fork.c static void set_max_threads(unsigned int · line 294
Quoted 11 lines
kernel/fork.c renamed from kernel/fork.c~dynamically-allocate-struct-fpu +5
-0@@ -287,15 +287,21 @@ static void set_max_threads(unsigned int max_threads =clamp_t (u64, threads,MIN_THREADS ,MAX_THREADS ); } +int __weak arch_task_struct_size (void ) +{ +return sizeof (struct task_struct ); +} +
Your system probably worked due to link order preferring the x86 version but I'm not sure.
Other than this bug it looks good to me in principle.
Lemme check it on various hardware.
Thanks,
Ingo