Upstream 4.14 switched to purely eager fpu switching. That was
backported to 4.4 and 4.9. This commit makes cobalt able to deal whith
the changed kernel behaviour.
This commit takes care of 4.9 to begin with.

Introduce IPIPE_X86_FPU_EAGER to switch between the new and the old
implementations. The new implementation is much simpler than the old
one. We basically only deal with the odd case where Xenomai preempts
Linux in a kernel-fpu context.
In a regular Linux that can never happen and if it happens we need to
make sure to get things into a consistent state. We have to make
"current" as not owning the fpu anymore and allow others to use
in-kernel fpu (kernel_fpu_enable). __switch_to from Linux will do the
rest.

Signed-off-by: Henning Schild <henning.sch...@siemens.com>
---
 .../arch/x86/include/asm/xenomai/thread.h     |  8 ++-
 .../arch/x86/include/asm/xenomai/wrappers.h   |  5 ++
 kernel/cobalt/arch/x86/thread.c               | 69 ++++++++++++++++++-
 3 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/kernel/cobalt/arch/x86/include/asm/xenomai/thread.h 
b/kernel/cobalt/arch/x86/include/asm/xenomai/thread.h
index f174a82c0..0c5c4da9c 100644
--- a/kernel/cobalt/arch/x86/include/asm/xenomai/thread.h
+++ b/kernel/cobalt/arch/x86/include/asm/xenomai/thread.h
@@ -24,6 +24,7 @@
 #include <asm/xenomai/wrappers.h>
 #include <asm/traps.h>
 
+#ifndef IPIPE_X86_FPU_EAGER
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,4,0)
 typedef union thread_xstate x86_fpustate;
 #define x86_fpustate_ptr(t) ((t)->fpu.state)
@@ -31,6 +32,7 @@ typedef union thread_xstate x86_fpustate;
 typedef union fpregs_state x86_fpustate;
 #define x86_fpustate_ptr(t) ((t)->fpu.active_state)
 #endif
