Jan Kiszka <[email protected]> writes:
> 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. Correct, the variant was introduced on the Dovetail side but not backported to the I-pipe section. Rationale: enabling kernel events can only apply to the current thread. -- Philippe.
