struct fxregs_state             fxsave;
        struct swregs_state             soft;
        struct xregs_state              xsave;
+       u8 __padding[PAGE_SIZE];
 };
 
 /*
  * state fields:
  */
 struct fpu {
-       /*
-        * @state:
-        *
-        * In-memory copy of all FPU registers that we save/restore
-        * over context switches. If the task is using the FPU then
-        * the registers in the FPU are more recent than this state
-        * copy. If the task context-switches away then they get
-        * saved here and represent the FPU state.
-        *
-        * After context switches there may be a (short) time period
-        * during which the in-FPU hardware registers are unchanged
-        * and still perfectly match this state, if the tasks
-        * scheduled afterwards are not using the FPU.
-        *
-        * This is the 'lazy restore' window of optimization, which
-        * we track though 'fpu_fpregs_owner_ctx' and 'fpu->last_cpu'.
-        *
-        * We detect whether a subsequent task uses the FPU via setting
-        * CR0::TS to 1, which causes any FPU use to raise a #NM fault.
-        *
-        * During this window, if the task gets scheduled again, we
-        * might be able to skip having to do a restore from this
-        * memory buffer to the hardware registers - at the cost of
-        * incurring the overhead of #NM fault traps.
-        *
-        * Note that on modern CPUs that support the XSAVEOPT (or other
-        * optimized XSAVE instructions), we don't use #NM traps anymore,
-        * as the hardware can track whether FPU registers need saving
-        * or not. On such CPUs we activate the non-lazy ('eagerfpu')
-        * logic, which unconditionally saves/restores all FPU state
-        * across context switches. (if FPU state exists.)
-        */
-       union fpregs_state              state;
-
        /*
         * @last_cpu:
         *
         * deal with bursty apps that only use the FPU for a short time:
         */
        unsigned char                   counter;
+       /*
+        * @state:
+        *
+        * In-memory copy of all FPU registers that we save/restore
+        * over context switches. If the task is using the FPU then
+        * the registers in the FPU are more recent than this state
+        * copy. If the task context-switches away then they get
+        * saved here and represent the FPU state.
+        *
+        * After context switches there may be a (short) time period
+        * during which the in-FPU hardware registers are unchanged
+        * and still perfectly match this state, if the tasks
+        * scheduled afterwards are not using the FPU.
+        *
+        * This is the 'lazy restore' window of optimization, which
+        * we track though 'fpu_fpregs_owner_ctx' and 'fpu->last_cpu'.
+        *
+        * We detect whether a subsequent task uses the FPU via setting
+        * CR0::TS to 1, which causes any FPU use to raise a #NM fault.
+        *
+        * During this window, if the task gets scheduled again, we
+        * might be able to skip having to do a restore from this
+        * memory buffer to the hardware registers - at the cost of
+        * incurring the overhead of #NM fault traps.
+        *
+        * Note that on modern CPUs that support the XSAVEOPT (or other
+        * optimized XSAVE instructions), we don't use #NM traps anymore,
+        * as the hardware can track whether FPU registers need saving
+        * or not. On such CPUs we activate the non-lazy ('eagerfpu')
+        * logic, which unconditionally saves/restores all FPU state
+        * across context switches. (if FPU state exists.)
+        */
+       union fpregs_state              state;
+       /*
+        * WARNING: 'state' is dynamically-sized.  Do not put
+        * anything after it here.
+        */
 };
 
 #endif /* _ASM_X86_FPU_H */
 
 #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... */
        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.
+        */
 };
 
 /*
 
 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);
+       CHECK_MEMBER_AT_END_OF(struct task_struct, thread);
+
+       return task_size;
+}
+
 /*
  * Set up the xstate_size based on the legacy FPU context size.
  *
 
  */
 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);
 }
 
                             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;
 }
        /* 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]);
 
 /* 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 */
        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)
 
 
        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