2008-07-17 07:46:05

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 0/6] x86 tracehook patches

Hi! Here is the x86 arch work that goes with the "tracehook" patch
series that I posted for the generic code. I don't know how the
merging will go with that. But when there is a tree containing all
that to base it on, you can merge these arch patches on top of that.
You can't take these without the generic tracehook series, some will
break the build until the generic file is there.

This series also requires as a baseline what you have in tip/x86/step.

The following changes since commit dc2cda6931dd54cd02b05da3a5bc969032e10992:
Roland McGrath (1):
x86 ptrace: user-sets-TF nits

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-utrace.git x86-tracehook

Roland McGrath (6):
x86: tracehook_signal_handler
x86: tracehook syscall
x86: tracehook: asm/syscall.h
x86 signals: use asm/syscall.h
x86: tracehook: TIF_NOTIFY_RESUME
x86: tracehook: CONFIG_HAVE_ARCH_TRACEHOOK

arch/x86/Kconfig | 1 +
arch/x86/kernel/ptrace.c | 34 +------
arch/x86/kernel/signal_32.c | 11 ++-
arch/x86/kernel/signal_64.c | 49 +++-------
include/asm-x86/ptrace.h | 5 +
include/asm-x86/syscall.h | 210 +++++++++++++++++++++++++++++++++++++++++
include/asm-x86/thread_info.h | 4 +-
7 files changed, 248 insertions(+), 66 deletions(-)
create mode 100644 include/asm-x86/syscall.h


Thanks,
Roland


2008-07-17 07:47:50

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 1/6] x86: tracehook_signal_handler

This makes the x86 signal handling code use tracehook_signal_handler() in
place of calling into ptrace guts. The call is moved after the sa_mask
processing, but there is no other change. This cleanup doesn't matter to
existing debuggers, but is the sensible thing: have all facets of the
handler setup complete before the debugger inspects the task again.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/signal_32.c | 6 ++++--
arch/x86/kernel/signal_64.c | 6 ++++--
2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
index 295b5f5..3ffb88f 100644
--- a/arch/x86/kernel/signal_32.c
+++ b/arch/x86/kernel/signal_32.c
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/tracehook.h>
#include <linux/elf.h>
#include <linux/smp.h>
#include <linux/mm.h>
@@ -558,8 +559,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
* handler too.
*/
regs->flags &= ~X86_EFLAGS_TF;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);

spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
@@ -568,6 +567,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);

+ tracehook_signal_handler(sig, info, ka, regs,
+ test_thread_flag(TIF_SINGLESTEP));
+
return 0;
}

diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
index bf87684..5d6c237 100644
--- a/arch/x86/kernel/signal_64.c
+++ b/arch/x86/kernel/signal_64.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
+#include <linux/tracehook.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <linux/personality.h>
@@ -391,8 +392,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
* handler too.
*/
regs->flags &= ~X86_EFLAGS_TF;
- if (test_thread_flag(TIF_SINGLESTEP))
- ptrace_notify(SIGTRAP);

spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
@@ -400,6 +399,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigaddset(&current->blocked,sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
+
+ tracehook_signal_handler(sig, info, ka, regs,
+ test_thread_flag(TIF_SINGLESTEP));
}

return ret;

2008-07-17 07:48:30

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 2/6] x86: tracehook syscall

This changes x86 syscall tracing to use the new tracehook.h entry points.
There is no change, only cleanup.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/ptrace.c | 34 ++++++----------------------------
1 files changed, 6 insertions(+), 28 deletions(-)

diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index e37dccc..19a7d2c 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -14,6 +14,7 @@
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/regset.h>
+#include <linux/tracehook.h>
#include <linux/user.h>
#include <linux/elf.h>
#include <linux/security.h>
@@ -1375,30 +1376,6 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
force_sig_info(SIGTRAP, &info, tsk);
}

-static void syscall_trace(struct pt_regs *regs)
-{
- if (!(current->ptrace & PT_PTRACED))
- return;
-
-#if 0
- printk("trace %s ip %lx sp %lx ax %d origrax %d caller %lx tiflags %x ptrace %x\n",
- current->comm,
- regs->ip, regs->sp, regs->ax, regs->orig_ax, __builtin_return_address(0),
- current_thread_info()->flags, current->ptrace);
-#endif
-
- ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0));
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
-}

#ifdef CONFIG_X86_32
# define IS_IA32 1
@@ -1432,8 +1409,9 @@ asmregparm long syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
ret = -1L;

