On 17.12.20 19:02, Philippe Gerum wrote:
> From: Philippe Gerum <[email protected]>
>
> Although there are significant commonalities between the I-pipe and
> Dovetail when it comes to dealing with synchronous kernel events,
> there is no strict 1:1 mapping between the two kernel interfaces.
>
> As an initial step, move all the code handling the kernel events to
> the I-pipe section. We may exploit commonalities between the I-pipe
> and Dovetail in this area as we gradually merge support for the
> latter.
>
> No functional change is introduced.
>
> Signed-off-by: Philippe Gerum <[email protected]>
> ---
> .../cobalt/kernel/ipipe/pipeline/kevents.h | 31 +
> kernel/cobalt/ipipe/Makefile | 4 +-
> kernel/cobalt/ipipe/kevents.c | 860 ++++++++++++++++++
> kernel/cobalt/posix/process.c | 846 +----------------
> kernel/cobalt/posix/process.h | 4 +
> kernel/cobalt/posix/signal.c | 5 +
> kernel/cobalt/posix/signal.h | 2 +
> kernel/cobalt/thread.c | 4 +-
> kernel/cobalt/trace/cobalt-core.h | 12 +-
> 9 files changed, 930 insertions(+), 838 deletions(-)
> create mode 100644 include/cobalt/kernel/ipipe/pipeline/kevents.h
> create mode 100644 kernel/cobalt/ipipe/kevents.c
>
> diff --git a/include/cobalt/kernel/ipipe/pipeline/kevents.h
> b/include/cobalt/kernel/ipipe/pipeline/kevents.h
> new file mode 100644
> index 000000000..30425a96b
> --- /dev/null
> +++ b/include/cobalt/kernel/ipipe/pipeline/kevents.h
> @@ -0,0 +1,31 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + * Copyright (C) 2019 Philippe Gerum <[email protected]>
> + */
> +
> +#ifndef _COBALT_KERNEL_IPIPE_KEVENTS_H
> +#define _COBALT_KERNEL_IPIPE_KEVENTS_H
> +
> +struct cobalt_process;
> +struct cobalt_thread;
> +
> +static inline
> +int pipeline_attach_process(struct cobalt_process *process)
> +{
> + return 0;
> +}
> +
> +static inline
> +void pipeline_detach_process(struct cobalt_process *process)
> +{ }
> +
> +int pipeline_prepare_current(void);
> +
> +void pipeline_attach_current(struct xnthread *thread);
> +
> +int pipeline_trap_kevents(void);
> +
> +void pipeline_enable_kevents(void);
> +
> +#endif /* !_COBALT_KERNEL_IPIPE_KEVENTS_H */
> diff --git a/kernel/cobalt/ipipe/Makefile b/kernel/cobalt/ipipe/Makefile
> index 6021008fb..5170bb32b 100644
> --- a/kernel/cobalt/ipipe/Makefile
> +++ b/kernel/cobalt/ipipe/Makefile
> @@ -1,3 +1,5 @@
> +ccflags-y += -Ikernel
> +
> obj-y += pipeline.o
>
> -pipeline-y := init.o intr.o
> +pipeline-y := init.o intr.o kevents.o
> diff --git a/kernel/cobalt/ipipe/kevents.c b/kernel/cobalt/ipipe/kevents.c
> new file mode 100644
> index 000000000..ba584677c
> --- /dev/null
> +++ b/kernel/cobalt/ipipe/kevents.c
> @@ -0,0 +1,860 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0
> + *
> + * Copyright (C) 2001-2014 Philippe Gerum <[email protected]>.
> + * Copyright (C) 2001-2014 The Xenomai project <http://www.xenomai.org>
> + * Copyright (C) 2006 Gilles Chanteperdrix <[email protected]>
> + *
> + * SMP support Copyright (C) 2004 The HYADES project
> <http://www.hyades-itea.org>
> + * RTAI/fusion Copyright (C) 2004 The RTAI project <http://www.rtai.org>
> + */
> +
> +#include <linux/ipipe.h>
> +#include <linux/ipipe_tickdev.h>
> +#include <linux/ptrace.h>
> +#include <pipeline/kevents.h>
> +#include <cobalt/kernel/sched.h>
> +#include <cobalt/kernel/thread.h>
> +#include <cobalt/kernel/vdso.h>
> +#include <rtdm/driver.h>
> +#include <trace/events/cobalt-core.h>
> +#include "../posix/process.h"
> +#include "../posix/thread.h"
> +#include "../posix/memory.h"
> +
> +static void detach_current(void);
> +
> +static inline struct cobalt_process *
> +process_from_thread(struct xnthread *thread)
> +{
> + return container_of(thread, struct cobalt_thread, threadbase)->process;
> +}
> +
> +#ifdef IPIPE_KEVT_PTRESUME
> +
> +static void stop_debugged_process(struct xnthread *thread)
> +{
> + struct cobalt_process *process = process_from_thread(thread);
> + struct cobalt_thread *cth;
> +
> + if (process->debugged_threads > 0)
> + return;
> +
> + list_for_each_entry(cth, &process->thread_list, next) {
> + if (&cth->threadbase == thread)
> + continue;
> +
> + xnthread_suspend(&cth->threadbase, XNDBGSTOP, XN_INFINITE,
> + XN_RELATIVE, NULL);
> + }
> +}
> +
> +static void resume_debugged_process(struct cobalt_process *process)
> +{
> + struct cobalt_thread *cth;
> +
> + xnsched_lock();
> +
> + list_for_each_entry(cth, &process->thread_list, next)
> + if (xnthread_test_state(&cth->threadbase, XNDBGSTOP))
> + xnthread_resume(&cth->threadbase, XNDBGSTOP);
> +
> + xnsched_unlock();
> +}
> +
> +#else /* !IPIPE_KEVT_PTRESUME */
> +
> +static inline void stop_debugged_process(struct xnthread *thread)
> +{
> +}
> +
> +static inline void resume_debugged_process(struct cobalt_process *process)
> +{
> +}
> +
> +#endif /* !IPIPE_KEVT_PTRESUME */
> +
> +/* called with nklock held */
> +static void register_debugged_thread(struct xnthread *thread)
> +{
> + struct cobalt_process *process = process_from_thread(thread);
> +
> + xnthread_set_state(thread, XNSSTEP);
> +
> + stop_debugged_process(thread);
> + process->debugged_threads++;
> +
> + if (xnthread_test_state(thread, XNRELAX))
> + xnthread_suspend(thread, XNDBGSTOP, XN_INFINITE, XN_RELATIVE,
> + NULL);
> +}
> +
> +/* called with nklock held */
> +static void unregister_debugged_thread(struct xnthread *thread)
> +{
> + struct cobalt_process *process = process_from_thread(thread);
> +
> + process->debugged_threads--;
> + xnthread_clear_state(thread, XNSSTEP);
> +
> + if (process->debugged_threads == 0)
> + resume_debugged_process(process);
> +}
> +
> +static inline int handle_exception(struct ipipe_trap_data *d)
> +{
> + struct xnthread *thread;
> + struct xnsched *sched;
> +
> + sched = xnsched_current();
> + thread = sched->curr;
> +
> + trace_cobalt_thread_fault(xnarch_fault_pc(d),
> + xnarch_fault_trap(d));
> +
> + if (xnthread_test_state(thread, XNROOT))
> + return 0;
> +
> +#ifdef IPIPE_KEVT_USERINTRET
> + if (xnarch_fault_bp_p(d) && user_mode(d->regs)) {
> + spl_t s;
> +
> + XENO_WARN_ON(CORE, xnthread_test_state(thread, XNRELAX));
> + xnlock_get_irqsave(&nklock, s);
> + xnthread_set_info(thread, XNCONTHI);
> + ipipe_enable_user_intret_notifier();
> + stop_debugged_process(thread);
> + xnlock_put_irqrestore(&nklock, s);
> + xnsched_run();
> + }
> +#endif
> +
> + if (xnarch_fault_fpu_p(d)) {
> +#ifdef CONFIG_XENO_ARCH_FPU
> + spl_t s;
> +
> + /* FPU exception received in primary mode. */
> + splhigh(s);
> + if (xnarch_handle_fpu_fault(sched->fpuholder, thread, d)) {
> + sched->fpuholder = thread;
> + splexit(s);
> + return 1;
> + }
> + splexit(s);
> +#endif /* CONFIG_XENO_ARCH_FPU */
> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
> + printk("invalid use of FPU in Xenomai context at %pS\n",
> + (void *)xnarch_fault_pc(d));
> +#else
> + print_symbol("invalid use of FPU in Xenomai context at %s\n",
> + xnarch_fault_pc(d));
> +#endif
> + }
> +
> + /*
> + * If we experienced a trap on behalf of a shadow thread
> + * running in primary mode, move it to the Linux domain,
> + * leaving the kernel process the exception.
> + */
> +#if defined(CONFIG_XENO_OPT_DEBUG_COBALT) ||
> defined(CONFIG_XENO_OPT_DEBUG_USER)
> + if (!user_mode(d->regs)) {
> + xntrace_panic_freeze();
> + printk(XENO_WARNING
> + "switching %s to secondary mode after exception #%u in "
> + "kernel-space at 0x%lx (pid %d)\n", thread->name,
> + xnarch_fault_trap(d),
> + xnarch_fault_pc(d),
> + xnthread_host_pid(thread));
> + xntrace_panic_dump();
> + } else if (xnarch_fault_notify(d)) /* Don't report debug traps */
> + printk(XENO_WARNING
> + "switching %s to secondary mode after exception #%u from
> "
> + "user-space at 0x%lx (pid %d)\n", thread->name,
> + xnarch_fault_trap(d),
> + xnarch_fault_pc(d),
> + xnthread_host_pid(thread));
> +#endif
> +
> + if (xnarch_fault_pf_p(d))
> + /*
> + * The page fault counter is not SMP-safe, but it's a
> + * simple indicator that something went wrong wrt
> + * memory locking anyway.
> + */
> + xnstat_counter_inc(&thread->stat.pf);
> +
> + xnthread_relax(xnarch_fault_notify(d), SIGDEBUG_MIGRATE_FAULT);
> +
> + return 0;
> +}
> +
> +static int handle_mayday_event(struct pt_regs *regs)
> +{
> + XENO_BUG_ON(COBALT, !xnthread_test_state(xnthread_current(), XNUSER));
> +
> + xnthread_relax(0, 0);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +int ipipe_trap_hook(struct ipipe_trap_data *data)
> +{
> + if (data->exception == IPIPE_TRAP_MAYDAY)
> + return handle_mayday_event(data->regs);
> +
> + /*
> + * No migration is possible on behalf of the head domain, so
> + * the following access is safe.
> + */
> + raw_cpu_ptr(&cobalt_machine_cpudata)->faults[data->exception]++;
> +
> + if (handle_exception(data))
> + return KEVENT_STOP;
> +
> + /*
> + * CAUTION: access faults must be propagated downstream
> + * whichever domain caused them, so that we don't spuriously
> + * raise a fatal error when some Linux fixup code is available
> + * to recover from the fault.
> + */
> + return KEVENT_PROPAGATE;
> +}
> +
> +/*
> + * Legacy idle hook, unconditionally allow entering the idle state.
> + */
> +bool ipipe_enter_idle_hook(void)
> +{
> + return true;
> +}
> +
> +#ifdef CONFIG_SMP
> +
> +static int handle_setaffinity_event(struct ipipe_cpu_migration_data *d)
> +{
> + struct task_struct *p = d->task;
> + struct xnthread *thread;
> + spl_t s;
> +
> + thread = xnthread_from_task(p);
> + if (thread == NULL)
> + return KEVENT_PROPAGATE;
> +
> + /*
> + * Detect a Cobalt thread sleeping in primary mode which is
> + * required to migrate to another CPU by the host kernel.
> + *
> + * We may NOT fix up thread->sched immediately using the
> + * passive migration call, because that latter always has to
> + * take place on behalf of the target thread itself while
> + * running in secondary mode. Therefore, that thread needs to
> + * go through secondary mode first, then move back to primary
> + * mode, so that affinity_ok() does the fixup work.
> + *
> + * We force this by sending a SIGSHADOW signal to the migrated
> + * thread, asking it to switch back to primary mode from the
> + * handler, at which point the interrupted syscall may be
> + * restarted.
> + */
> + xnlock_get_irqsave(&nklock, s);
> +
> + if (xnthread_test_state(thread, XNTHREAD_BLOCK_BITS & ~XNRELAX))
> + xnthread_signal(thread, SIGSHADOW, SIGSHADOW_ACTION_HARDEN);
> +
> + xnlock_put_irqrestore(&nklock, s);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +static inline bool affinity_ok(struct task_struct *p) /* nklocked, IRQs off
> */
> +{
> + struct xnthread *thread = xnthread_from_task(p);
> + struct xnsched *sched;
> + int cpu = task_cpu(p);
> +
> + /*
> + * To maintain consistency between both Cobalt and host
> + * schedulers, reflecting a thread migration to another CPU
> + * into the Cobalt scheduler state must happen from secondary
> + * mode only, on behalf of the migrated thread itself once it
> + * runs on the target CPU.
> + *
> + * This means that the Cobalt scheduler state regarding the
> + * CPU information lags behind the host scheduler state until
> + * the migrated thread switches back to primary mode
> + * (i.e. task_cpu(p) != xnsched_cpu(xnthread_from_task(p)->sched)).
> + * This is ok since Cobalt does not schedule such thread until then.
> + *
> + * check_affinity() detects when a Cobalt thread switching
> + * back to primary mode did move to another CPU earlier while
> + * in secondary mode. If so, do the fixups to reflect the
> + * change.
> + */
> + if (!xnsched_threading_cpu(cpu)) {
> + /*
> + * The thread is about to switch to primary mode on a
> + * non-rt CPU, which is damn wrong and hopeless.
> + * Whine and cancel that thread.
> + */
> + printk(XENO_WARNING "thread %s[%d] switched to non-rt CPU%d,
> aborted.\n",
> + thread->name, xnthread_host_pid(thread), cpu);
> + /*
> + * Can't call xnthread_cancel() from a migration
> + * point, that would break. Since we are on the wakeup
> + * path to hardening, just raise XNCANCELD to catch it
> + * in xnthread_harden().
> + */
> + xnthread_set_info(thread, XNCANCELD);
> + return false;
> + }
> +
> + sched = xnsched_struct(cpu);
> + if (sched == thread->sched)
> + return true;
> +
> + /*
> + * The current thread moved to a supported real-time CPU,
> + * which is not part of its original affinity mask
> + * though. Assume user wants to extend this mask.
> + */
> + if (!cpumask_test_cpu(cpu, &thread->affinity))
> + cpumask_set_cpu(cpu, &thread->affinity);
> +
> + xnthread_run_handler_stack(thread, move_thread, cpu);
> + xnthread_migrate_passive(thread, sched);
> +
> + return true;
> +}
> +
> +#else /* !CONFIG_SMP */
> +
> +struct ipipe_cpu_migration_data;
> +
> +static int handle_setaffinity_event(struct ipipe_cpu_migration_data *d)
> +{
> + return KEVENT_PROPAGATE;
> +}
> +
> +static inline bool affinity_ok(struct task_struct *p)
> +{
> + return true;
> +}
> +
> +#endif /* CONFIG_SMP */
> +
> +void ipipe_migration_hook(struct task_struct *p) /* hw IRQs off */
> +{
> + struct xnthread *thread = xnthread_from_task(p);
> +
> + xnlock_get(&nklock);
> +
> + /*
> + * We fire the handler before the thread is migrated, so that
> + * thread->sched does not change between paired invocations of
> + * relax_thread/harden_thread handlers.
> + */
> + xnthread_run_handler_stack(thread, harden_thread);
> + if (affinity_ok(p))
> + xnthread_resume(thread, XNRELAX);
> +
> +#ifdef IPIPE_KEVT_USERINTRET
> + /*
> + * In case we migrated independently of the user return notifier, clear
> + * XNCONTHI here and also disable the notifier - we are already done.
> + */
> + if (unlikely(xnthread_test_info(thread, XNCONTHI))) {
> + xnthread_clear_info(thread, XNCONTHI);
> + ipipe_disable_user_intret_notifier();
> + }
> +#endif
> +
> + /* Unregister as debugged thread in case we postponed this. */
> + if (unlikely(xnthread_test_state(thread, XNSSTEP)))
> + unregister_debugged_thread(thread);
> +
> + xnlock_put(&nklock);
> +
> + xnsched_run();
> +}
> +
> +#ifdef CONFIG_XENO_OPT_HOSTRT
> +
> +static IPIPE_DEFINE_SPINLOCK(__hostrtlock);
> +
> +static int handle_hostrt_event(struct ipipe_hostrt_data *hostrt)
> +{
> + unsigned long flags;
> + urwstate_t tmp;
> +
> + /*
> + * The locking strategy is twofold:
> + * - The spinlock protects against concurrent updates from within the
> + * Linux kernel and against preemption by Xenomai
> + * - The unsynced R/W block is for lockless read-only access.
> + */
> + raw_spin_lock_irqsave(&__hostrtlock, flags);
> +
> + unsynced_write_block(&tmp, &nkvdso->hostrt_data.lock) {
> + nkvdso->hostrt_data.live = 1;
> + nkvdso->hostrt_data.cycle_last = hostrt->cycle_last;
> + nkvdso->hostrt_data.mask = hostrt->mask;
> + nkvdso->hostrt_data.mult = hostrt->mult;
> + nkvdso->hostrt_data.shift = hostrt->shift;
> + nkvdso->hostrt_data.wall_sec = hostrt->wall_time_sec;
> + nkvdso->hostrt_data.wall_nsec = hostrt->wall_time_nsec;
> + nkvdso->hostrt_data.wtom_sec = hostrt->wall_to_monotonic.tv_sec;
> + nkvdso->hostrt_data.wtom_nsec =
> hostrt->wall_to_monotonic.tv_nsec;
> + }
> +
> + raw_spin_unlock_irqrestore(&__hostrtlock, flags);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +static inline void init_hostrt(void)
> +{
> + unsynced_rw_init(&nkvdso->hostrt_data.lock);
> + nkvdso->hostrt_data.live = 0;
> +}
> +
> +#else /* !CONFIG_XENO_OPT_HOSTRT */
> +
> +struct ipipe_hostrt_data;
> +
> +static inline int handle_hostrt_event(struct ipipe_hostrt_data *hostrt)
> +{
> + return KEVENT_PROPAGATE;
> +}
> +
> +static inline void init_hostrt(void) { }
> +
> +#endif /* !CONFIG_XENO_OPT_HOSTRT */
> +
> +static void __handle_taskexit_event(struct task_struct *p)
> +{
> + struct cobalt_ppd *sys_ppd;
> + struct xnthread *thread;
> + spl_t s;
> +
> + /*
> + * We are called for both kernel and user shadows over the
> + * root thread.
> + */
> + secondary_mode_only();
> +
> + thread = xnthread_current();
> + XENO_BUG_ON(COBALT, thread == NULL);
> + trace_cobalt_shadow_unmap(thread);
> +
> + xnlock_get_irqsave(&nklock, s);
> +
> + if (xnthread_test_state(thread, XNSSTEP))
> + unregister_debugged_thread(thread);
> +
> + xnsched_run();
> +
> + xnlock_put_irqrestore(&nklock, s);
> +
> + xnthread_run_handler_stack(thread, exit_thread);
> +
> + if (xnthread_test_state(thread, XNUSER)) {
> + cobalt_umm_free(&cobalt_kernel_ppd.umm, thread->u_window);
> + thread->u_window = NULL;
> + sys_ppd = cobalt_ppd_get(0);
> + if (atomic_dec_and_test(&sys_ppd->refcnt))
> + cobalt_remove_process(cobalt_current_process());
> + }
> +}
> +
> +static int handle_taskexit_event(struct task_struct *p) /* p == current */
> +{
> + __handle_taskexit_event(p);
> +
> + /*
> + * __xnthread_cleanup() -> ... -> finalize_thread
> + * handler. From that point, the TCB is dropped. Be careful of
> + * not treading on stale memory within @thread.
> + */
> + __xnthread_cleanup(xnthread_current());
> +
> + detach_current();
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +static int handle_schedule_event(struct task_struct *next_task)
> +{
> + struct task_struct *prev_task;
> + struct xnthread *next;
> + sigset_t pending;
> + spl_t s;
> +
> + cobalt_signal_yield();
> +
> + prev_task = current;
> + next = xnthread_from_task(next_task);
> + if (next == NULL)
> + goto out;
> +
> + xnlock_get_irqsave(&nklock, s);
> +
> + /*
> + * Track tasks leaving the ptraced state. Check both SIGSTOP
> + * (NPTL) and SIGINT (LinuxThreads) to detect ptrace
> + * continuation.
> + */
> + if (xnthread_test_state(next, XNSSTEP)) {
> + if (signal_pending(next_task)) {
> + /*
> + * Do not grab the sighand lock here: it's
> + * useless, and we already own the runqueue
> + * lock, so this would expose us to deadlock
> + * situations on SMP.
> + */
> + sigorsets(&pending,
> + &next_task->pending.signal,
> + &next_task->signal->shared_pending.signal);
> + if (sigismember(&pending, SIGSTOP) ||
> + sigismember(&pending, SIGINT))
> + goto no_ptrace;
> + }
> +
> + /*
> + * Do not unregister before the thread migrated.
> + * unregister_debugged_thread will then be called by our
> + * ipipe_migration_hook.
> + */
> + if (!xnthread_test_info(next, XNCONTHI))
> + unregister_debugged_thread(next);
> +
> + xnthread_set_localinfo(next, XNHICCUP);
> + }
> +
> +no_ptrace:
> + xnlock_put_irqrestore(&nklock, s);
> +
> + /*
> + * Do basic sanity checks on the incoming thread state.
> + * NOTE: we allow ptraced threads to run shortly in order to
> + * properly recover from a stopped state.
> + */
> + if (!XENO_WARN(COBALT, !xnthread_test_state(next, XNRELAX),
> + "hardened thread %s[%d] running in Linux domain?! "
> + "(status=0x%x, sig=%d, prev=%s[%d])",
> + next->name, task_pid_nr(next_task),
> + xnthread_get_state(next),
> + signal_pending(next_task),
> + prev_task->comm, task_pid_nr(prev_task)))
> + XENO_WARN(COBALT,
> + !(next_task->ptrace & PT_PTRACED) &&
> + !xnthread_test_state(next, XNDORMANT)
> + && xnthread_test_state(next, XNPEND),
> + "blocked thread %s[%d] rescheduled?! "
> + "(status=0x%x, sig=%d, prev=%s[%d])",
> + next->name, task_pid_nr(next_task),
> + xnthread_get_state(next),
> + signal_pending(next_task), prev_task->comm,
> + task_pid_nr(prev_task));
> +out:
> + return KEVENT_PROPAGATE;
> +}
> +
> +static int handle_sigwake_event(struct task_struct *p)
> +{
> + struct xnthread *thread;
> + sigset_t pending;
> + spl_t s;
> +
> + thread = xnthread_from_task(p);
> + if (thread == NULL)
> + return KEVENT_PROPAGATE;
> +
> + xnlock_get_irqsave(&nklock, s);
> +
> + /*
> + * CAUTION: __TASK_TRACED is not set in p->state yet. This
> + * state bit will be set right after we return, when the task
> + * is woken up.
> + */
> + if ((p->ptrace & PT_PTRACED) && !xnthread_test_state(thread, XNSSTEP)) {
> + /* We already own the siglock. */
> + sigorsets(&pending,
> + &p->pending.signal,
> + &p->signal->shared_pending.signal);
> +
> + if (sigismember(&pending, SIGTRAP) ||
> + sigismember(&pending, SIGSTOP)
> + || sigismember(&pending, SIGINT))
> + register_debugged_thread(thread);
> + }
> +
> + if (xnthread_test_state(thread, XNRELAX))
> + goto out;
> +
> + /*
> + * If kicking a shadow thread in primary mode, make sure Linux
> + * won't schedule in its mate under our feet as a result of
> + * running signal_wake_up(). The Xenomai scheduler must remain
> + * in control for now, until we explicitly relax the shadow
> + * thread to allow for processing the pending signals. Make
> + * sure we keep the additional state flags unmodified so that
> + * we don't break any undergoing ptrace.
> + */
> + if (p->state & (TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE))
> + cobalt_set_task_state(p, p->state | TASK_NOWAKEUP);
> +
> + /*
> + * Allow a thread stopped for debugging to resume briefly in order to
> + * migrate to secondary mode. xnthread_relax will reapply XNDBGSTOP.
> + */
> + if (xnthread_test_state(thread, XNDBGSTOP))
> + xnthread_resume(thread, XNDBGSTOP);
> +
> + __xnthread_kick(thread);
> +out:
> + xnsched_run();
> +
> + xnlock_put_irqrestore(&nklock, s);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +static int handle_cleanup_event(struct mm_struct *mm)
> +{
> + struct cobalt_process *old, *process;
> + struct cobalt_ppd *sys_ppd;
> + struct xnthread *curr;
> +
> + /*
> + * We are NOT called for exiting kernel shadows.
> + * cobalt_current_process() is cleared if we get there after
> + * handle_task_exit(), so we need to restore this context
> + * pointer temporarily.
> + */
> + process = cobalt_search_process(mm);
> + old = cobalt_set_process(process);
> + sys_ppd = cobalt_ppd_get(0);
> + if (sys_ppd != &cobalt_kernel_ppd) {
> + bool running_exec;
> +
> + /*
> + * Detect a userland shadow running exec(), i.e. still
> + * attached to the current linux task (no prior
> + * detach_current). In this case, we emulate a task
> + * exit, since the Xenomai binding shall not survive
> + * the exec() syscall. Since the process will keep on
> + * running though, we have to disable the event
> + * notifier manually for it.
> + */
> + curr = xnthread_current();
> + running_exec = curr && (current->flags & PF_EXITING) == 0;
> + if (running_exec) {
> + __handle_taskexit_event(current);
> + ipipe_disable_notifier(current);
> + }
> + if (atomic_dec_and_test(&sys_ppd->refcnt))
> + cobalt_remove_process(process);
> + if (running_exec) {
> + __xnthread_cleanup(curr);
> + detach_current();
> + }
> + }
> +
> + /*
> + * CAUTION: Do not override a state change caused by
> + * cobalt_remove_process().
> + */
> + if (cobalt_current_process() == process)
> + cobalt_set_process(old);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +static inline int handle_clockfreq_event(unsigned int *p)
> +{
> + unsigned int newfreq = *p;
> +
> + xnclock_update_freq(newfreq);
> +
> + return KEVENT_PROPAGATE;
> +}
> +
> +#ifdef IPIPE_KEVT_USERINTRET
> +static int handle_user_return(struct task_struct *task)
> +{
> + struct xnthread *thread;
> + spl_t s;
> + int err;
> +
> + ipipe_disable_user_intret_notifier();
> +
> + thread = xnthread_from_task(task);
> + if (thread == NULL)
> + return KEVENT_PROPAGATE;
> +
> + if (xnthread_test_info(thread, XNCONTHI)) {
> + xnlock_get_irqsave(&nklock, s);
> + xnthread_clear_info(thread, XNCONTHI);
> + xnlock_put_irqrestore(&nklock, s);
> +
> + err = xnthread_harden();
> +
> + /*
> + * XNCONTHI may or may not have been re-applied if
> + * harden bailed out due to pending signals. Make sure
> + * it is set in that case.
> + */
> + if (err == -ERESTARTSYS) {
> + xnlock_get_irqsave(&nklock, s);
> + xnthread_set_info(thread, XNCONTHI);
> + xnlock_put_irqrestore(&nklock, s);
> + }
> + }
> +
> + return KEVENT_PROPAGATE;
> +}
> +#endif /* IPIPE_KEVT_USERINTRET */
> +
> +#ifdef IPIPE_KEVT_PTRESUME
> +int handle_ptrace_resume(struct ipipe_ptrace_resume_data *resume)
> +{
> + struct xnthread *thread;
> + spl_t s;
> +
> + thread = xnthread_from_task(resume->task);
> + if (thread == NULL)
> + return KEVENT_PROPAGATE;
> +
> + if (resume->request == PTRACE_SINGLESTEP &&
> + xnthread_test_state(thread, XNSSTEP)) {
> + xnlock_get_irqsave(&nklock, s);
> +
> + xnthread_resume(thread, XNDBGSTOP);
> + unregister_debugged_thread(thread);
> +
> + xnlock_put_irqrestore(&nklock, s);
> + }
> +
> + return KEVENT_PROPAGATE;
> +}
> +#endif /* IPIPE_KEVT_PTRESUME */
> +
> +int ipipe_kevent_hook(int kevent, void *data)
> +{
> + int ret;
> +
> + switch (kevent) {
> + case IPIPE_KEVT_SCHEDULE:
> + ret = handle_schedule_event(data);
> + break;
> + case IPIPE_KEVT_SIGWAKE:
> + ret = handle_sigwake_event(data);
> + break;
> + case IPIPE_KEVT_EXIT:
> + ret = handle_taskexit_event(data);
> + break;
> + case IPIPE_KEVT_CLEANUP:
> + ret = handle_cleanup_event(data);
> + break;
> + case IPIPE_KEVT_HOSTRT:
> + ret = handle_hostrt_event(data);
> + break;
> + case IPIPE_KEVT_SETAFFINITY:
> + ret = handle_setaffinity_event(data);
> + break;
> +#ifdef IPIPE_KEVT_CLOCKFREQ
> + case IPIPE_KEVT_CLOCKFREQ:
> + ret = handle_clockfreq_event(data);
> + break;
> +#endif
> +#ifdef IPIPE_KEVT_USERINTRET
> + case IPIPE_KEVT_USERINTRET:
> + ret = handle_user_return(data);
> + break;
> +#endif
> +#ifdef IPIPE_KEVT_PTRESUME
> + case IPIPE_KEVT_PTRESUME:
> + ret = handle_ptrace_resume(data);
> + break;
> +#endif
> + default:
> + ret = KEVENT_PROPAGATE;
> + }
> +
> + return ret;
> +}
> +
> +#ifdef CONFIG_MMU
> +
> +int pipeline_prepare_current(void)
> +{
> + struct task_struct *p = current;
> + kernel_siginfo_t si;
> +
> + if ((p->mm->def_flags & VM_LOCKED) == 0) {
> + memset(&si, 0, sizeof(si));
> + si.si_signo = SIGDEBUG;
> + si.si_code = SI_QUEUE;
> + si.si_int = SIGDEBUG_NOMLOCK | sigdebug_marker;
> + send_sig_info(SIGDEBUG, &si, p);
> + return 0;
> + }
> +
> + return __ipipe_disable_ondemand_mappings(p);
> +}
> +
> +static inline int get_mayday_prot(void)
> +{
> + return PROT_READ|PROT_EXEC;
> +}
> +
> +#else /* !CONFIG_MMU */
> +
> +int pipeline_prepare_current(void)
> +{
> + return 0;
> +}
> +
> +static inline int get_mayday_prot(void)
> +{
> + /*
> + * Until we stop backing /dev/mem with the mayday page, we
> + * can't ask for PROT_EXEC since the former does not define
> + * mmap capabilities, and default ones won't allow an
> + * executable mapping with MAP_SHARED. In the NOMMU case, this
> + * is (currently) not an issue.
> + */
> + return PROT_READ;
> +}
> +
> +#endif /* !CONFIG_MMU */
> +
> +void pipeline_attach_current(struct xnthread *thread)
> +{
> + struct ipipe_threadinfo *p;
> +
> + p = ipipe_current_threadinfo();
> + p->thread = thread;
> + p->process = cobalt_search_process(current->mm);
> +}
> +
> +static void detach_current(void)
> +{
> + struct ipipe_threadinfo *p = ipipe_current_threadinfo();
> + p->thread = NULL;
> + p->process = NULL;
> +}
> +
> +int pipeline_trap_kevents(void)
> +{
> + init_hostrt();
> + ipipe_set_hooks(ipipe_root_domain, IPIPE_SYSCALL|IPIPE_KEVENT);
> + ipipe_set_hooks(&xnsched_realtime_domain, IPIPE_SYSCALL|IPIPE_TRAP);
> +
> + return 0;
> +}
> +
> +void pipeline_enable_kevents(void)
> +{
> + ipipe_enable_notifier(current);
> +}
> diff --git a/kernel/cobalt/posix/process.c b/kernel/cobalt/posix/process.c
> index 8351d28fb..9bc6082d0 100644
> --- a/kernel/cobalt/posix/process.c
> +++ b/kernel/cobalt/posix/process.c
> @@ -32,12 +32,10 @@
> #include <linux/slab.h>
> #include <linux/cred.h>
> #include <linux/file.h>
> -#include <linux/ptrace.h>
> #include <linux/sched.h>
> #include <linux/signal.h>
> #include <linux/kallsyms.h>
> -#include <linux/ipipe.h>
> -#include <linux/ipipe_tickdev.h>
> +#include <pipeline/kevents.h>
> #include <cobalt/kernel/sched.h>
> #include <cobalt/kernel/heap.h>
> #include <cobalt/kernel/synch.h>
> @@ -94,12 +92,6 @@ struct cobalt_resources cobalt_global_resources = {
> .schedq = LIST_HEAD_INIT(cobalt_global_resources.schedq),
> };
>
> -static inline struct cobalt_process *
> -process_from_thread(struct xnthread *thread)
> -{
> - return container_of(thread, struct cobalt_thread, threadbase)->process;
> -}
> -
> static unsigned __attribute__((pure)) process_hash_crunch(struct mm_struct
> *mm)
> {
> unsigned long hash = ((unsigned long)mm - PAGE_OFFSET) / sizeof(*mm);
> @@ -185,7 +177,7 @@ static void *lookup_context(int xid)
> return priv;
> }
>
> -static void remove_process(struct cobalt_process *process)
> +void cobalt_remove_process(struct cobalt_process *process)
> {
> struct xnthread_personality *personality;
> void *priv;
> @@ -567,67 +559,6 @@ int cobalt_yield(xnticks_t min, xnticks_t max)
> }
> EXPORT_SYMBOL_GPL(cobalt_yield);
>
> -static inline void init_uthread_info(struct xnthread *thread)
> -{
> - struct ipipe_threadinfo *p;
> -
> - p = ipipe_current_threadinfo();
> - p->thread = thread;
> - p->process = cobalt_search_process(current->mm);
> -}
> -
> -static inline void clear_threadinfo(void)
> -{
> - struct ipipe_threadinfo *p = ipipe_current_threadinfo();
> - p->thread = NULL;
> - p->process = NULL;
> -}
> -
> -#ifdef CONFIG_MMU
> -
> -static inline int disable_ondemand_memory(void)
> -{
> - struct task_struct *p = current;
> - kernel_siginfo_t si;
> -
> - if ((p->mm->def_flags & VM_LOCKED) == 0) {
> - memset(&si, 0, sizeof(si));
> - si.si_signo = SIGDEBUG;
> - si.si_code = SI_QUEUE;
> - si.si_int = SIGDEBUG_NOMLOCK | sigdebug_marker;
> - send_sig_info(SIGDEBUG, &si, p);
> - return 0;
> - }
> -
> - return __ipipe_disable_ondemand_mappings(p);
> -}
> -
> -static inline int get_mayday_prot(void)
> -{
> - return PROT_READ|PROT_EXEC;
> -}
> -
> -#else /* !CONFIG_MMU */
> -
> -static inline int disable_ondemand_memory(void)
> -{
> - return 0;
> -}
> -
> -static inline int get_mayday_prot(void)
> -{
> - /*
> - * Until we stop backing /dev/mem with the mayday page, we
> - * can't ask for PROT_EXEC since the former does not define
> - * mmap capabilities, and default ones won't allow an
> - * executable mapping with MAP_SHARED. In the NOMMU case, this
> - * is (currently) not an issue.
> - */
> - return PROT_READ;
> -}
> -
> -#endif /* !CONFIG_MMU */
> -
> /**
> * @fn int cobalt_map_user(struct xnthread *thread, __u32 __user *u_winoff)
> * @internal
> @@ -675,7 +606,7 @@ int cobalt_map_user(struct xnthread *thread, __u32 __user
> *u_winoff)
> if (!access_wok(u_winoff, sizeof(*u_winoff)))
> return -EFAULT;
>
> - ret = disable_ondemand_memory();
> + ret = pipeline_prepare_current();
> if (ret)
> return ret;
>
> @@ -696,7 +627,7 @@ int cobalt_map_user(struct xnthread *thread, __u32 __user
> *u_winoff)
> */
> xnthread_init_shadow_tcb(thread);
> xnthread_suspend(thread, XNRELAX, XN_INFINITE, XN_RELATIVE, NULL);
> - init_uthread_info(thread);
> + pipeline_attach_current(thread);
> xnthread_set_state(thread, XNMAPPED);
> xndebug_shadow_init(thread);
> sys_ppd = cobalt_ppd_get(0);
> @@ -709,7 +640,7 @@ int cobalt_map_user(struct xnthread *thread, __u32 __user
> *u_winoff)
> * it.
> */
> xnthread_run_handler(thread, map_thread);
> - ipipe_enable_notifier(current);
> + pipeline_enable_kevents(current);
This must be called without an argument.
>
> attr.mode = 0;
> attr.entry = NULL;
> @@ -726,456 +657,7 @@ int cobalt_map_user(struct xnthread *thread, __u32
> __user *u_winoff)
> return 0;
> }
>
> -#ifdef IPIPE_KEVT_PTRESUME
> -static void stop_debugged_process(struct xnthread *thread)
> -{
> - struct cobalt_process *process = process_from_thread(thread);
> - struct cobalt_thread *cth;
> -
> - if (process->debugged_threads > 0)
> - return;
> -
> - list_for_each_entry(cth, &process->thread_list, next) {
> - if (&cth->threadbase == thread)
> - continue;
> -
> - xnthread_suspend(&cth->threadbase, XNDBGSTOP, XN_INFINITE,
> - XN_RELATIVE, NULL);
> - }
> -}
> -
> -static void resume_debugged_process(struct cobalt_process *process)
> -{
> - struct cobalt_thread *cth;
> -
> - xnsched_lock();
> -
> - list_for_each_entry(cth, &process->thread_list, next)
> - if (xnthread_test_state(&cth->threadbase, XNDBGSTOP))
> - xnthread_resume(&cth->threadbase, XNDBGSTOP);
> -
> - xnsched_unlock();
> -}
> -
> -#else /* IPIPE_KEVT_PTRESUME unavailable */
> -
> -static inline void stop_debugged_process(struct xnthread *thread)
> -{
> -}
> -
> -static inline void resume_debugged_process(struct cobalt_process *process)
> -{
> -}
> -#endif /* IPIPE_KEVT_PTRESUME unavailable */
> -
> -/* called with nklock held */
> -static void cobalt_register_debugged_thread(struct xnthread *thread)
> -{
> - struct cobalt_process *process = process_from_thread(thread);
> -
> - xnthread_set_state(thread, XNSSTEP);
> -
> - stop_debugged_process(thread);
> - process->debugged_threads++;
> -
> - if (xnthread_test_state(thread, XNRELAX))
> - xnthread_suspend(thread, XNDBGSTOP, XN_INFINITE, XN_RELATIVE,
> - NULL);
> -}
> -
> -/* called with nklock held */
> -static void cobalt_unregister_debugged_thread(struct xnthread *thread)
> -{
> - struct cobalt_process *process = process_from_thread(thread);
> -
> - process->debugged_threads--;
> - xnthread_clear_state(thread, XNSSTEP);
> -
> - if (process->debugged_threads == 0)
> - resume_debugged_process(process);
> -}
> -
> -static inline int handle_exception(struct ipipe_trap_data *d)
> -{
> - struct xnthread *thread;
> - struct xnsched *sched;
> -
> - sched = xnsched_current();
> - thread = sched->curr;
> -
> - trace_cobalt_thread_fault(d);
> -
> - if (xnthread_test_state(thread, XNROOT))
> - return 0;
> -
> -#ifdef IPIPE_KEVT_USERINTRET
> - if (xnarch_fault_bp_p(d) && user_mode(d->regs)) {
> - spl_t s;
> -
> - XENO_WARN_ON(CORE, xnthread_test_state(thread, XNRELAX));
> - xnlock_get_irqsave(&nklock, s);
> - xnthread_set_info(thread, XNCONTHI);
> - ipipe_enable_user_intret_notifier();
> - stop_debugged_process(thread);
> - xnlock_put_irqrestore(&nklock, s);
> - xnsched_run();
> - }
> -#endif
> -
> - if (xnarch_fault_fpu_p(d)) {
> -#ifdef CONFIG_XENO_ARCH_FPU
> - spl_t s;
> -
> - /* FPU exception received in primary mode. */
> - splhigh(s);
> - if (xnarch_handle_fpu_fault(sched->fpuholder, thread, d)) {
> - sched->fpuholder = thread;
> - splexit(s);
> - return 1;
> - }
> - splexit(s);
> -#endif /* CONFIG_XENO_ARCH_FPU */
> -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
> - printk("invalid use of FPU in Xenomai context at %pS\n",
> - (void *)xnarch_fault_pc(d));
> -#else
> - print_symbol("invalid use of FPU in Xenomai context at %s\n",
> - xnarch_fault_pc(d));
> -#endif
> - }
> -
> - /*
> - * If we experienced a trap on behalf of a shadow thread
> - * running in primary mode, move it to the Linux domain,
> - * leaving the kernel process the exception.
> - */
> -#if defined(CONFIG_XENO_OPT_DEBUG_COBALT) ||
> defined(CONFIG_XENO_OPT_DEBUG_USER)
> - if (!user_mode(d->regs)) {
> - xntrace_panic_freeze();
> - printk(XENO_WARNING
> - "switching %s to secondary mode after exception #%u in "
> - "kernel-space at 0x%lx (pid %d)\n", thread->name,
> - xnarch_fault_trap(d),
> - xnarch_fault_pc(d),
> - xnthread_host_pid(thread));
> - xntrace_panic_dump();
> - } else if (xnarch_fault_notify(d)) /* Don't report debug traps */
> - printk(XENO_WARNING
> - "switching %s to secondary mode after exception #%u from
> "
> - "user-space at 0x%lx (pid %d)\n", thread->name,
> - xnarch_fault_trap(d),
> - xnarch_fault_pc(d),
> - xnthread_host_pid(thread));
> -#endif
> -
> - if (xnarch_fault_pf_p(d))
> - /*
> - * The page fault counter is not SMP-safe, but it's a
> - * simple indicator that something went wrong wrt
> - * memory locking anyway.
> - */
> - xnstat_counter_inc(&thread->stat.pf);
> -
> - xnthread_relax(xnarch_fault_notify(d), SIGDEBUG_MIGRATE_FAULT);
> -
> - return 0;
> -}
> -
> -static int handle_mayday_event(struct pt_regs *regs)
> -{
> - XENO_BUG_ON(COBALT, !xnthread_test_state(xnthread_current(), XNUSER));
> -
> - xnthread_relax(0, 0);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -int ipipe_trap_hook(struct ipipe_trap_data *data)
> -{
> - if (data->exception == IPIPE_TRAP_MAYDAY)
> - return handle_mayday_event(data->regs);
> -
> - /*
> - * No migration is possible on behalf of the head domain, so
> - * the following access is safe.
> - */
> - raw_cpu_ptr(&cobalt_machine_cpudata)->faults[data->exception]++;
> -
> - if (handle_exception(data))
> - return KEVENT_STOP;
> -
> - /*
> - * CAUTION: access faults must be propagated downstream
> - * whichever domain caused them, so that we don't spuriously
> - * raise a fatal error when some Linux fixup code is available
> - * to recover from the fault.
> - */
> - return KEVENT_PROPAGATE;
> -}
> -
> -/*
> - * Legacy idle hook, unconditionally allow entering the idle state.
> - */
> -bool ipipe_enter_idle_hook(void)
> -{
> - return true;
> -}
> -
> -#ifdef CONFIG_SMP
> -
> -static int handle_setaffinity_event(struct ipipe_cpu_migration_data *d)
> -{
> - struct task_struct *p = d->task;
> - struct xnthread *thread;
> - spl_t s;
> -
> - thread = xnthread_from_task(p);
> - if (thread == NULL)
> - return KEVENT_PROPAGATE;
> -
> - /*
> - * Detect a Cobalt thread sleeping in primary mode which is
> - * required to migrate to another CPU by the host kernel.
> - *
> - * We may NOT fix up thread->sched immediately using the
> - * passive migration call, because that latter always has to
> - * take place on behalf of the target thread itself while
> - * running in secondary mode. Therefore, that thread needs to
> - * go through secondary mode first, then move back to primary
> - * mode, so that affinity_ok() does the fixup work.
> - *
> - * We force this by sending a SIGSHADOW signal to the migrated
> - * thread, asking it to switch back to primary mode from the
> - * handler, at which point the interrupted syscall may be
> - * restarted.
> - */
> - xnlock_get_irqsave(&nklock, s);
> -
> - if (xnthread_test_state(thread, XNTHREAD_BLOCK_BITS & ~XNRELAX))
> - xnthread_signal(thread, SIGSHADOW, SIGSHADOW_ACTION_HARDEN);
> -
> - xnlock_put_irqrestore(&nklock, s);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline bool affinity_ok(struct task_struct *p) /* nklocked, IRQs off
> */
> -{
> - struct xnthread *thread = xnthread_from_task(p);
> - struct xnsched *sched;
> - int cpu = task_cpu(p);
> -
> - /*
> - * To maintain consistency between both Cobalt and host
> - * schedulers, reflecting a thread migration to another CPU
> - * into the Cobalt scheduler state must happen from secondary
> - * mode only, on behalf of the migrated thread itself once it
> - * runs on the target CPU.
> - *
> - * This means that the Cobalt scheduler state regarding the
> - * CPU information lags behind the host scheduler state until
> - * the migrated thread switches back to primary mode
> - * (i.e. task_cpu(p) != xnsched_cpu(xnthread_from_task(p)->sched)).
> - * This is ok since Cobalt does not schedule such thread until then.
> - *
> - * check_affinity() detects when a Cobalt thread switching
> - * back to primary mode did move to another CPU earlier while
> - * in secondary mode. If so, do the fixups to reflect the
> - * change.
> - */
> - if (!xnsched_threading_cpu(cpu)) {
> - /*
> - * The thread is about to switch to primary mode on a
> - * non-rt CPU, which is damn wrong and hopeless.
> - * Whine and cancel that thread.
> - */
> - printk(XENO_WARNING "thread %s[%d] switched to non-rt CPU%d,
> aborted.\n",
> - thread->name, xnthread_host_pid(thread), cpu);
> - /*
> - * Can't call xnthread_cancel() from a migration
> - * point, that would break. Since we are on the wakeup
> - * path to hardening, just raise XNCANCELD to catch it
> - * in xnthread_harden().
> - */
> - xnthread_set_info(thread, XNCANCELD);
> - return false;
> - }
> -
> - sched = xnsched_struct(cpu);
> - if (sched == thread->sched)
> - return true;
> -
> - /*
> - * The current thread moved to a supported real-time CPU,
> - * which is not part of its original affinity mask
> - * though. Assume user wants to extend this mask.
> - */
> - if (!cpumask_test_cpu(cpu, &thread->affinity))
> - cpumask_set_cpu(cpu, &thread->affinity);
> -
> - xnthread_run_handler_stack(thread, move_thread, cpu);
> - xnthread_migrate_passive(thread, sched);
> -
> - return true;
> -}
> -
> -#else /* !CONFIG_SMP */
> -
> -struct ipipe_cpu_migration_data;
> -
> -static int handle_setaffinity_event(struct ipipe_cpu_migration_data *d)
> -{
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline bool affinity_ok(struct task_struct *p)
> -{
> - return true;
> -}
> -
> -#endif /* CONFIG_SMP */
> -
> -void ipipe_migration_hook(struct task_struct *p) /* hw IRQs off */
> -{
> - struct xnthread *thread = xnthread_from_task(p);
> -
> - xnlock_get(&nklock);
> -
> - /*
> - * We fire the handler before the thread is migrated, so that
> - * thread->sched does not change between paired invocations of
> - * relax_thread/harden_thread handlers.
> - */
> - xnthread_run_handler_stack(thread, harden_thread);
> - if (affinity_ok(p))
> - xnthread_resume(thread, XNRELAX);
> -
> -#ifdef IPIPE_KEVT_USERINTRET
> - /*
> - * In case we migrated independently of the user return notifier, clear
> - * XNCONTHI here and also disable the notifier - we are already done.
> - */
> - if (unlikely(xnthread_test_info(thread, XNCONTHI))) {
> - xnthread_clear_info(thread, XNCONTHI);
> - ipipe_disable_user_intret_notifier();
> - }
> -#endif
> -
> - /* Unregister as debugged thread in case we postponed this. */
> - if (unlikely(xnthread_test_state(thread, XNSSTEP)))
> - cobalt_unregister_debugged_thread(thread);
> -
> - xnlock_put(&nklock);
> -
> - xnsched_run();
> -}
> -
> -#ifdef CONFIG_XENO_OPT_HOSTRT
> -
> -static IPIPE_DEFINE_SPINLOCK(__hostrtlock);
> -
> -static int handle_hostrt_event(struct ipipe_hostrt_data *hostrt)
> -{
> - unsigned long flags;
> - urwstate_t tmp;
> -
> - /*
> - * The locking strategy is twofold:
> - * - The spinlock protects against concurrent updates from within the
> - * Linux kernel and against preemption by Xenomai
> - * - The unsynced R/W block is for lockless read-only access.
> - */
> - raw_spin_lock_irqsave(&__hostrtlock, flags);
> -
> - unsynced_write_block(&tmp, &nkvdso->hostrt_data.lock) {
> - nkvdso->hostrt_data.live = 1;
> - nkvdso->hostrt_data.cycle_last = hostrt->cycle_last;
> - nkvdso->hostrt_data.mask = hostrt->mask;
> - nkvdso->hostrt_data.mult = hostrt->mult;
> - nkvdso->hostrt_data.shift = hostrt->shift;
> - nkvdso->hostrt_data.wall_sec = hostrt->wall_time_sec;
> - nkvdso->hostrt_data.wall_nsec = hostrt->wall_time_nsec;
> - nkvdso->hostrt_data.wtom_sec = hostrt->wall_to_monotonic.tv_sec;
> - nkvdso->hostrt_data.wtom_nsec =
> hostrt->wall_to_monotonic.tv_nsec;
> - }
> -
> - raw_spin_unlock_irqrestore(&__hostrtlock, flags);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline void init_hostrt(void)
> -{
> - unsynced_rw_init(&nkvdso->hostrt_data.lock);
> - nkvdso->hostrt_data.live = 0;
> -}
> -
> -#else /* !CONFIG_XENO_OPT_HOSTRT */
> -
> -struct ipipe_hostrt_data;
> -
> -static inline int handle_hostrt_event(struct ipipe_hostrt_data *hostrt)
> -{
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline void init_hostrt(void) { }
> -
> -#endif /* !CONFIG_XENO_OPT_HOSTRT */
> -
> -static void __handle_taskexit_event(struct task_struct *p)
> -{
> - struct cobalt_ppd *sys_ppd;
> - struct xnthread *thread;
> - spl_t s;
> -
> - /*
> - * We are called for both kernel and user shadows over the
> - * root thread.
> - */
> - secondary_mode_only();
> -
> - thread = xnthread_current();
> - XENO_BUG_ON(COBALT, thread == NULL);
> - trace_cobalt_shadow_unmap(thread);
> -
> - xnlock_get_irqsave(&nklock, s);
> -
> - if (xnthread_test_state(thread, XNSSTEP))
> - cobalt_unregister_debugged_thread(thread);
> -
> - xnsched_run();
> -
> - xnlock_put_irqrestore(&nklock, s);
> -
> - xnthread_run_handler_stack(thread, exit_thread);
> -
> - if (xnthread_test_state(thread, XNUSER)) {
> - cobalt_umm_free(&cobalt_kernel_ppd.umm, thread->u_window);
> - thread->u_window = NULL;
> - sys_ppd = cobalt_ppd_get(0);
> - if (atomic_dec_and_test(&sys_ppd->refcnt))
> - remove_process(cobalt_current_process());
> - }
> -}
> -
> -static int handle_taskexit_event(struct task_struct *p) /* p == current */
> -{
> - __handle_taskexit_event(p);
> -
> - /*
> - * __xnthread_cleanup() -> ... -> finalize_thread
> - * handler. From that point, the TCB is dropped. Be careful of
> - * not treading on stale memory within @thread.
> - */
> - __xnthread_cleanup(xnthread_current());
> -
> - clear_threadinfo();
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline void signal_yield(void)
> +void cobalt_signal_yield(void)
> {
> spl_t s;
>
> @@ -1190,308 +672,6 @@ static inline void signal_yield(void)
> xnlock_put_irqrestore(&nklock, s);
> }
>
> -static int handle_schedule_event(struct task_struct *next_task)
> -{
> - struct task_struct *prev_task;
> - struct xnthread *next;
> - sigset_t pending;
> - spl_t s;
> -
> - signal_yield();
> -
> - prev_task = current;
> - next = xnthread_from_task(next_task);
> - if (next == NULL)
> - goto out;
> -
> - xnlock_get_irqsave(&nklock, s);
> -
> - /*
> - * Track tasks leaving the ptraced state. Check both SIGSTOP
> - * (NPTL) and SIGINT (LinuxThreads) to detect ptrace
> - * continuation.
> - */
> - if (xnthread_test_state(next, XNSSTEP)) {
> - if (signal_pending(next_task)) {
> - /*
> - * Do not grab the sighand lock here: it's
> - * useless, and we already own the runqueue
> - * lock, so this would expose us to deadlock
> - * situations on SMP.
> - */
> - sigorsets(&pending,
> - &next_task->pending.signal,
> - &next_task->signal->shared_pending.signal);
> - if (sigismember(&pending, SIGSTOP) ||
> - sigismember(&pending, SIGINT))
> - goto no_ptrace;
> - }
> -
> - /*
> - * Do not unregister before the thread migrated.
> - * cobalt_unregister_debugged_thread will then be called by our
> - * ipipe_migration_hook.
> - */
> - if (!xnthread_test_info(next, XNCONTHI))
> - cobalt_unregister_debugged_thread(next);
> -
> - xnthread_set_localinfo(next, XNHICCUP);
> - }
> -
> -no_ptrace:
> - xnlock_put_irqrestore(&nklock, s);
> -
> - /*
> - * Do basic sanity checks on the incoming thread state.
> - * NOTE: we allow ptraced threads to run shortly in order to
> - * properly recover from a stopped state.
> - */
> - if (!XENO_WARN(COBALT, !xnthread_test_state(next, XNRELAX),
> - "hardened thread %s[%d] running in Linux domain?! "
> - "(status=0x%x, sig=%d, prev=%s[%d])",
> - next->name, task_pid_nr(next_task),
> - xnthread_get_state(next),
> - signal_pending(next_task),
> - prev_task->comm, task_pid_nr(prev_task)))
> - XENO_WARN(COBALT,
> - !(next_task->ptrace & PT_PTRACED) &&
> - !xnthread_test_state(next, XNDORMANT)
> - && xnthread_test_state(next, XNPEND),
> - "blocked thread %s[%d] rescheduled?! "
> - "(status=0x%x, sig=%d, prev=%s[%d])",
> - next->name, task_pid_nr(next_task),
> - xnthread_get_state(next),
> - signal_pending(next_task), prev_task->comm,
> - task_pid_nr(prev_task));
> -out:
> - return KEVENT_PROPAGATE;
> -}
> -
> -static int handle_sigwake_event(struct task_struct *p)
> -{
> - struct xnthread *thread;
> - sigset_t pending;
> - spl_t s;
> -
> - thread = xnthread_from_task(p);
> - if (thread == NULL)
> - return KEVENT_PROPAGATE;
> -
> - xnlock_get_irqsave(&nklock, s);
> -
> - /*
> - * CAUTION: __TASK_TRACED is not set in p->state yet. This
> - * state bit will be set right after we return, when the task
> - * is woken up.
> - */
> - if ((p->ptrace & PT_PTRACED) && !xnthread_test_state(thread, XNSSTEP)) {
> - /* We already own the siglock. */
> - sigorsets(&pending,
> - &p->pending.signal,
> - &p->signal->shared_pending.signal);
> -
> - if (sigismember(&pending, SIGTRAP) ||
> - sigismember(&pending, SIGSTOP)
> - || sigismember(&pending, SIGINT))
> - cobalt_register_debugged_thread(thread);
> - }
> -
> - if (xnthread_test_state(thread, XNRELAX))
> - goto out;
> -
> - /*
> - * If kicking a shadow thread in primary mode, make sure Linux
> - * won't schedule in its mate under our feet as a result of
> - * running signal_wake_up(). The Xenomai scheduler must remain
> - * in control for now, until we explicitly relax the shadow
> - * thread to allow for processing the pending signals. Make
> - * sure we keep the additional state flags unmodified so that
> - * we don't break any undergoing ptrace.
> - */
> - if (p->state & (TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE))
> - cobalt_set_task_state(p, p->state | TASK_NOWAKEUP);
> -
> - /*
> - * Allow a thread stopped for debugging to resume briefly in order to
> - * migrate to secondary mode. xnthread_relax will reapply XNDBGSTOP.
> - */
> - if (xnthread_test_state(thread, XNDBGSTOP))
> - xnthread_resume(thread, XNDBGSTOP);
> -
> - __xnthread_kick(thread);
> -out:
> - xnsched_run();
> -
> - xnlock_put_irqrestore(&nklock, s);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -static int handle_cleanup_event(struct mm_struct *mm)
> -{
> - struct cobalt_process *old, *process;
> - struct cobalt_ppd *sys_ppd;
> - struct xnthread *curr;
> -
> - /*
> - * We are NOT called for exiting kernel shadows.
> - * cobalt_current_process() is cleared if we get there after
> - * handle_task_exit(), so we need to restore this context
> - * pointer temporarily.
> - */
> - process = cobalt_search_process(mm);
> - old = cobalt_set_process(process);
> - sys_ppd = cobalt_ppd_get(0);
> - if (sys_ppd != &cobalt_kernel_ppd) {
> - bool running_exec;
> -
> - /*
> - * Detect a userland shadow running exec(), i.e. still
> - * attached to the current linux task (no prior
> - * clear_threadinfo). In this case, we emulate a task
> - * exit, since the Xenomai binding shall not survive
> - * the exec() syscall. Since the process will keep on
> - * running though, we have to disable the event
> - * notifier manually for it.
> - */
> - curr = xnthread_current();
> - running_exec = curr && (current->flags & PF_EXITING) == 0;
> - if (running_exec) {
> - __handle_taskexit_event(current);
> - ipipe_disable_notifier(current);
> - }
> - if (atomic_dec_and_test(&sys_ppd->refcnt))
> - remove_process(process);
> - if (running_exec) {
> - __xnthread_cleanup(curr);
> - clear_threadinfo();
> - }
> - }
> -
> - /*
> - * CAUTION: Do not override a state change caused by
> - * remove_process().
> - */
> - if (cobalt_current_process() == process)
> - cobalt_set_process(old);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -static inline int handle_clockfreq_event(unsigned int *p)
> -{
> - unsigned int newfreq = *p;
> -
> - xnclock_update_freq(newfreq);
> -
> - return KEVENT_PROPAGATE;
> -}
> -
> -#ifdef IPIPE_KEVT_USERINTRET
> -static int handle_user_return(struct task_struct *task)
> -{
> - struct xnthread *thread;
> - spl_t s;
> - int err;
> -
> - ipipe_disable_user_intret_notifier();
> -
> - thread = xnthread_from_task(task);
> - if (thread == NULL)
> - return KEVENT_PROPAGATE;
> -
> - if (xnthread_test_info(thread, XNCONTHI)) {
> - xnlock_get_irqsave(&nklock, s);
> - xnthread_clear_info(thread, XNCONTHI);
> - xnlock_put_irqrestore(&nklock, s);
> -
> - err = xnthread_harden();
> -
> - /*
> - * XNCONTHI may or may not have been re-applied if
> - * harden bailed out due to pending signals. Make sure
> - * it is set in that case.
> - */
> - if (err == -ERESTARTSYS) {
> - xnlock_get_irqsave(&nklock, s);
> - xnthread_set_info(thread, XNCONTHI);
> - xnlock_put_irqrestore(&nklock, s);
> - }
> - }
> -
> - return KEVENT_PROPAGATE;
> -}
> -#endif /* IPIPE_KEVT_USERINTRET */
> -
> -#ifdef IPIPE_KEVT_PTRESUME
> -int handle_ptrace_resume(struct ipipe_ptrace_resume_data *resume)
> -{
> - struct xnthread *thread;
> - spl_t s;
> -
> - thread = xnthread_from_task(resume->task);
> - if (thread == NULL)
> - return KEVENT_PROPAGATE;
> -
> - if (resume->request == PTRACE_SINGLESTEP &&
> - xnthread_test_state(thread, XNSSTEP)) {
> - xnlock_get_irqsave(&nklock, s);
> -
> - xnthread_resume(thread, XNDBGSTOP);
> - cobalt_unregister_debugged_thread(thread);
> -
> - xnlock_put_irqrestore(&nklock, s);
> - }
> -
> - return KEVENT_PROPAGATE;
> -}
> -#endif /* IPIPE_KEVT_PTRESUME */
> -
> -int ipipe_kevent_hook(int kevent, void *data)
> -{
> - int ret;
> -
> - switch (kevent) {
> - case IPIPE_KEVT_SCHEDULE:
> - ret = handle_schedule_event(data);
> - break;
> - case IPIPE_KEVT_SIGWAKE:
> - ret = handle_sigwake_event(data);
> - break;
> - case IPIPE_KEVT_EXIT:
> - ret = handle_taskexit_event(data);
> - break;
> - case IPIPE_KEVT_CLEANUP:
> - ret = handle_cleanup_event(data);
> - break;
> - case IPIPE_KEVT_HOSTRT:
> - ret = handle_hostrt_event(data);
> - break;
> - case IPIPE_KEVT_SETAFFINITY:
> - ret = handle_setaffinity_event(data);
> - break;
> -#ifdef IPIPE_KEVT_CLOCKFREQ
> - case IPIPE_KEVT_CLOCKFREQ:
> - ret = handle_clockfreq_event(data);
> - break;
> -#endif
> -#ifdef IPIPE_KEVT_USERINTRET
> - case IPIPE_KEVT_USERINTRET:
> - ret = handle_user_return(data);
> - break;
> -#endif
> -#ifdef IPIPE_KEVT_PTRESUME
> - case IPIPE_KEVT_PTRESUME:
> - ret = handle_ptrace_resume(data);
> - break;
> -#endif
> - default:
> - ret = KEVENT_PROPAGATE;
> - }
> -
> - return ret;
> -}
> -
> static int attach_process(struct cobalt_process *process)
> {
> struct cobalt_ppd *p = &process->sys_ppd;
> @@ -1505,6 +685,10 @@ static int attach_process(struct cobalt_process
> *process)
>
> cobalt_umm_set_name(&p->umm, "private heap[%d]", task_pid_nr(current));
>
> + ret = pipeline_attach_process(process);
> + if (ret)
> + goto fail_pipeline;
> +
> exe_path = get_exe_path(current);
> if (IS_ERR(exe_path)) {
> printk(XENO_WARNING
> @@ -1522,8 +706,10 @@ static int attach_process(struct cobalt_process
> *process)
>
> return 0;
> fail_hash:
> + pipeline_detach_process(process);
> if (p->exe_path)
> kfree(p->exe_path);
> +fail_pipeline:
> cobalt_umm_destroy(&p->umm);
>
> return ret;
> @@ -1688,14 +874,16 @@ __init int cobalt_init(void)
> if (ret)
> goto fail_siginit;
>
> - init_hostrt();
> - ipipe_set_hooks(ipipe_root_domain, IPIPE_SYSCALL|IPIPE_KEVENT);
> - ipipe_set_hooks(&xnsched_realtime_domain, IPIPE_SYSCALL|IPIPE_TRAP);
> + ret = pipeline_trap_kevents();
> + if (ret)
> + goto fail_kevents;
>
> if (gid_arg != -1)
> printk(XENO_INFO "allowing access to group %d\n", gid_arg);
>
> return 0;
> +fail_kevents:
> + cobalt_signal_cleanup();
> fail_siginit:
> cobalt_unregister_personality(0);
> fail_register:
> diff --git a/kernel/cobalt/posix/process.h b/kernel/cobalt/posix/process.h
> index 375b4cbbb..3a38ae639 100644
> --- a/kernel/cobalt/posix/process.h
> +++ b/kernel/cobalt/posix/process.h
> @@ -149,6 +149,10 @@ void cobalt_del_resource(struct cobalt_resnode *node)
> list_del(&node->next);
> }
>
> +void cobalt_remove_process(struct cobalt_process *process);
> +
> +void cobalt_signal_yield(void);
> +
> extern struct xnthread_personality *cobalt_personalities[];
>
> extern struct xnthread_personality cobalt_personality;
> diff --git a/kernel/cobalt/posix/signal.c b/kernel/cobalt/posix/signal.c
> index 75514d84f..862a644c0 100644
> --- a/kernel/cobalt/posix/signal.c
> +++ b/kernel/cobalt/posix/signal.c
> @@ -614,3 +614,8 @@ __init int cobalt_signal_init(void)
>
> return 0;
> }
> +
> +__init void cobalt_signal_cleanup(void)
> +{
> + xnheap_vfree(sigpending_mem);
> +}
> diff --git a/kernel/cobalt/posix/signal.h b/kernel/cobalt/posix/signal.h
> index e506842e7..7a0b4b22b 100644
> --- a/kernel/cobalt/posix/signal.h
> +++ b/kernel/cobalt/posix/signal.h
> @@ -110,4 +110,6 @@ COBALT_SYSCALL_DECL(sigqueue,
>
> int cobalt_signal_init(void);
>
> +void cobalt_signal_cleanup(void);
> +
> #endif /* !_COBALT_POSIX_SIGNAL_H */
> diff --git a/kernel/cobalt/thread.c b/kernel/cobalt/thread.c
> index 49f749f1c..a882bcc45 100644
> --- a/kernel/cobalt/thread.c
> +++ b/kernel/cobalt/thread.c
> @@ -38,6 +38,7 @@
> #include <cobalt/kernel/select.h>
> #include <cobalt/kernel/lock.h>
> #include <cobalt/kernel/thread.h>
> +#include <pipeline/kevents.h>
> #include <trace/events/cobalt-core.h>
> #include "debug.h"
>
> @@ -2502,7 +2503,6 @@ static inline void init_kthread_info(struct xnthread
> *thread)
> */
> int xnthread_map(struct xnthread *thread, struct completion *done)
> {
> - struct task_struct *p = current;
> int ret;
> spl_t s;
>
> @@ -2521,7 +2521,7 @@ int xnthread_map(struct xnthread *thread, struct
> completion *done)
> xnthread_set_state(thread, XNMAPPED);
> xndebug_shadow_init(thread);
> xnthread_run_handler(thread, map_thread);
> - ipipe_enable_notifier(p);
> + pipeline_enable_kevents();
>
> /*
> * CAUTION: Soon after xnthread_init() has returned,
> diff --git a/kernel/cobalt/trace/cobalt-core.h
> b/kernel/cobalt/trace/cobalt-core.h
> index a9e14815b..96eec3664 100644
> --- a/kernel/cobalt/trace/cobalt-core.h
> +++ b/kernel/cobalt/trace/cobalt-core.h
> @@ -377,20 +377,20 @@ TRACE_EVENT(cobalt_thread_resume,
> );
>
> TRACE_EVENT(cobalt_thread_fault,
> - TP_PROTO(struct ipipe_trap_data *td),
> - TP_ARGS(td),
> + TP_PROTO(unsigned long ip, unsigned int type),
> + TP_ARGS(ip, type),
>
> TP_STRUCT__entry(
> - __field(void *, ip)
> + __field(unsigned long, ip)
> __field(unsigned int, type)
> ),
>
> TP_fast_assign(
> - __entry->ip = (void *)xnarch_fault_pc(td);
> - __entry->type = xnarch_fault_trap(td);
> + __entry->ip = ip;
> + __entry->type = type;
> ),
>
> - TP_printk("ip=%p type=%x",
> + TP_printk("ip=%#lx type=%#x",
> __entry->ip, __entry->type)
> );
>
>
Applied with the issue above fixed up.
Jan
--
Siemens AG, T RDA IOT
Corporate Competence Center Embedded Linux