From: Philippe Gerum <rpm@xenomai.org>
To: Jan Kiszka <jan.kiszka@siemens.com>
Cc: xenomai@xenomai.org
Subject: Re: [PATCH] cobalt/posix/process: pipeline: abstract kernel event handlers
Date: Fri, 08 Jan 2021 17:52:32 +0100 [thread overview]
Message-ID: <87wnwnl6b3.fsf@xenomai.org> (raw)
In-Reply-To: <8a3259f5-507d-0c8f-ae06-7ad5613e6f88@siemens.com>
Jan Kiszka <jan.kiszka@siemens.com> writes:
> On 17.12.20 19:02, Philippe Gerum wrote:
>> From: Philippe Gerum <rpm@xenomai.org>
>>
>> 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 <rpm@xenomai.org>
>> ---
>> .../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 <rpm@xenomai.org>
>> + */
>> +
>> +#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 <rpm@xenomai.org>.
>> + * Copyright (C) 2001-2014 The Xenomai project <http://www.xenomai.org>
>> + * Copyright (C) 2006 Gilles Chanteperdrix <gilles.chanteperdrix@xenomai.org>
>> + *
>> + * 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.
prev parent reply other threads:[~2021-01-08 16:52 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-12-17 18:02 [PATCH] cobalt/posix/process: pipeline: abstract kernel event handlers Philippe Gerum
2021-01-07 17:56 ` Jan Kiszka
2021-01-07 18:27 ` Jan Kiszka
2021-01-08 7:08 ` Jan Kiszka
2021-01-08 9:24 ` Jan Kiszka
2021-01-08 9:49 ` Philippe Gerum
2021-01-08 10:07 ` Jan Kiszka
2021-01-08 10:22 ` Philippe Gerum
2021-01-08 11:00 ` Jan Kiszka
2021-01-08 14:04 ` Philippe Gerum
2021-01-08 16:58 ` Philippe Gerum
2021-01-08 16:53 ` Philippe Gerum
2021-01-08 16:52 ` Philippe Gerum [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87wnwnl6b3.fsf@xenomai.org \
--to=rpm@xenomai.org \
--cc=jan.kiszka@siemens.com \
--cc=xenomai@xenomai.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.