- if (ret || test_thread_flag(TIF_SYSCALL_TRACE))
- syscall_trace(regs);
+ if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) &&
+ tracehook_report_syscall_entry(regs))
+ ret = -1L;

if (unlikely(current->audit_context)) {
if (IS_IA32)
@@ -1459,7 +1437,7 @@ asmregparm void syscall_trace_leave(struct pt_regs *regs)
audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);

if (test_thread_flag(TIF_SYSCALL_TRACE))
- syscall_trace(regs);
+ tracehook_report_syscall_exit(regs, 0);

/*
* If TIF_SYSCALL_EMU is set, we only get here because of
@@ -1475,6 +1453,6 @@ asmregparm void syscall_trace_leave(struct pt_regs *regs)
* system call instruction.
*/
if (test_thread_flag(TIF_SINGLESTEP) &&
- (current->ptrace & PT_PTRACED))
+ tracehook_consider_fatal_signal(current, SIGTRAP, SIG_DFL))
send_sigtrap(current, regs, 0);
}

2008-07-17 07:49:35

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 3/6] x86: tracehook: asm/syscall.h

Add asm/syscall.h for x86 with all the required entry points.
This will allow arch-independent tracing code for system calls.

Signed-off-by: Roland McGrath <[email protected]>
---
include/asm-x86/ptrace.h | 5 +
include/asm-x86/syscall.h | 210 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 215 insertions(+), 0 deletions(-)

diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h
index 8a71db8..91a77f5 100644
--- a/include/asm-x86/ptrace.h
+++ b/include/asm-x86/ptrace.h
@@ -213,6 +213,11 @@ static inline unsigned long frame_pointer(struct pt_regs *regs)
return regs->bp;
}

+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+ return regs->sp;
+}
+
/*
* These are defined as per linux/ptrace.h, which see.
*/
diff --git a/include/asm-x86/syscall.h b/include/asm-x86/syscall.h
new file mode 100644
index 0000000..7de35f0
--- /dev/null
+++ b/include/asm-x86/syscall.h
@@ -0,0 +1,210 @@
+/*
+ * Access to user system call parameters and results
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * See asm-generic/syscall.h for descriptions of what we must do here.
+ */
+
+#ifndef _ASM_SYSCALL_H
+#define _ASM_SYSCALL_H 1
+
+#include <linux/sched.h>
+
+static inline long syscall_get_nr(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ /*
+ * We always sign-extend a -1 value being set here,
+ * so this is always either -1L or a syscall number.
+ */
+ return regs->orig_ax;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ regs->ax = regs->orig_ax;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ unsigned long error = regs->ax;
+#ifdef CONFIG_IA32_EMULATION
+ /*
+ * TS_COMPAT is set for 32-bit syscall entries and then
+ * remains set until we return to user mode.
+ */
+ if (task_thread_info(task)->status & TS_COMPAT)
+ /*
+ * Sign-extend the value so (int)-EFOO becomes (long)-EFOO
+ * and will match correctly in comparisons.
+ */
+ error = (long) (int) error;
+#endif
+ return error >= -4095L ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->ax;
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs,
+ int error, long val)
+{
+ regs->ax = (long) error ?: val;
+}
+
+#ifdef CONFIG_X86_32
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long args[n])
+{
+ BUG_ON(i + n > 6);
+ memcpy(args, &regs->bx + i, n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ const unsigned long args[n])
+{
+ BUG_ON(i + n > 6);
+ memcpy(&regs->bx + i, args, n * sizeof(args[0]));
+}
+
+#else /* CONFIG_X86_64 */
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+# ifdef CONFIG_IA32_EMULATION
+ if (task_thread_info(task)->status & TS_COMPAT)
+ switch (i + n) {
+ case 6:
+ if (!n--) break;
+ *args++ = regs->bp;
+ case 5:
+ if (!n--) break;
+ *args++ = regs->di;
+ case 4:
+ if (!n--) break;
+ *args++ = regs->si;
+ case 3:
+ if (!n--) break;
+ *args++ = regs->dx;
+ case 2:
+ if (!n--) break;
+ *args++ = regs->cx;
+ case 1:
+ if (!n--) break;
+ *args++ = regs->bx;
+ case 0:
+ if (!n--) break;
+ default:
+ BUG();
+ break;
+ }
+ else
+# endif
+ switch (i + n) {
+ case 6:
+ if (!n--) break;
+ *args++ = regs->r9;
+ case 5:
+ if (!n--) break;
+ *args++ = regs->r8;
+ case 4:
+ if (!n--) break;
+ *args++ = regs->r10;
+ case 3:
+ if (!n--) break;
+ *args++ = regs->dx;
+ case 2:
+ if (!n--) break;
+ *args++ = regs->si;
+ case 1:
+ if (!n--) break;
+ *args++ = regs->di;
+ case 0:
+ if (!n--) break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ const unsigned long *args)
+{
+# ifdef CONFIG_IA32_EMULATION
+ if (task_thread_info(task)->status & TS_COMPAT)
+ switch (i + n) {
+ case 6:
+ if (!n--) break;
+ regs->bp = *args++;
+ case 5:
+ if (!n--) break;
+ regs->di = *args++;
+ case 4:
+ if (!n--) break;
+ regs->si = *args++;
+ case 3:
+ if (!n--) break;
+ regs->dx = *args++;
+ case 2:
+ if (!n--) break;
+ regs->cx = *args++;
+ case 1:
+ if (!n--) break;
+ regs->bx = *args++;
+ case 0:
+ if (!n--) break;
+ default:
+ BUG();
+ }
+ else
+# endif
+ switch (i + n) {
+ case 6:
+ if (!n--) break;
+ regs->r9 = *args++;
+ case 5:
+ if (!n--) break;
+ regs->r8 = *args++;
+ case 4:
+ if (!n--) break;
+ regs->r10 = *args++;
+ case 3:
+ if (!n--) break;
+ regs->dx = *args++;
+ case 2:
+ if (!n--) break;
+ regs->si = *args++;
+ case 1:
+ if (!n--) break;
+ regs->di = *args++;
+ case 0:
+ if (!n--) break;
+ default:
+ BUG();
+ }
+}
+
+#endif /* CONFIG_X86_32 */
+
+#endif /* _ASM_SYSCALL_H */

2008-07-17 07:50:24

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 5/6] x86: tracehook: TIF_NOTIFY_RESUME

