Commit bf53de90 authored by Markus Metzger's avatar Markus Metzger Committed by Ingo Molnar

x86, bts: add fork and exit handling

Impact: introduce new ptrace facility

Add arch_ptrace_untrace() function that is called when the tracer
detaches (either voluntarily or when the tracing task dies);
ptrace_disable() is only called on a voluntary detach.

Add ptrace_fork() and arch_ptrace_fork(). They are called when a
traced task is forked.

Clear DS and BTS related fields on fork.

Release DS resources and reclaim memory in ptrace_untrace(). This
releases resources already when the tracing task dies. We used to do
that when the traced task dies.
Signed-off-by: default avatarMarkus Metzger <markus.t.metzger@intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 30cd324e
...@@ -252,12 +252,21 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *); ...@@ -252,12 +252,21 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
*/ */
extern void ds_switch_to(struct task_struct *prev, struct task_struct *next); extern void ds_switch_to(struct task_struct *prev, struct task_struct *next);
/*
* Task clone/init and cleanup work
*/
extern void ds_copy_thread(struct task_struct *tsk, struct task_struct *father);
extern void ds_exit_thread(struct task_struct *tsk);
#else /* CONFIG_X86_DS */ #else /* CONFIG_X86_DS */
struct cpuinfo_x86; struct cpuinfo_x86;
static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {} static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {}
static inline void ds_switch_to(struct task_struct *prev, static inline void ds_switch_to(struct task_struct *prev,
struct task_struct *next) {} struct task_struct *next) {}
static inline void ds_copy_thread(struct task_struct *tsk,
struct task_struct *father) {}
static inline void ds_exit_thread(struct task_struct *tsk) {}
#endif /* CONFIG_X86_DS */ #endif /* CONFIG_X86_DS */
#endif /* _ASM_X86_DS_H */ #endif /* _ASM_X86_DS_H */
...@@ -235,6 +235,13 @@ extern int do_get_thread_area(struct task_struct *p, int idx, ...@@ -235,6 +235,13 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
extern int do_set_thread_area(struct task_struct *p, int idx, extern int do_set_thread_area(struct task_struct *p, int idx,
struct user_desc __user *info, int can_allocate); struct user_desc __user *info, int can_allocate);
extern void x86_ptrace_untrace(struct task_struct *);
extern void x86_ptrace_fork(struct task_struct *child,
unsigned long clone_flags);
#define arch_ptrace_untrace(tsk) x86_ptrace_untrace(tsk)
#define arch_ptrace_fork(child, flags) x86_ptrace_fork(child, flags)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
......
...@@ -1017,3 +1017,14 @@ void ds_switch_to(struct task_struct *prev, struct task_struct *next) ...@@ -1017,3 +1017,14 @@ void ds_switch_to(struct task_struct *prev, struct task_struct *next)
update_debugctlmsr(next->thread.debugctlmsr); update_debugctlmsr(next->thread.debugctlmsr);
} }
void ds_copy_thread(struct task_struct *tsk, struct task_struct *father)
{
clear_tsk_thread_flag(tsk, TIF_DS_AREA_MSR);
tsk->thread.ds_ctx = NULL;
}
void ds_exit_thread(struct task_struct *tsk)
{
WARN_ON(tsk->thread.ds_ctx);
}
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include <asm/idle.h> #include <asm/idle.h>
#include <asm/syscalls.h> #include <asm/syscalls.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/ds.h>
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
...@@ -251,17 +252,8 @@ void exit_thread(void) ...@@ -251,17 +252,8 @@ void exit_thread(void)
tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET; tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
put_cpu(); put_cpu();
} }
#ifdef CONFIG_X86_DS
/* Free any BTS tracers that have not been properly released. */ ds_exit_thread(current);
if (unlikely(current->bts)) {
ds_release_bts(current->bts);
current->bts = NULL;
kfree(current->bts_buffer);
current->bts_buffer = NULL;
current->bts_size = 0;
}
#endif /* CONFIG_X86_DS */
} }
void flush_thread(void) void flush_thread(void)
...@@ -343,6 +335,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, ...@@ -343,6 +335,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
kfree(p->thread.io_bitmap_ptr); kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0; p->thread.io_bitmap_max = 0;
} }
ds_copy_thread(p, current);
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
p->thread.debugctlmsr = 0;
return err; return err;
} }
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include <asm/ia32.h> #include <asm/ia32.h>
#include <asm/idle.h> #include <asm/idle.h>
#include <asm/syscalls.h> #include <asm/syscalls.h>
#include <asm/ds.h>
asmlinkage extern void ret_from_fork(void); asmlinkage extern void ret_from_fork(void);
...@@ -236,17 +237,8 @@ void exit_thread(void) ...@@ -236,17 +237,8 @@ void exit_thread(void)
t->io_bitmap_max = 0; t->io_bitmap_max = 0;
put_cpu(); put_cpu();
} }
#ifdef CONFIG_X86_DS
/* Free any BTS tracers that have not been properly released. */ ds_exit_thread(current);
if (unlikely(current->bts)) {
ds_release_bts(current->bts);
current->bts = NULL;
kfree(current->bts_buffer);
current->bts_buffer = NULL;
current->bts_size = 0;
}
#endif /* CONFIG_X86_DS */
} }
void flush_thread(void) void flush_thread(void)
...@@ -376,6 +368,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, ...@@ -376,6 +368,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
if (err) if (err)
goto out; goto out;
} }
ds_copy_thread(p, me);
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
p->thread.debugctlmsr = 0;
err = 0; err = 0;
out: out:
if (err && p->thread.io_bitmap_ptr) { if (err && p->thread.io_bitmap_ptr) {
......
...@@ -769,8 +769,47 @@ static int ptrace_bts_size(struct task_struct *child) ...@@ -769,8 +769,47 @@ static int ptrace_bts_size(struct task_struct *child)
return (trace->ds.top - trace->ds.begin) / trace->ds.size; return (trace->ds.top - trace->ds.begin) / trace->ds.size;
} }
static void ptrace_bts_fork(struct task_struct *tsk)
{
tsk->bts = NULL;
tsk->bts_buffer = NULL;
tsk->bts_size = 0;
tsk->thread.bts_ovfl_signal = 0;
}
static void ptrace_bts_untrace(struct task_struct *child)
{
if (unlikely(child->bts)) {
ds_release_bts(child->bts);
child->bts = NULL;
kfree(child->bts_buffer);
child->bts_buffer = NULL;
child->bts_size = 0;
}
}
static void ptrace_bts_detach(struct task_struct *child)
{
ptrace_bts_untrace(child);
}
#else
static inline void ptrace_bts_fork(struct task_struct *tsk) {}
static inline void ptrace_bts_detach(struct task_struct *child) {}
static inline void ptrace_bts_untrace(struct task_struct *child) {}
#endif /* CONFIG_X86_PTRACE_BTS */ #endif /* CONFIG_X86_PTRACE_BTS */
void x86_ptrace_fork(struct task_struct *child, unsigned long clone_flags)
{
ptrace_bts_fork(child);
}
void x86_ptrace_untrace(struct task_struct *child)
{
ptrace_bts_untrace(child);
}
/* /*
* Called by kernel/ptrace.c when detaching.. * Called by kernel/ptrace.c when detaching..
* *
...@@ -782,16 +821,7 @@ void ptrace_disable(struct task_struct *child) ...@@ -782,16 +821,7 @@ void ptrace_disable(struct task_struct *child)
#ifdef TIF_SYSCALL_EMU #ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif #endif
#ifdef CONFIG_X86_PTRACE_BTS ptrace_bts_detach(child);
if (child->bts) {
ds_release_bts(child->bts);
child->bts = NULL;
kfree(child->bts_buffer);
child->bts_buffer = NULL;
child->bts_size = 0;
}
#endif /* CONFIG_X86_PTRACE_BTS */
} }
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION #if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
......
...@@ -94,6 +94,7 @@ extern void ptrace_notify(int exit_code); ...@@ -94,6 +94,7 @@ extern void ptrace_notify(int exit_code);
extern void __ptrace_link(struct task_struct *child, extern void __ptrace_link(struct task_struct *child,
struct task_struct *new_parent); struct task_struct *new_parent);
extern void __ptrace_unlink(struct task_struct *child); extern void __ptrace_unlink(struct task_struct *child);
extern void ptrace_fork(struct task_struct *task, unsigned long clone_flags);
#define PTRACE_MODE_READ 1 #define PTRACE_MODE_READ 1
#define PTRACE_MODE_ATTACH 2 #define PTRACE_MODE_ATTACH 2
/* Returns 0 on success, -errno on denial. */ /* Returns 0 on success, -errno on denial. */
...@@ -313,6 +314,27 @@ static inline void user_enable_block_step(struct task_struct *task) ...@@ -313,6 +314,27 @@ static inline void user_enable_block_step(struct task_struct *task)
#define arch_ptrace_stop(code, info) do { } while (0) #define arch_ptrace_stop(code, info) do { } while (0)
#endif #endif
#ifndef arch_ptrace_untrace
/*
* Do machine-specific work before untracing child.
*
* This is called for a normal detach as well as from ptrace_exit()
* when the tracing task dies.
*
* Called with write_lock(&tasklist_lock) held.
*/
#define arch_ptrace_untrace(task) do { } while (0)
#endif
#ifndef arch_ptrace_fork
/*
* Do machine-specific work to initialize a new task.
*
* This is called from copy_process().
*/
#define arch_ptrace_fork(child, clone_flags) do { } while (0)
#endif
extern int task_current_syscall(struct task_struct *target, long *callno, extern int task_current_syscall(struct task_struct *target, long *callno,
unsigned long args[6], unsigned int maxargs, unsigned long args[6], unsigned int maxargs,
unsigned long *sp, unsigned long *pc); unsigned long *sp, unsigned long *pc);
......
...@@ -1096,6 +1096,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1096,6 +1096,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
#ifdef CONFIG_DEBUG_MUTEXES #ifdef CONFIG_DEBUG_MUTEXES
p->blocked_on = NULL; /* not blocked yet */ p->blocked_on = NULL; /* not blocked yet */
#endif #endif
if (unlikely(ptrace_reparented(current)))
ptrace_fork(p, clone_flags);
/* Perform scheduler related setup. Assign this task to a CPU. */ /* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, clone_flags); sched_fork(p, clone_flags);
......
...@@ -25,6 +25,17 @@ ...@@ -25,6 +25,17 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
/*
* Initialize a new task whose father had been ptraced.
*
* Called from copy_process().
*/
void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
{
arch_ptrace_fork(child, clone_flags);
}
/* /*
* ptrace a task: make the debugger its new parent and * ptrace a task: make the debugger its new parent and
* move it to the ptrace list. * move it to the ptrace list.
...@@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child) ...@@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child)
child->parent = child->real_parent; child->parent = child->real_parent;
list_del_init(&child->ptrace_entry); list_del_init(&child->ptrace_entry);
arch_ptrace_untrace(child);
if (task_is_traced(child)) if (task_is_traced(child))
ptrace_untrace(child); ptrace_untrace(child);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment