You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'tracing-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'tracing-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (244 commits) Revert "x86, bts: reenable ptrace branch trace support" tracing: do not translate event helper macros in print format ftrace/documentation: fix typo in function grapher name tracing/events: convert block trace points to TRACE_EVENT(), fix !CONFIG_BLOCK tracing: add protection around module events unload tracing: add trace_seq_vprint interface tracing: fix the block trace points print size tracing/events: convert block trace points to TRACE_EVENT() ring-buffer: fix ret in rb_add_time_stamp ring-buffer: pass in lockdep class key for reader_lock tracing: add annotation to what type of stack trace is recorded tracing: fix multiple use of __print_flags and __print_symbolic tracing/events: fix output format of user stack tracing/events: fix output format of kernel stack tracing/trace_stack: fix the number of entries in the header ring-buffer: discard timestamps that are at the start of the buffer ring-buffer: try to discard unneeded timestamps ring-buffer: fix bug in ring_buffer_discard_commit ftrace: do not profile functions when disabled tracing: make trace pipe recognize latency format flag ...
This commit is contained in:
@@ -13,7 +13,8 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
|
||||
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
|
||||
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
|
||||
mac80211.xml debugobjects.xml sh.xml regulator.xml \
|
||||
alsa-driver-api.xml writing-an-alsa-driver.xml
|
||||
alsa-driver-api.xml writing-an-alsa-driver.xml \
|
||||
tracepoint.xml
|
||||
|
||||
###
|
||||
# The build process is as follows (targets):
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
|
||||
|
||||
<book id="Tracepoints">
|
||||
<bookinfo>
|
||||
<title>The Linux Kernel Tracepoint API</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Jason</firstname>
|
||||
<surname>Baron</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>jbaron@redhat.com</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
This documentation is free software; you can redistribute
|
||||
it and/or modify it under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This program is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
MA 02111-1307 USA
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For more details see the file COPYING in the source
|
||||
distribution of Linux.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</bookinfo>
|
||||
|
||||
<toc></toc>
|
||||
<chapter id="intro">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
Tracepoints are static probe points that are located in strategic points
|
||||
throughout the kernel. 'Probes' register/unregister with tracepoints
|
||||
via a callback mechanism. The 'probes' are strictly typed functions that
|
||||
are passed a unique set of parameters defined by each tracepoint.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
From this simple callback mechanism, 'probes' can be used to profile, debug,
|
||||
and understand kernel behavior. There are a number of tools that provide a
|
||||
framework for using 'probes'. These tools include Systemtap, ftrace, and
|
||||
LTTng.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Tracepoints are defined in a number of header files via various macros. Thus,
|
||||
the purpose of this document is to provide a clear accounting of the available
|
||||
tracepoints. The intention is to understand not only what tracepoints are
|
||||
available but also to understand where future tracepoints might be added.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The API presented has functions of the form:
|
||||
<function>trace_tracepointname(function parameters)</function>. These are the
|
||||
tracepoints callbacks that are found throughout the code. Registering and
|
||||
unregistering probes with these callback sites is covered in the
|
||||
<filename>Documentation/trace/*</filename> directory.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
<chapter id="irq">
|
||||
<title>IRQ</title>
|
||||
!Iinclude/trace/events/irq.h
|
||||
</chapter>
|
||||
|
||||
</book>
|
||||
@@ -56,7 +56,6 @@ parameter is applicable:
|
||||
ISAPNP ISA PnP code is enabled.
|
||||
ISDN Appropriate ISDN support is enabled.
|
||||
JOY Appropriate joystick support is enabled.
|
||||
KMEMTRACE kmemtrace is enabled.
|
||||
LIBATA Libata driver is enabled
|
||||
LP Printer support is enabled.
|
||||
LOOP Loopback device support is enabled.
|
||||
@@ -754,12 +753,25 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
ia64_pal_cache_flush instead of SAL_CACHE_FLUSH.
|
||||
|
||||
ftrace=[tracer]
|
||||
[ftrace] will set and start the specified tracer
|
||||
[FTRACE] will set and start the specified tracer
|
||||
as early as possible in order to facilitate early
|
||||
boot debugging.
|
||||
|
||||
ftrace_dump_on_oops
|
||||
[ftrace] will dump the trace buffers on oops.
|
||||
[FTRACE] will dump the trace buffers on oops.
|
||||
|
||||
ftrace_filter=[function-list]
|
||||
[FTRACE] Limit the functions traced by the function
|
||||
tracer at boot up. function-list is a comma separated
|
||||
list of functions. This list can be changed at run
|
||||
time by the set_ftrace_filter file in the debugfs
|
||||
tracing directory.
|
||||
|
||||
ftrace_notrace=[function-list]
|
||||
[FTRACE] Do not trace the functions specified in
|
||||
function-list. This list can be changed at run time
|
||||
by the set_ftrace_notrace file in the debugfs
|
||||
tracing directory.
|
||||
|
||||
gamecon.map[2|3]=
|
||||
[HW,JOY] Multisystem joystick and NES/SNES/PSX pad
|
||||
@@ -1056,15 +1068,6 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
use the HighMem zone if it exists, and the Normal
|
||||
zone if it does not.
|
||||
|
||||
kmemtrace.enable= [KNL,KMEMTRACE] Format: { yes | no }
|
||||
Controls whether kmemtrace is enabled
|
||||
at boot-time.
|
||||
|
||||
kmemtrace.subbufs=n [KNL,KMEMTRACE] Overrides the number of
|
||||
subbufs kmemtrace's relay channel has. Set this
|
||||
higher than default (KMEMTRACE_N_SUBBUFS in code) if
|
||||
you experience buffer overruns.
|
||||
|
||||
kgdboc= [HW] kgdb over consoles.
|
||||
Requires a tty driver that supports console polling.
|
||||
(only serial suported for now)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
Event Tracing
|
||||
|
||||
Documentation written by Theodore Ts'o
|
||||
Updated by Li Zefan
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
Tracepoints (see Documentation/trace/tracepoints.txt) can be used
|
||||
without creating custom kernel modules to register probe functions
|
||||
using the event tracing infrastructure.
|
||||
|
||||
Not all tracepoints can be traced using the event tracing system;
|
||||
the kernel developer must provide code snippets which define how the
|
||||
tracing information is saved into the tracing buffer, and how the
|
||||
tracing information should be printed.
|
||||
|
||||
2. Using Event Tracing
|
||||
======================
|
||||
|
||||
2.1 Via the 'set_event' interface
|
||||
---------------------------------
|
||||
|
||||
The events which are available for tracing can be found in the file
|
||||
/debug/tracing/available_events.
|
||||
|
||||
To enable a particular event, such as 'sched_wakeup', simply echo it
|
||||
to /debug/tracing/set_event. For example:
|
||||
|
||||
# echo sched_wakeup >> /debug/tracing/set_event
|
||||
|
||||
[ Note: '>>' is necessary, otherwise it will firstly disable
|
||||
all the events. ]
|
||||
|
||||
To disable an event, echo the event name to the set_event file prefixed
|
||||
with an exclamation point:
|
||||
|
||||
# echo '!sched_wakeup' >> /debug/tracing/set_event
|
||||
|
||||
To disable all events, echo an empty line to the set_event file:
|
||||
|
||||
# echo > /debug/tracing/set_event
|
||||
|
||||
To enable all events, echo '*:*' or '*:' to the set_event file:
|
||||
|
||||
# echo *:* > /debug/tracing/set_event
|
||||
|
||||
The events are organized into subsystems, such as ext4, irq, sched,
|
||||
etc., and a full event name looks like this: <subsystem>:<event>. The
|
||||
subsystem name is optional, but it is displayed in the available_events
|
||||
file. All of the events in a subsystem can be specified via the syntax
|
||||
"<subsystem>:*"; for example, to enable all irq events, you can use the
|
||||
command:
|
||||
|
||||
# echo 'irq:*' > /debug/tracing/set_event
|
||||
|
||||
2.2 Via the 'enable' toggle
|
||||
---------------------------
|
||||
|
||||
The events available are also listed in /debug/tracing/events/ hierarchy
|
||||
of directories.
|
||||
|
||||
To enable event 'sched_wakeup':
|
||||
|
||||
# echo 1 > /debug/tracing/events/sched/sched_wakeup/enable
|
||||
|
||||
To disable it:
|
||||
|
||||
# echo 0 > /debug/tracing/events/sched/sched_wakeup/enable
|
||||
|
||||
To enable all events in sched subsystem:
|
||||
|
||||
# echo 1 > /debug/tracing/events/sched/enable
|
||||
|
||||
To eanble all events:
|
||||
|
||||
# echo 1 > /debug/tracing/events/enable
|
||||
|
||||
When reading one of these enable files, there are four results:
|
||||
|
||||
0 - all events this file affects are disabled
|
||||
1 - all events this file affects are enabled
|
||||
X - there is a mixture of events enabled and disabled
|
||||
? - this file does not affect any event
|
||||
|
||||
3. Defining an event-enabled tracepoint
|
||||
=======================================
|
||||
|
||||
See The example provided in samples/trace_events
|
||||
|
||||
@@ -179,7 +179,7 @@ Here is the list of current tracers that may be configured.
|
||||
|
||||
Function call tracer to trace all kernel functions.
|
||||
|
||||
"function_graph_tracer"
|
||||
"function_graph"
|
||||
|
||||
Similar to the function tracer except that the
|
||||
function tracer probes the functions on their entry
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
The power tracer collects detailed information about C-state and P-state
|
||||
transitions, instead of just looking at the high-level "average"
|
||||
information.
|
||||
|
||||
There is a helper script found in scrips/tracing/power.pl in the kernel
|
||||
sources which can be used to parse this information and create a
|
||||
Scalable Vector Graphics (SVG) picture from the trace data.
|
||||
|
||||
To use this tracer:
|
||||
|
||||
echo 0 > /sys/kernel/debug/tracing/tracing_enabled
|
||||
echo power > /sys/kernel/debug/tracing/current_tracer
|
||||
echo 1 > /sys/kernel/debug/tracing/tracing_enabled
|
||||
sleep 1
|
||||
echo 0 > /sys/kernel/debug/tracing/tracing_enabled
|
||||
cat /sys/kernel/debug/tracing/trace | \
|
||||
perl scripts/tracing/power.pl > out.sv
|
||||
@@ -174,6 +174,15 @@ config IOMMU_LEAK
|
||||
Add a simple leak tracer to the IOMMU code. This is useful when you
|
||||
are debugging a buggy device driver that leaks IOMMU mappings.
|
||||
|
||||
config X86_DS_SELFTEST
|
||||
bool "DS selftest"
|
||||
default y
|
||||
depends on DEBUG_KERNEL
|
||||
depends on X86_DS
|
||||
---help---
|
||||
Perform Debug Store selftests at boot time.
|
||||
If in doubt, say "N".
|
||||
|
||||
config HAVE_MMIOTRACE_SUPPORT
|
||||
def_bool y
|
||||
|
||||
|
||||
+56
-26
@@ -15,8 +15,8 @@
|
||||
* - buffer allocation (memory accounting)
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2007-2008 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2007-2008
|
||||
* Copyright (C) 2007-2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2007-2009
|
||||
*/
|
||||
|
||||
#ifndef _ASM_X86_DS_H
|
||||
@@ -83,8 +83,10 @@ enum ds_feature {
|
||||
* The interrupt threshold is independent from the overflow callback
|
||||
* to allow users to use their own overflow interrupt handling mechanism.
|
||||
*
|
||||
* task: the task to request recording for;
|
||||
* NULL for per-cpu recording on the current cpu
|
||||
* The function might sleep.
|
||||
*
|
||||
* task: the task to request recording for
|
||||
* cpu: the cpu to request recording for
|
||||
* base: the base pointer for the (non-pageable) buffer;
|
||||
* size: the size of the provided buffer in bytes
|
||||
* ovfl: pointer to a function to be called on buffer overflow;
|
||||
@@ -93,19 +95,28 @@ enum ds_feature {
|
||||
* -1 if no interrupt threshold is requested.
|
||||
* flags: a bit-mask of the above flags
|
||||
*/
|
||||
extern struct bts_tracer *ds_request_bts(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
bts_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct pebs_tracer *ds_request_pebs(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
pebs_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct bts_tracer *ds_request_bts_task(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
bts_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct bts_tracer *ds_request_bts_cpu(int cpu, void *base, size_t size,
|
||||
bts_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct pebs_tracer *ds_request_pebs_task(struct task_struct *task,
|
||||
void *base, size_t size,
|
||||
pebs_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
extern struct pebs_tracer *ds_request_pebs_cpu(int cpu,
|
||||
void *base, size_t size,
|
||||
pebs_ovfl_callback_t ovfl,
|
||||
size_t th, unsigned int flags);
|
||||
|
||||
/*
|
||||
* Release BTS or PEBS resources
|
||||
* Suspend and resume BTS or PEBS tracing
|
||||
*
|
||||
* Must be called with irq's enabled.
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern void ds_release_bts(struct bts_tracer *tracer);
|
||||
@@ -115,6 +126,28 @@ extern void ds_release_pebs(struct pebs_tracer *tracer);
|
||||
extern void ds_suspend_pebs(struct pebs_tracer *tracer);
|
||||
extern void ds_resume_pebs(struct pebs_tracer *tracer);
|
||||
|
||||
/*
|
||||
* Release BTS or PEBS resources
|
||||
* Suspend and resume BTS or PEBS tracing
|
||||
*
|
||||
* Cpu tracers must call this on the traced cpu.
|
||||
* Task tracers must call ds_release_~_noirq() for themselves.
|
||||
*
|
||||
* May be called with irq's disabled.
|
||||
*
|
||||
* Returns 0 if successful;
|
||||
* -EPERM if the cpu tracer does not trace the current cpu.
|
||||
* -EPERM if the task tracer does not trace itself.
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_~()
|
||||
*/
|
||||
extern int ds_release_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_suspend_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_resume_bts_noirq(struct bts_tracer *tracer);
|
||||
extern int ds_release_pebs_noirq(struct pebs_tracer *tracer);
|
||||
extern int ds_suspend_pebs_noirq(struct pebs_tracer *tracer);
|
||||
extern int ds_resume_pebs_noirq(struct pebs_tracer *tracer);
|
||||
|
||||
|
||||
/*
|
||||
* The raw DS buffer state as it is used for BTS and PEBS recording.
|
||||
@@ -170,9 +203,9 @@ struct bts_struct {
|
||||
} lbr;
|
||||
/* BTS_TASK_ARRIVES or BTS_TASK_DEPARTS */
|
||||
struct {
|
||||
__u64 jiffies;
|
||||
__u64 clock;
|
||||
pid_t pid;
|
||||
} timestamp;
|
||||
} event;
|
||||
} variant;
|
||||
};
|
||||
|
||||
@@ -201,8 +234,12 @@ struct bts_trace {
|
||||
struct pebs_trace {
|
||||
struct ds_trace ds;
|
||||
|
||||
/* the PEBS reset value */
|
||||
unsigned long long reset_value;
|
||||
/* the number of valid counters in the below array */
|
||||
unsigned int counters;
|
||||
|
||||
#define MAX_PEBS_COUNTERS 4
|
||||
/* the counter reset value */
|
||||
unsigned long long counter_reset[MAX_PEBS_COUNTERS];
|
||||
};
|
||||
|
||||
|
||||
@@ -237,9 +274,11 @@ extern int ds_reset_pebs(struct pebs_tracer *tracer);
|
||||
* Returns 0 on success; -Eerrno on error
|
||||
*
|
||||
* tracer: the tracer handle returned from ds_request_pebs()
|
||||
* counter: the index of the counter
|
||||
* value: the new counter reset value
|
||||
*/
|
||||
extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value);
|
||||
extern int ds_set_pebs_reset(struct pebs_tracer *tracer,
|
||||
unsigned int counter, u64 value);
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
@@ -252,21 +291,12 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
|
||||
*/
|
||||
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 */
|
||||
|
||||
struct cpuinfo_x86;
|
||||
static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {}
|
||||
static inline void ds_switch_to(struct task_struct *prev,
|
||||
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 /* _ASM_X86_DS_H */
|
||||
|
||||
@@ -462,14 +462,8 @@ struct thread_struct {
|
||||
unsigned io_bitmap_max;
|
||||
/* MSR_IA32_DEBUGCTLMSR value to switch in if TIF_DEBUGCTLMSR is set. */
|
||||
unsigned long debugctlmsr;
|
||||
#ifdef CONFIG_X86_DS
|
||||
/* Debug Store context; see include/asm-x86/ds.h; goes into MSR_IA32_DS_AREA */
|
||||
/* Debug Store context; see asm/ds.h */
|
||||
struct ds_context *ds_ctx;
|
||||
#endif /* CONFIG_X86_DS */
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
/* the signal to send on a bts buffer overflow */
|
||||
unsigned int bts_ovfl_signal;
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
};
|
||||
|
||||
static inline unsigned long native_get_debugreg(int regno)
|
||||
@@ -797,6 +791,21 @@ static inline unsigned long get_debugctlmsr(void)
|
||||
return debugctlmsr;
|
||||
}
|
||||
|
||||
static inline unsigned long get_debugctlmsr_on_cpu(int cpu)
|
||||
{
|
||||
u64 debugctlmsr = 0;
|
||||
u32 val1, val2;
|
||||
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
return 0;
|
||||
#endif
|
||||
rdmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR, &val1, &val2);
|
||||
debugctlmsr = val1 | ((u64)val2 << 32);
|
||||
|
||||
return debugctlmsr;
|
||||
}
|
||||
|
||||
static inline void update_debugctlmsr(unsigned long debugctlmsr)
|
||||
{
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
@@ -806,6 +815,18 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr)
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
||||
}
|
||||
|
||||
static inline void update_debugctlmsr_on_cpu(int cpu,
|
||||
unsigned long debugctlmsr)
|
||||
{
|
||||
#ifndef CONFIG_X86_DEBUGCTLMSR
|
||||
if (boot_cpu_data.x86 < 6)
|
||||
return;
|
||||
#endif
|
||||
wrmsr_on_cpu(cpu, MSR_IA32_DEBUGCTLMSR,
|
||||
(u32)((u64)debugctlmsr),
|
||||
(u32)((u64)debugctlmsr >> 32));
|
||||
}
|
||||
|
||||
/*
|
||||
* from system description table in BIOS. Mostly for MCA use, but
|
||||
* others may find it useful:
|
||||
|
||||
@@ -236,12 +236,11 @@ extern int do_get_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);
|
||||
|
||||
extern void x86_ptrace_untrace(struct task_struct *);
|
||||
extern void x86_ptrace_fork(struct task_struct *child,
|
||||
unsigned long clone_flags);
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
extern void ptrace_bts_untrace(struct task_struct *tsk);
|
||||
|
||||
#define arch_ptrace_untrace(tsk) x86_ptrace_untrace(tsk)
|
||||
#define arch_ptrace_fork(child, flags) x86_ptrace_fork(child, flags)
|
||||
#define arch_ptrace_untrace(tsk) ptrace_bts_untrace(tsk)
|
||||
#endif /* CONFIG_X86_PTRACE_BTS */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
static inline void __native_flush_tlb(void)
|
||||
{
|
||||
write_cr3(read_cr3());
|
||||
native_write_cr3(native_read_cr3());
|
||||
}
|
||||
|
||||
static inline void __native_flush_tlb_global(void)
|
||||
@@ -32,11 +32,11 @@ static inline void __native_flush_tlb_global(void)
|
||||
*/
|
||||
raw_local_irq_save(flags);
|
||||
|
||||
cr4 = read_cr4();
|
||||
cr4 = native_read_cr4();
|
||||
/* clear PGE */
|
||||
write_cr4(cr4 & ~X86_CR4_PGE);
|
||||
native_write_cr4(cr4 & ~X86_CR4_PGE);
|
||||
/* write old PGE again and flush TLBs */
|
||||
write_cr4(cr4);
|
||||
native_write_cr4(cr4);
|
||||
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ obj-y += process.o
|
||||
obj-y += i387.o xsave.o
|
||||
obj-y += ptrace.o
|
||||
obj-$(CONFIG_X86_DS) += ds.o
|
||||
obj-$(CONFIG_X86_DS_SELFTEST) += ds_selftest.o
|
||||
obj-$(CONFIG_X86_32) += tls.o
|
||||
obj-$(CONFIG_IA32_EMULATION) += tls.o
|
||||
obj-y += step.o
|
||||
|
||||
+665
-260
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Debug Store support - selftest
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2009
|
||||
*/
|
||||
|
||||
#include "ds_selftest.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <asm/ds.h>
|
||||
|
||||
|
||||
#define BUFFER_SIZE 521 /* Intentionally chose an odd size. */
|
||||
#define SMALL_BUFFER_SIZE 24 /* A single bts entry. */
|
||||
|
||||
struct ds_selftest_bts_conf {
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
int (*suspend)(struct bts_tracer *);
|
||||
int (*resume)(struct bts_tracer *);
|
||||
};
|
||||
|
||||
static int ds_selftest_bts_consistency(const struct bts_trace *trace)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!trace) {
|
||||
printk(KERN_CONT "failed to access trace...");
|
||||
/* Bail out. Other tests are pointless. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!trace->read) {
|
||||
printk(KERN_CONT "bts read not available...");
|
||||
error = -1;
|
||||
}
|
||||
|
||||
/* Do some sanity checks on the trace configuration. */
|
||||
if (!trace->ds.n) {
|
||||
printk(KERN_CONT "empty bts buffer...");
|
||||
error = -1;
|
||||
}
|
||||
if (!trace->ds.size) {
|
||||
printk(KERN_CONT "bad bts trace setup...");
|
||||
error = -1;
|
||||
}
|
||||
if (trace->ds.end !=
|
||||
(char *)trace->ds.begin + (trace->ds.n * trace->ds.size)) {
|
||||
printk(KERN_CONT "bad bts buffer setup...");
|
||||
error = -1;
|
||||
}
|
||||
/*
|
||||
* We allow top in [begin; end], since its not clear when the
|
||||
* overflow adjustment happens: after the increment or before the
|
||||
* write.
|
||||
*/
|
||||
if ((trace->ds.top < trace->ds.begin) ||
|
||||
(trace->ds.end < trace->ds.top)) {
|
||||
printk(KERN_CONT "bts top out of bounds...");
|
||||
error = -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_read(struct bts_tracer *tracer,
|
||||
const struct bts_trace *trace,
|
||||
const void *from, const void *to)
|
||||
{
|
||||
const unsigned char *at;
|
||||
|
||||
/*
|
||||
* Check a few things which do not belong to this test.
|
||||
* They should be covered by other tests.
|
||||
*/
|
||||
if (!trace)
|
||||
return -1;
|
||||
|
||||
if (!trace->read)
|
||||
return -1;
|
||||
|
||||
if (to < from)
|
||||
return -1;
|
||||
|
||||
if (from < trace->ds.begin)
|
||||
return -1;
|
||||
|
||||
if (trace->ds.end < to)
|
||||
return -1;
|
||||
|
||||
if (!trace->ds.size)
|
||||
return -1;
|
||||
|
||||
/* Now to the test itself. */
|
||||
for (at = from; (void *)at < to; at += trace->ds.size) {
|
||||
struct bts_struct bts;
|
||||
unsigned long index;
|
||||
int error;
|
||||
|
||||
if (((void *)at - trace->ds.begin) % trace->ds.size) {
|
||||
printk(KERN_CONT
|
||||
"read from non-integer index...");
|
||||
return -1;
|
||||
}
|
||||
index = ((void *)at - trace->ds.begin) / trace->ds.size;
|
||||
|
||||
memset(&bts, 0, sizeof(bts));
|
||||
error = trace->read(tracer, at, &bts);
|
||||
if (error < 0) {
|
||||
printk(KERN_CONT
|
||||
"error reading bts trace at [%lu] (0x%p)...",
|
||||
index, at);
|
||||
return error;
|
||||
}
|
||||
|
||||
switch (bts.qualifier) {
|
||||
case BTS_BRANCH:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_CONT
|
||||
"unexpected bts entry %llu at [%lu] (0x%p)...",
|
||||
bts.qualifier, index, at);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds_selftest_bts_cpu(void *arg)
|
||||
{
|
||||
struct ds_selftest_bts_conf *conf = arg;
|
||||
const struct bts_trace *trace;
|
||||
void *top;
|
||||
|
||||
if (IS_ERR(conf->tracer)) {
|
||||
conf->error = PTR_ERR(conf->tracer);
|
||||
conf->tracer = NULL;
|
||||
|
||||
printk(KERN_CONT
|
||||
"initialization failed (err: %d)...", conf->error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We should meanwhile have enough trace. */
|
||||
conf->error = conf->suspend(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/* Let's see if we can access the trace. */
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/* If everything went well, we should have a few trace entries. */
|
||||
if (trace->ds.top == trace->ds.begin) {
|
||||
/*
|
||||
* It is possible but highly unlikely that we got a
|
||||
* buffer overflow and end up at exactly the same
|
||||
* position we started from.
|
||||
* Let's issue a warning, but continue.
|
||||
*/
|
||||
printk(KERN_CONT "no trace/overflow...");
|
||||
}
|
||||
|
||||
/* Let's try to read the trace we collected. */
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.top);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Let's read the trace again.
|
||||
* Since we suspended tracing, we should get the same result.
|
||||
*/
|
||||
top = trace->ds.top;
|
||||
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
if (top != trace->ds.top) {
|
||||
printk(KERN_CONT "suspend not working...");
|
||||
conf->error = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's collect some more trace - see if resume is working. */
|
||||
conf->error = conf->resume(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
conf->error = conf->suspend(conf->tracer);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
trace = ds_read_bts(conf->tracer);
|
||||
|
||||
conf->error = ds_selftest_bts_consistency(trace);
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
if (trace->ds.top == top) {
|
||||
/*
|
||||
* It is possible but highly unlikely that we got a
|
||||
* buffer overflow and end up at exactly the same
|
||||
* position we started from.
|
||||
* Let's issue a warning and check the full trace.
|
||||
*/
|
||||
printk(KERN_CONT
|
||||
"no resume progress/overflow...");
|
||||
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.end);
|
||||
} else if (trace->ds.top < top) {
|
||||
/*
|
||||
* We had a buffer overflow - the entire buffer should
|
||||
* contain trace records.
|
||||
*/
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace,
|
||||
trace->ds.begin, trace->ds.end);
|
||||
} else {
|
||||
/*
|
||||
* It is quite likely that the buffer did not overflow.
|
||||
* Let's just check the delta trace.
|
||||
*/
|
||||
conf->error =
|
||||
ds_selftest_bts_read(conf->tracer, trace, top,
|
||||
trace->ds.top);
|
||||
}
|
||||
if (conf->error < 0)
|
||||
return;
|
||||
|
||||
conf->error = 0;
|
||||
}
|
||||
|
||||
static int ds_suspend_bts_wrap(struct bts_tracer *tracer)
|
||||
{
|
||||
ds_suspend_bts(tracer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds_resume_bts_wrap(struct bts_tracer *tracer)
|
||||
{
|
||||
ds_resume_bts(tracer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ds_release_bts_noirq_wrap(void *tracer)
|
||||
{
|
||||
(void)ds_release_bts_noirq(tracer);
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_release_noirq(int cpu,
|
||||
struct bts_tracer *tracer)
|
||||
{
|
||||
int error = -EPERM;
|
||||
|
||||
/* Try to release the tracer on the wrong cpu. */
|
||||
get_cpu();
|
||||
if (cpu != smp_processor_id()) {
|
||||
error = ds_release_bts_noirq(tracer);
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "release on wrong cpu...");
|
||||
}
|
||||
put_cpu();
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_request_cpu(int cpu, void *buffer)
|
||||
{
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
|
||||
/* Try to request cpu tracing while task tracing is active. */
|
||||
tracer = ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE, NULL,
|
||||
(size_t)-1, BTS_KERNEL);
|
||||
error = PTR_ERR(tracer);
|
||||
if (!IS_ERR(tracer)) {
|
||||
ds_release_bts(tracer);
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "cpu/task tracing overlap...");
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
static int ds_selftest_bts_bad_request_task(void *buffer)
|
||||
{
|
||||
struct bts_tracer *tracer;
|
||||
int error;
|
||||
|
||||
/* Try to request cpu tracing while task tracing is active. */
|
||||
tracer = ds_request_bts_task(current, buffer, BUFFER_SIZE, NULL,
|
||||
(size_t)-1, BTS_KERNEL);
|
||||
error = PTR_ERR(tracer);
|
||||
if (!IS_ERR(tracer)) {
|
||||
error = 0;
|
||||
ds_release_bts(tracer);
|
||||
}
|
||||
|
||||
if (error != -EPERM)
|
||||
printk(KERN_CONT "task/cpu tracing overlap...");
|
||||
|
||||
return error ? 0 : -1;
|
||||
}
|
||||
|
||||
int ds_selftest_bts(void)
|
||||
{
|
||||
struct ds_selftest_bts_conf conf;
|
||||
unsigned char buffer[BUFFER_SIZE], *small_buffer;
|
||||
unsigned long irq;
|
||||
int cpu;
|
||||
|
||||
printk(KERN_INFO "[ds] bts selftest...");
|
||||
conf.error = 0;
|
||||
|
||||
small_buffer = (unsigned char *)ALIGN((unsigned long)buffer, 8) + 8;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
conf.suspend = ds_suspend_bts_wrap;
|
||||
conf.resume = ds_resume_bts_wrap;
|
||||
conf.tracer =
|
||||
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_task(buffer);
|
||||
ds_release_bts(conf.tracer);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.suspend = ds_suspend_bts_noirq;
|
||||
conf.resume = ds_resume_bts_noirq;
|
||||
conf.tracer =
|
||||
ds_request_bts_cpu(cpu, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
smp_call_function_single(cpu, ds_selftest_bts_cpu, &conf, 1);
|
||||
if (conf.error >= 0) {
|
||||
conf.error =
|
||||
ds_selftest_bts_bad_release_noirq(cpu,
|
||||
conf.tracer);
|
||||
/* We must not release the tracer twice. */
|
||||
if (conf.error < 0)
|
||||
conf.tracer = NULL;
|
||||
}
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_task(buffer);
|
||||
smp_call_function_single(cpu, ds_release_bts_noirq_wrap,
|
||||
conf.tracer, 1);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
conf.suspend = ds_suspend_bts_wrap;
|
||||
conf.resume = ds_resume_bts_wrap;
|
||||
conf.tracer =
|
||||
ds_request_bts_task(current, buffer, BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
|
||||
ds_release_bts(conf.tracer);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.suspend = ds_suspend_bts_noirq;
|
||||
conf.resume = ds_resume_bts_noirq;
|
||||
conf.tracer =
|
||||
ds_request_bts_task(current, small_buffer, SMALL_BUFFER_SIZE,
|
||||
NULL, (size_t)-1, BTS_KERNEL);
|
||||
local_irq_save(irq);
|
||||
ds_selftest_bts_cpu(&conf);
|
||||
if (conf.error >= 0)
|
||||
conf.error = ds_selftest_bts_bad_request_cpu(0, buffer);
|
||||
ds_release_bts_noirq(conf.tracer);
|
||||
local_irq_restore(irq);
|
||||
if (conf.error < 0)
|
||||
goto out;
|
||||
|
||||
conf.error = 0;
|
||||
out:
|
||||
put_online_cpus();
|
||||
printk(KERN_CONT "%s.\n", (conf.error ? "failed" : "passed"));
|
||||
|
||||
return conf.error;
|
||||
}
|
||||
|
||||
int ds_selftest_pebs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Debug Store support - selftest
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation.
|
||||
* Markus Metzger <markus.t.metzger@intel.com>, 2009
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_X86_DS_SELFTEST
|
||||
extern int ds_selftest_bts(void);
|
||||
extern int ds_selftest_pebs(void);
|
||||
#else
|
||||
static inline int ds_selftest_bts(void) { return 0; }
|
||||
static inline int ds_selftest_pebs(void) { return 0; }
|
||||
#endif
|
||||
@@ -147,27 +147,14 @@ END(ftrace_graph_caller)
|
||||
GLOBAL(return_to_handler)
|
||||
subq $80, %rsp
|
||||
|
||||
/* Save the return values */
|
||||
movq %rax, (%rsp)
|
||||
movq %rcx, 8(%rsp)
|
||||
movq %rdx, 16(%rsp)
|
||||
movq %rsi, 24(%rsp)
|
||||
movq %rdi, 32(%rsp)
|
||||
movq %r8, 40(%rsp)
|
||||
movq %r9, 48(%rsp)
|
||||
movq %r10, 56(%rsp)
|
||||
movq %r11, 64(%rsp)
|
||||
movq %rdx, 8(%rsp)
|
||||
|
||||
call ftrace_return_to_handler
|
||||
|
||||
movq %rax, 72(%rsp)
|
||||
movq 64(%rsp), %r11
|
||||
movq 56(%rsp), %r10
|
||||
movq 48(%rsp), %r9
|
||||
movq 40(%rsp), %r8
|
||||
movq 32(%rsp), %rdi
|
||||
movq 24(%rsp), %rsi
|
||||
movq 16(%rsp), %rdx
|
||||
movq 8(%rsp), %rcx
|
||||
movq 8(%rsp), %rdx
|
||||
movq (%rsp), %rax
|
||||
addq $72, %rsp
|
||||
retq
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <asm/idle.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/i387.h>
|
||||
#include <asm/ds.h>
|
||||
|
||||
unsigned long idle_halt;
|
||||
EXPORT_SYMBOL(idle_halt);
|
||||
@@ -47,6 +48,8 @@ void free_thread_xstate(struct task_struct *tsk)
|
||||
kmem_cache_free(task_xstate_cachep, tsk->thread.xstate);
|
||||
tsk->thread.xstate = NULL;
|
||||
}
|
||||
|
||||
WARN(tsk->thread.ds_ctx, "leaking DS context\n");
|
||||
}
|
||||
|
||||
void free_thread_info(struct thread_info *ti)
|
||||
@@ -85,8 +88,6 @@ void exit_thread(void)
|
||||
put_cpu();
|
||||
kfree(bp);
|
||||
}
|
||||
|
||||
ds_exit_thread(current);
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
|
||||
@@ -287,7 +287,8 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||
p->thread.io_bitmap_max = 0;
|
||||
}
|
||||
|
||||
ds_copy_thread(p, current);
|
||||
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
||||
p->thread.ds_ctx = NULL;
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
|
||||
p->thread.debugctlmsr = 0;
|
||||
|
||||
@@ -332,7 +332,8 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ds_copy_thread(p, me);
|
||||
clear_tsk_thread_flag(p, TIF_DS_AREA_MSR);
|
||||
p->thread.ds_ctx = NULL;
|
||||
|
||||
clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
|
||||
p->thread.debugctlmsr = 0;
|
||||
|
||||
+182
-102
@@ -21,6 +21,7 @@
|
||||
#include <linux/audit.h>
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
@@ -578,17 +579,130 @@ static int ioperm_get(struct task_struct *target,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_PTRACE_BTS
|
||||
/*
|
||||
* A branch trace store context.
|
||||
*
|
||||
* Contexts may only be installed by ptrace_bts_config() and only for
|
||||
* ptraced tasks.
|
||||
*
|
||||
* Contexts are destroyed when the tracee is detached from the tracer.
|
||||
* The actual destruction work requires interrupts enabled, so the
|
||||
* work is deferred and will be scheduled during __ptrace_unlink().
|
||||
*
|
||||
* Contexts hold an additional task_struct reference on the traced
|
||||
* task, as well as a reference on the tracer's mm.
|
||||
*
|
||||
* Ptrace already holds a task_struct for the duration of ptrace operations,
|
||||
* but since destruction is deferred, it may be executed after both
|
||||
* tracer and tracee exited.
|
||||
*/
|
||||
struct bts_context {
|
||||
/* The branch trace handle. */
|
||||
struct bts_tracer *tracer;
|
||||
|
||||
/* The buffer used to store the branch trace and its size. */
|
||||
void *buffer;
|
||||
unsigned int size;
|
||||
|
||||
/* The mm that paid for the above buffer. */
|
||||
struct mm_struct *mm;
|
||||
|
||||
/* The task this context belongs to. */
|
||||
struct task_struct *task;
|
||||
|
||||
/* The signal to send on a bts buffer overflow. */
|
||||
unsigned int bts_ovfl_signal;
|
||||
|
||||
/* The work struct to destroy a context. */
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static int alloc_bts_buffer(struct bts_context *context, unsigned int size)
|
||||
{
|
||||
void *buffer = NULL;
|
||||
int err = -ENOMEM;
|
||||
|
||||
err = account_locked_memory(current->mm, current->signal->rlim, size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
buffer = kzalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
goto out_refund;
|
||||
|
||||
context->buffer = buffer;
|
||||
context->size = size;
|
||||
context->mm = get_task_mm(current);
|
||||
|
||||
return 0;
|
||||
|
||||
out_refund:
|
||||
refund_locked_memory(current->mm, size);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void free_bts_buffer(struct bts_context *context)
|
||||
{
|
||||
if (!context->buffer)
|
||||
return;
|
||||
|
||||
kfree(context->buffer);
|
||||
context->buffer = NULL;
|
||||
|
||||
refund_locked_memory(context->mm, context->size);
|
||||
context->size = 0;
|
||||
|
||||
mmput(context->mm);
|
||||
context->mm = NULL;
|
||||
}
|
||||
|
||||
static void free_bts_context_work(struct work_struct *w)
|
||||
{
|
||||
struct bts_context *context;
|
||||
|
||||
context = container_of(w, struct bts_context, work);
|
||||
|
||||
ds_release_bts(context->tracer);
|
||||
put_task_struct(context->task);
|
||||
free_bts_buffer(context);
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
static inline void free_bts_context(struct bts_context *context)
|
||||
{
|
||||
INIT_WORK(&context->work, free_bts_context_work);
|
||||
schedule_work(&context->work);
|
||||
}
|
||||
|
||||
static inline struct bts_context *alloc_bts_context(struct task_struct *task)
|
||||
{
|
||||
struct bts_context *context = kzalloc(sizeof(*context), GFP_KERNEL);
|
||||
if (context) {
|
||||
context->task = task;
|
||||
task->bts = context;
|
||||
|
||||
get_task_struct(task);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static int ptrace_bts_read_record(struct task_struct *child, size_t index,
|
||||
struct bts_struct __user *out)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
struct bts_struct bts;
|
||||
const unsigned char *at;
|
||||
int error;
|
||||
|
||||
trace = ds_read_bts(child->bts);
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -EPERM;
|
||||
return -ESRCH;
|
||||
|
||||
at = trace->ds.top - ((index + 1) * trace->ds.size);
|
||||
if ((void *)at < trace->ds.begin)
|
||||
@@ -597,7 +711,7 @@ static int ptrace_bts_read_record(struct task_struct *child, size_t index,
|
||||
if (!trace->read)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
error = trace->read(child->bts, at, &bts);
|
||||
error = trace->read(context->tracer, at, &bts);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
@@ -611,13 +725,18 @@ static int ptrace_bts_drain(struct task_struct *child,
|
||||
long size,
|
||||
struct bts_struct __user *out)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
const unsigned char *at;
|
||||
int error, drained = 0;
|
||||
|
||||
trace = ds_read_bts(child->bts);
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -EPERM;
|
||||
return -ESRCH;
|
||||
|
||||
if (!trace->read)
|
||||
return -EOPNOTSUPP;
|
||||
@@ -628,9 +747,8 @@ static int ptrace_bts_drain(struct task_struct *child,
|
||||
for (at = trace->ds.begin; (void *)at < trace->ds.top;
|
||||
out++, drained++, at += trace->ds.size) {
|
||||
struct bts_struct bts;
|
||||
int error;
|
||||
|
||||
error = trace->read(child->bts, at, &bts);
|
||||
error = trace->read(context->tracer, at, &bts);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
@@ -640,35 +758,18 @@ static int ptrace_bts_drain(struct task_struct *child,
|
||||
|
||||
memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
|
||||
|
||||
error = ds_reset_bts(child->bts);
|
||||
error = ds_reset_bts(context->tracer);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
return drained;
|
||||
}
|
||||
|
||||
static int ptrace_bts_allocate_buffer(struct task_struct *child, size_t size)
|
||||
{
|
||||
child->bts_buffer = alloc_locked_buffer(size);
|
||||
if (!child->bts_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
child->bts_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ptrace_bts_free_buffer(struct task_struct *child)
|
||||
{
|
||||
free_locked_buffer(child->bts_buffer, child->bts_size);
|
||||
child->bts_buffer = NULL;
|
||||
child->bts_size = 0;
|
||||
}
|
||||
|
||||
static int ptrace_bts_config(struct task_struct *child,
|
||||
long cfg_size,
|
||||
const struct ptrace_bts_config __user *ucfg)
|
||||
{
|
||||
struct bts_context *context;
|
||||
struct ptrace_bts_config cfg;
|
||||
unsigned int flags = 0;
|
||||
|
||||
@@ -678,28 +779,33 @@ static int ptrace_bts_config(struct task_struct *child,
|
||||
if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (child->bts) {
|
||||
ds_release_bts(child->bts);
|
||||
child->bts = NULL;
|
||||
}
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
context = alloc_bts_context(child);
|
||||
if (!context)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
|
||||
if (!cfg.signal)
|
||||
return -EINVAL;
|
||||
|
||||
child->thread.bts_ovfl_signal = cfg.signal;
|
||||
return -EOPNOTSUPP;
|
||||
context->bts_ovfl_signal = cfg.signal;
|
||||
}
|
||||
|
||||
if ((cfg.flags & PTRACE_BTS_O_ALLOC) &&
|
||||
(cfg.size != child->bts_size)) {
|
||||
int error;
|
||||
ds_release_bts(context->tracer);
|
||||
context->tracer = NULL;
|
||||
|
||||
ptrace_bts_free_buffer(child);
|
||||
if ((cfg.flags & PTRACE_BTS_O_ALLOC) && (cfg.size != context->size)) {
|
||||
int err;
|
||||
|
||||
error = ptrace_bts_allocate_buffer(child, cfg.size);
|
||||
if (error < 0)
|
||||
return error;
|
||||
free_bts_buffer(context);
|
||||
if (!cfg.size)
|
||||
return 0;
|
||||
|
||||
err = alloc_bts_buffer(context, cfg.size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (cfg.flags & PTRACE_BTS_O_TRACE)
|
||||
@@ -708,15 +814,14 @@ static int ptrace_bts_config(struct task_struct *child,
|
||||
if (cfg.flags & PTRACE_BTS_O_SCHED)
|
||||
flags |= BTS_TIMESTAMPS;
|
||||
|
||||
child->bts = ds_request_bts(child, child->bts_buffer, child->bts_size,
|
||||
/* ovfl = */ NULL, /* th = */ (size_t)-1,
|
||||
flags);
|
||||
if (IS_ERR(child->bts)) {
|
||||
int error = PTR_ERR(child->bts);
|
||||
|
||||
ptrace_bts_free_buffer(child);
|
||||
child->bts = NULL;
|
||||
context->tracer =
|
||||
ds_request_bts_task(child, context->buffer, context->size,
|
||||
NULL, (size_t)-1, flags);
|
||||
if (unlikely(IS_ERR(context->tracer))) {
|
||||
int error = PTR_ERR(context->tracer);
|
||||
|
||||
free_bts_buffer(context);
|
||||
context->tracer = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -727,20 +832,25 @@ static int ptrace_bts_status(struct task_struct *child,
|
||||
long cfg_size,
|
||||
struct ptrace_bts_config __user *ucfg)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
struct ptrace_bts_config cfg;
|
||||
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
if (cfg_size < sizeof(cfg))
|
||||
return -EIO;
|
||||
|
||||
trace = ds_read_bts(child->bts);
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -EPERM;
|
||||
return -ESRCH;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.size = trace->ds.end - trace->ds.begin;
|
||||
cfg.signal = child->thread.bts_ovfl_signal;
|
||||
cfg.bts_size = sizeof(struct bts_struct);
|
||||
cfg.size = trace->ds.end - trace->ds.begin;
|
||||
cfg.signal = context->bts_ovfl_signal;
|
||||
cfg.bts_size = sizeof(struct bts_struct);
|
||||
|
||||
if (cfg.signal)
|
||||
cfg.flags |= PTRACE_BTS_O_SIGNAL;
|
||||
@@ -759,80 +869,51 @@ static int ptrace_bts_status(struct task_struct *child,
|
||||
|
||||
static int ptrace_bts_clear(struct task_struct *child)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
|
||||
trace = ds_read_bts(child->bts);
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -EPERM;
|
||||
return -ESRCH;
|
||||
|
||||
memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
|
||||
|
||||
return ds_reset_bts(child->bts);
|
||||
return ds_reset_bts(context->tracer);
|
||||
}
|
||||
|
||||
static int ptrace_bts_size(struct task_struct *child)
|
||||
{
|
||||
struct bts_context *context;
|
||||
const struct bts_trace *trace;
|
||||
|
||||
trace = ds_read_bts(child->bts);
|
||||
context = child->bts;
|
||||
if (!context)
|
||||
return -ESRCH;
|
||||
|
||||
trace = ds_read_bts(context->tracer);
|
||||
if (!trace)
|
||||
return -EPERM;
|
||||
return -ESRCH;
|
||||
|
||||
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)
|
||||
/*
|
||||
* Called from __ptrace_unlink() after the child has been moved back
|
||||
* to its original parent.
|
||||
*/
|
||||
void ptrace_bts_untrace(struct task_struct *child)
|
||||
{
|
||||
if (unlikely(child->bts)) {
|
||||
ds_release_bts(child->bts);
|
||||
free_bts_context(child->bts);
|
||||
child->bts = NULL;
|
||||
|
||||
/* We cannot update total_vm and locked_vm since
|
||||
child's mm is already gone. But we can reclaim the
|
||||
memory. */
|
||||
kfree(child->bts_buffer);
|
||||
child->bts_buffer = NULL;
|
||||
child->bts_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ptrace_bts_detach(struct task_struct *child)
|
||||
{
|
||||
/*
|
||||
* Ptrace_detach() races with ptrace_untrace() in case
|
||||
* the child dies and is reaped by another thread.
|
||||
*
|
||||
* We only do the memory accounting at this point and
|
||||
* leave the buffer deallocation and the bts tracer
|
||||
* release to ptrace_bts_untrace() which will be called
|
||||
* later on with tasklist_lock held.
|
||||
*/
|
||||
release_locked_buffer(child->bts_buffer, child->bts_size);
|
||||
}
|
||||
#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 */
|
||||
|
||||
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..
|
||||
*
|
||||
@@ -844,7 +925,6 @@ void ptrace_disable(struct task_struct *child)
|
||||
#ifdef TIF_SYSCALL_EMU
|
||||
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
|
||||
#endif
|
||||
ptrace_bts_detach(child);
|
||||
}
|
||||
|
||||
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user