This adds TIF_NOTIFY_RESUME support for x86, both 64-bit and 32-bit.
When set, we call tracehook_notify_resume() on the way to user mode.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/signal_32.c | 5 +++++
arch/x86/kernel/signal_64.c | 5 +++++
include/asm-x86/thread_info.h | 4 +++-
3 files changed, 13 insertions(+), 1 deletions(-)

diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
index 3ffb88f..c17c0cb 100644
--- a/arch/x86/kernel/signal_32.c
+++ b/arch/x86/kernel/signal_32.c
@@ -663,6 +663,11 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);

+ if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
+
if (thread_info_flags & _TIF_HRTICK_RESCHED)
hrtick_resched();

diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
index e78b97f..f65ece6 100644
--- a/arch/x86/kernel/signal_64.c
+++ b/arch/x86/kernel/signal_64.c
@@ -471,6 +471,11 @@ void do_notify_resume(struct pt_regs *regs, void *unused,
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);

+ if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
+
if (thread_info_flags & _TIF_HRTICK_RESCHED)
hrtick_resched();
}
diff --git a/include/asm-x86/thread_info.h b/include/asm-x86/thread_info.h
index 0a8f27d..6ed4362 100644
--- a/include/asm-x86/thread_info.h
+++ b/include/asm-x86/thread_info.h
@@ -71,6 +71,7 @@ struct thread_info {
* Warning: layout of LSW is hardcoded in entry.S
*/
#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
+#define TIF_NOTIFY_RESUME 1 /* callback before returning to user */
#define TIF_SIGPENDING 2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/
@@ -94,6 +95,7 @@ struct thread_info {
#define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */

#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
+#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
@@ -135,7 +137,7 @@ struct thread_info {

/* Only used for 64 bit */
#define _TIF_DO_NOTIFY_MASK \
- (_TIF_SIGPENDING|_TIF_MCE_NOTIFY|_TIF_HRTICK_RESCHED)
+ (_TIF_SIGPENDING|_TIF_MCE_NOTIFY|_TIF_HRTICK_RESCHED|_TIF_NOTIFY_RESUME)

/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \

2008-07-17 07:50:40

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 4/6] x86 signals: use asm/syscall.h

Replace local inlines with the asm/syscall.h
interfaces that do the same things.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/kernel/signal_64.c | 38 +++++---------------------------------
1 files changed, 5 insertions(+), 33 deletions(-)

diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
index 5d6c237..e78b97f 100644
--- a/arch/x86/kernel/signal_64.c
+++ b/arch/x86/kernel/signal_64.c
@@ -27,6 +27,7 @@
#include <asm/proto.h>
#include <asm/ia32_unistd.h>
#include <asm/mce.h>
+#include <asm/syscall.h>
#include "sigframe.h"

#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
@@ -294,35 +295,6 @@ give_sigsegv:
}

/*
- * Return -1L or the syscall number that @regs is executing.
- */
-static long current_syscall(struct pt_regs *regs)
-{
- /*
- * We always sign-extend a -1 value being set here,
- * so this is always either -1L or a syscall number.
- */
- return regs->orig_ax;
-}
-
-/*
- * Return a value that is -EFOO if the system call in @regs->orig_ax
- * returned an error. This only works for @regs from @current.
- */
-static long current_syscall_ret(struct pt_regs *regs)
-{
-#ifdef CONFIG_IA32_EMULATION
- if (test_thread_flag(TIF_IA32))
- /*
- * Sign-extend the value so (int)-EFOO becomes (long)-EFOO
- * and will match correctly in comparisons.
- */
- return (int) regs->ax;
-#endif
- return regs->ax;
-}
-
-/*
* OK, we're invoking a handler
*/

@@ -333,9 +305,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
int ret;

/* Are we from a system call? */
- if (current_syscall(regs) >= 0) {
+ if (syscall_get_nr(current, regs) >= 0) {
/* If so, check system call restarting.. */
- switch (current_syscall_ret(regs)) {
+ switch (syscall_get_error(current, regs)) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->ax = -EINTR;
@@ -458,9 +430,9 @@ static void do_signal(struct pt_regs *regs)
}

/* Did we come from a system call? */
- if (current_syscall(regs) >= 0) {
+ if (syscall_get_nr(current, regs) >= 0) {
/* Restart the system call - no handlers present */
- switch (current_syscall_ret(regs)) {
+ switch (syscall_get_error(current, regs)) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:

2008-07-17 07:51:19

by Roland McGrath

[permalink] [raw]
Subject: [PATCH 6/6] x86: tracehook: CONFIG_HAVE_ARCH_TRACEHOOK

The x86 arch code has all the prerequisites, so set HAVE_ARCH_TRACEHOOK.

Signed-off-by: Roland McGrath <[email protected]>
---
arch/x86/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 96e0c2e..dab1735 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -27,6 +27,7 @@ config X86
select HAVE_FTRACE
select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
select HAVE_ARCH_KGDB if !X86_VOYAGER
+ select HAVE_ARCH_TRACEHOOK

config ARCH_DEFCONFIG
string

2008-07-18 08:27:27

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 0/6] x86 tracehook patches


* Roland McGrath <[email protected]> wrote:

> Hi! Here is the x86 arch work that goes with the "tracehook" patch
> series that I posted for the generic code. I don't know how the
> merging will go with that. But when there is a tree containing all
> that to base it on, you can merge these arch patches on top of that.
> You can't take these without the generic tracehook series, some will
> break the build until the generic file is there.
>
> This series also requires as a baseline what you have in tip/x86/step.
>
> The following changes since commit dc2cda6931dd54cd02b05da3a5bc969032e10992:
> Roland McGrath (1):
> x86 ptrace: user-sets-TF nits
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-utrace.git x86-tracehook
>
> Roland McGrath (6):
> x86: tracehook_signal_handler
> x86: tracehook syscall
> x86: tracehook: asm/syscall.h
> x86 signals: use asm/syscall.h
> x86: tracehook: TIF_NOTIFY_RESUME
> x86: tracehook: CONFIG_HAVE_ARCH_TRACEHOOK

since Andrew picked up the generic bits into -mm i cannot put the
generic+x86 bits into -tip.

Andrew, could you please pick up these x86 bits as well?

Reviewed-by: Ingo Molnar <[email protected]>

Roland, a sidenote: your sha1's did not match that of tip/x86/step.

It's not a big issue in this case because Andrew takes patches but best
practice is to not rebase commits you base future pull requests on.
(rebasing is fine until something is WIP and put pushed/pulled - but
it's lethal to transparency and mergability once patches have been
published, so we try to avoid it as much as possible)

The way you could rebase existing commits back ontop of x86/step is to
do this in your tracehook branch:

git-rebase -i --onto tip/x86/step linus/master

(and if you've got fixes for x86/step then please send them as a pull
request that just adds the fix in an append-only manner.)

Ingo