+#endif
 
 struct xnarchtcb {
        struct xntcb core;
@@ -40,10 +42,14 @@ struct xnarchtcb {
        unsigned long ip;
        unsigned long *ipp;
 #endif  
+#ifdef IPIPE_X86_FPU_EAGER
+       struct fpu *kfpu;
+#else
        x86_fpustate *fpup;
-       unsigned int root_kfpu: 1;
        unsigned int root_used_math: 1;
        x86_fpustate *kfpu_state;
+#endif
+       unsigned int root_kfpu: 1;
        struct {
                unsigned long ip;
                unsigned long ax;
diff --git a/kernel/cobalt/arch/x86/include/asm/xenomai/wrappers.h 
b/kernel/cobalt/arch/x86/include/asm/xenomai/wrappers.h
index 5f9cff3c9..00f0aaae5 100644
--- a/kernel/cobalt/arch/x86/include/asm/xenomai/wrappers.h
+++ b/kernel/cobalt/arch/x86/include/asm/xenomai/wrappers.h
@@ -24,6 +24,11 @@
 #define __get_user_inatomic __get_user
 #define __put_user_inatomic __put_user
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4,9,108) && \
+    LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+#define IPIPE_X86_FPU_EAGER
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
 #include <asm/i387.h>
 #include <asm/fpu-internal.h>
diff --git a/kernel/cobalt/arch/x86/thread.c b/kernel/cobalt/arch/x86/thread.c
index 7fb136300..18cf636e5 100644
--- a/kernel/cobalt/arch/x86/thread.c
+++ b/kernel/cobalt/arch/x86/thread.c
@@ -28,9 +28,13 @@
 
 static struct kmem_cache *xstate_cache;
 
+#ifdef IPIPE_X86_FPU_EAGER
+#define fpu_kernel_xstate_size sizeof(struct fpu)
+#else
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
 #define fpu_kernel_xstate_size xstate_size
 #endif
+#endif /* IPIPE_X86_FPU_EAGER */
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
 #define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM)
@@ -199,14 +203,17 @@ void xnarch_switch_to(struct xnthread *out, struct 
xnthread *in)
        struct mm_struct *prev_mm, *next_mm;
 
        prev = out_tcb->core.host_task;
+#ifndef IPIPE_X86_FPU_EAGER
        if (x86_fpregs_active(prev))
                /*
                 * __switch_to will try and use __unlazy_fpu, so we
                 * need to clear the ts bit.
                 */
                clts();
+#endif /* ! IPIPE_X86_FPU_EAGER */
 
        next = in_tcb->core.host_task;
+#ifndef IPIPE_X86_FPU_EAGER
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
        next->thread.fpu.counter = 0;
 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
@@ -214,6 +221,7 @@ void xnarch_switch_to(struct xnthread *out, struct xnthread 
*in)
 #else
        next->fpu_counter = 0;
 #endif
+#endif /* ! IPIPE_X86_FPU_EAGER */
        prev_mm = out_tcb->core.active_mm;
        next_mm = in_tcb->core.mm;
        if (next_mm == NULL) {
@@ -245,9 +253,13 @@ void xnarch_switch_to(struct xnthread *out, struct 
xnthread *in)
        switch_to(prev, next, last);
 #endif /* LINUX_VERSION_CODE >= 4.8 */
 
+#ifndef IPIPE_X86_FPU_EAGER
        stts();
+#endif /* ! IPIPE_X86_FPU_EAGER */
 }
 
+#ifndef IPIPE_X86_FPU_EAGER
+
 #ifdef CONFIG_X86_64
 #define XSAVE_PREFIX   "0x48,"
 #define XSAVE_SUFFIX   "q"
@@ -359,11 +371,21 @@ int xnarch_handle_fpu_fault(struct xnthread *from,
 
        return 1;
 }
+#else /* IPIPE_X86_FPU_EAGER */
+
+int xnarch_handle_fpu_fault(struct xnthread *from,
+                       struct xnthread *to, struct ipipe_trap_data *d)
+{
+       // in eager mode there are no such faults
+       BUG_ON(1);
+}
+#endif /* ! IPIPE_X86_FPU_EAGER */
 
 #define current_task_used_kfpu() kernel_fpu_disabled()
 
 #define tcb_used_kfpu(t) ((t)->root_kfpu)
 
+#ifndef IPIPE_X86_FPU_EAGER
 void xnarch_leave_root(struct xnthread *root)
 {
        struct xnarchtcb *const rootcb = xnthread_archtcb(root);
@@ -430,6 +452,35 @@ void xnarch_switch_fpu(struct xnthread *from, struct 
xnthread *to)
                p->flags &= ~PF_USED_MATH;
        }
 }
+#else /* IPIPE_X86_FPU_EAGER */
+void xnarch_leave_root(struct xnthread *root)
+{
+       struct xnarchtcb *const rootcb = xnthread_archtcb(root);
+
+       rootcb->root_kfpu = current_task_used_kfpu();
+
+       if (!tcb_used_kfpu(rootcb))
+               return;
+
+       // save fpregs from in-kernel use
+       copy_fpregs_to_fpstate(rootcb->kfpu);
+       kernel_fpu_enable();
+       // mark current thread as not owning the FPU anymore
+       if (&current->thread.fpu.fpstate_active)
+               fpregs_deactivate(&current->thread.fpu);
+}
+
+void xnarch_switch_fpu(struct xnthread *from, struct xnthread *to)
+{
+       struct xnarchtcb *const to_tcb = xnthread_archtcb(to);
+
+       if (!tcb_used_kfpu(to_tcb))
+               return;
+
+       copy_kernel_to_fpregs(&to_tcb->kfpu->state);
+       kernel_fpu_disable();
+}
+#endif /* ! IPIPE_X86_FPU_EAGER */
 
 void xnarch_init_root_tcb(struct xnthread *thread)
 {
@@ -440,9 +491,13 @@ void xnarch_init_root_tcb(struct xnthread *thread)
        tcb->spp = &tcb->sp;
        tcb->ipp = &tcb->ip;
 #endif 
+#ifndef IPIPE_X86_FPU_EAGER
        tcb->fpup = NULL;
-       tcb->root_kfpu = 0;
        tcb->kfpu_state = kmem_cache_zalloc(xstate_cache, GFP_KERNEL);
+#else /* IPIPE_X86_FPU_EAGER */
+       tcb->kfpu = kmem_cache_zalloc(xstate_cache, GFP_KERNEL);
+#endif /* ! IPIPE_X86_FPU_EAGER */
+       tcb->root_kfpu = 0;
 }
 
 void xnarch_init_shadow_tcb(struct xnthread *thread)
@@ -459,12 +514,22 @@ void xnarch_init_shadow_tcb(struct xnthread *thread)
        tcb->ipp = &p->thread.rip; /* <!> raw naming intended. */
 #endif
 #endif
+#ifndef IPIPE_X86_FPU_EAGER
        tcb->fpup = x86_fpustate_ptr(&p->thread);
-       tcb->root_kfpu = 0;
        tcb->kfpu_state = NULL;
+#else /* IPIPE_X86_FPU_EAGER */
+       tcb->kfpu = NULL;
+#endif /* ! IPIPE_X86_FPU_EAGER */
+       tcb->root_kfpu = 0;
 
+#ifndef IPIPE_X86_FPU_EAGER
        /* XNFPU is set upon first FPU fault */
        xnthread_clear_state(thread, XNFPU);
+#else /* IPIPE_X86_FPU_EAGER */
+       /* XNFPU is always set */
+       xnthread_set_state(thread, XNFPU);
+       fpu__activate_fpstate_read(&p->thread.fpu);
+#endif /* ! IPIPE_X86_FPU_EAGER */
 }
 
 int mach_x86_thread_init(void)
-- 
2.19.0


_______________________________________________
Xenomai mailing list
Xenomai@xenomai.org
https://xenomai.org/mailman/listinfo/xenomai

Reply via email to