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

x86, bts: base in-kernel ds interface on handles

Impact: generalize the DS code to shared buffers

Change the in-kernel ds.h interface to identify the tracer via a
handle returned on ds_request_~().

Tracers used to be identified via their task_struct.

The changes are required to allow DS to be shared between different
tasks, which is needed for perfmon2 and for ftrace.

For ptrace, the handle is stored in the traced task's task_struct.
This should probably go into a (arch-specific) ptrace context some
time.
Signed-off-by: default avatarMarkus Metzger <markus.t.metzger@intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 7d55718b
...@@ -26,11 +26,18 @@ ...@@ -26,11 +26,18 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h>
#ifdef CONFIG_X86_DS #ifdef CONFIG_X86_DS
struct task_struct; struct task_struct;
struct ds_tracer;
struct bts_tracer;
struct pebs_tracer;
typedef void (*bts_ovfl_callback_t)(struct bts_tracer *);
typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *);
/* /*
* Request BTS or PEBS * Request BTS or PEBS
...@@ -38,21 +45,29 @@ struct task_struct; ...@@ -38,21 +45,29 @@ struct task_struct;
* Due to alignement constraints, the actual buffer may be slightly * Due to alignement constraints, the actual buffer may be slightly
* smaller than the requested or provided buffer. * smaller than the requested or provided buffer.
* *
* Returns 0 on success; -Eerrno otherwise * Returns a pointer to a tracer structure on success, or
* ERR_PTR(errcode) on failure.
*
* 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; * task: the task to request recording for;
* NULL for per-cpu recording on the current cpu * NULL for per-cpu recording on the current cpu
* base: the base pointer for the (non-pageable) buffer; * base: the base pointer for the (non-pageable) buffer;
* NULL if buffer allocation requested * NULL if buffer allocation requested
* size: the size of the requested or provided buffer * size: the size of the requested or provided buffer in bytes
* ovfl: pointer to a function to be called on buffer overflow; * ovfl: pointer to a function to be called on buffer overflow;
* NULL if cyclic buffer requested * NULL if cyclic buffer requested
* th: the interrupt threshold in records from the end of the buffer;
* -1 if no interrupt threshold is requested.
*/ */
typedef void (*ds_ovfl_callback_t)(struct task_struct *); extern struct bts_tracer *ds_request_bts(struct task_struct *task,
extern int ds_request_bts(struct task_struct *task, void *base, size_t size, void *base, size_t size,
ds_ovfl_callback_t ovfl); bts_ovfl_callback_t ovfl, size_t th);
extern int ds_request_pebs(struct task_struct *task, void *base, size_t size, extern struct pebs_tracer *ds_request_pebs(struct task_struct *task,
ds_ovfl_callback_t ovfl); void *base, size_t size,
pebs_ovfl_callback_t ovfl,
size_t th);
/* /*
* Release BTS or PEBS resources * Release BTS or PEBS resources
...@@ -61,37 +76,34 @@ extern int ds_request_pebs(struct task_struct *task, void *base, size_t size, ...@@ -61,37 +76,34 @@ extern int ds_request_pebs(struct task_struct *task, void *base, size_t size,
* *
* Returns 0 on success; -Eerrno otherwise * Returns 0 on success; -Eerrno otherwise
* *
* task: the task to release resources for; * tracer: the tracer handle returned from ds_request_~()
* NULL to release resources for the current cpu
*/ */
extern int ds_release_bts(struct task_struct *task); extern int ds_release_bts(struct bts_tracer *tracer);
extern int ds_release_pebs(struct task_struct *task); extern int ds_release_pebs(struct pebs_tracer *tracer);
/* /*
* Return the (array) index of the write pointer. * Get the (array) index of the write pointer.
* (assuming an array of BTS/PEBS records) * (assuming an array of BTS/PEBS records)
* *
* Returns -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu * pos (out): will hold the result
* pos (out): if not NULL, will hold the result
*/ */
extern int ds_get_bts_index(struct task_struct *task, size_t *pos); extern int ds_get_bts_index(struct bts_tracer *tracer, size_t *pos);
extern int ds_get_pebs_index(struct task_struct *task, size_t *pos); extern int ds_get_pebs_index(struct pebs_tracer *tracer, size_t *pos);
/* /*
* Return the (array) index one record beyond the end of the array. * Get the (array) index one record beyond the end of the array.
* (assuming an array of BTS/PEBS records) * (assuming an array of BTS/PEBS records)
* *
* Returns -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu * pos (out): will hold the result
* pos (out): if not NULL, will hold the result
*/ */
extern int ds_get_bts_end(struct task_struct *task, size_t *pos); extern int ds_get_bts_end(struct bts_tracer *tracer, size_t *pos);
extern int ds_get_pebs_end(struct task_struct *task, size_t *pos); extern int ds_get_pebs_end(struct pebs_tracer *tracer, size_t *pos);
/* /*
* Provide a pointer to the BTS/PEBS record at parameter index. * Provide a pointer to the BTS/PEBS record at parameter index.
...@@ -102,14 +114,13 @@ extern int ds_get_pebs_end(struct task_struct *task, size_t *pos); ...@@ -102,14 +114,13 @@ extern int ds_get_pebs_end(struct task_struct *task, size_t *pos);
* *
* Returns the size of a single record on success; -Eerrno on error * Returns the size of a single record on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu
* index: the index of the requested record * index: the index of the requested record
* record (out): pointer to the requested record * record (out): pointer to the requested record
*/ */
extern int ds_access_bts(struct task_struct *task, extern int ds_access_bts(struct bts_tracer *tracer,
size_t index, const void **record); size_t index, const void **record);
extern int ds_access_pebs(struct task_struct *task, extern int ds_access_pebs(struct pebs_tracer *tracer,
size_t index, const void **record); size_t index, const void **record);
/* /*
...@@ -129,38 +140,24 @@ extern int ds_access_pebs(struct task_struct *task, ...@@ -129,38 +140,24 @@ extern int ds_access_pebs(struct task_struct *task,
* *
* Returns the number of bytes written or -Eerrno. * Returns the number of bytes written or -Eerrno.
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu
* buffer: the buffer to write * buffer: the buffer to write
* size: the size of the buffer * size: the size of the buffer
*/ */
extern int ds_write_bts(struct task_struct *task, extern int ds_write_bts(struct bts_tracer *tracer,
const void *buffer, size_t size); const void *buffer, size_t size);
extern int ds_write_pebs(struct task_struct *task, extern int ds_write_pebs(struct pebs_tracer *tracer,
const void *buffer, size_t size); const void *buffer, size_t size);
/*
* Same as ds_write_bts/pebs, but omit ownership checks.
*
* This is needed to have some other task than the owner of the
* BTS/PEBS buffer or the parameter task itself write into the
* respective buffer.
*/
extern int ds_unchecked_write_bts(struct task_struct *task,
const void *buffer, size_t size);
extern int ds_unchecked_write_pebs(struct task_struct *task,
const void *buffer, size_t size);
/* /*
* Reset the write pointer of the BTS/PEBS buffer. * Reset the write pointer of the BTS/PEBS buffer.
* *
* Returns 0 on success; -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu
*/ */
extern int ds_reset_bts(struct task_struct *task); extern int ds_reset_bts(struct bts_tracer *tracer);
extern int ds_reset_pebs(struct task_struct *task); extern int ds_reset_pebs(struct pebs_tracer *tracer);
/* /*
* Clear the BTS/PEBS buffer and reset the write pointer. * Clear the BTS/PEBS buffer and reset the write pointer.
...@@ -168,33 +165,30 @@ extern int ds_reset_pebs(struct task_struct *task); ...@@ -168,33 +165,30 @@ extern int ds_reset_pebs(struct task_struct *task);
* *
* Returns 0 on success; -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_~()
* NULL to access the current cpu
*/ */
extern int ds_clear_bts(struct task_struct *task); extern int ds_clear_bts(struct bts_tracer *tracer);
extern int ds_clear_pebs(struct task_struct *task); extern int ds_clear_pebs(struct pebs_tracer *tracer);
/* /*
* Provide the PEBS counter reset value. * Provide the PEBS counter reset value.
* *
* Returns 0 on success; -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_pebs()
* NULL to access the current cpu
* value (out): the counter reset value * value (out): the counter reset value
*/ */
extern int ds_get_pebs_reset(struct task_struct *task, u64 *value); extern int ds_get_pebs_reset(struct pebs_tracer *tracer, u64 *value);
/* /*
* Set the PEBS counter reset value. * Set the PEBS counter reset value.
* *
* Returns 0 on success; -Eerrno on error * Returns 0 on success; -Eerrno on error
* *
* task: the task to access; * tracer: the tracer handle returned from ds_request_pebs()
* NULL to access the current cpu
* value: the new counter reset value * value: the new counter reset value
*/ */
extern int ds_set_pebs_reset(struct task_struct *task, u64 value); extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value);
/* /*
* Initialization * Initialization
...@@ -207,17 +201,13 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *); ...@@ -207,17 +201,13 @@ extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
/* /*
* The DS context - part of struct thread_struct. * The DS context - part of struct thread_struct.
*/ */
#define MAX_SIZEOF_DS (12 * 8)
struct ds_context { struct ds_context {
/* pointer to the DS configuration; goes into MSR_IA32_DS_AREA */ /* pointer to the DS configuration; goes into MSR_IA32_DS_AREA */
unsigned char *ds; unsigned char ds[MAX_SIZEOF_DS];
/* the owner of the BTS and PEBS configuration, respectively */ /* the owner of the BTS and PEBS configuration, respectively */
struct task_struct *owner[2]; struct ds_tracer *owner[2];
/* buffer overflow notification function for BTS and PEBS */
ds_ovfl_callback_t callback[2];
/* the original buffer address */
void *buffer[2];
/* the number of allocated pages for on-request allocated buffers */
unsigned int pages[2];
/* use count */ /* use count */
unsigned long count; unsigned long count;
/* a pointer to the context location inside the thread_struct /* a pointer to the context location inside the thread_struct
......
This diff is collapsed.
...@@ -668,14 +668,14 @@ static int ptrace_bts_read_record(struct task_struct *child, size_t index, ...@@ -668,14 +668,14 @@ static int ptrace_bts_read_record(struct task_struct *child, size_t index,
size_t bts_index, bts_end; size_t bts_index, bts_end;
int error; int error;
error = ds_get_bts_end(child, &bts_end); error = ds_get_bts_end(child->bts, &bts_end);
if (error < 0) if (error < 0)
return error; return error;
if (bts_end <= index) if (bts_end <= index)
return -EINVAL; return -EINVAL;
error = ds_get_bts_index(child, &bts_index); error = ds_get_bts_index(child->bts, &bts_index);
if (error < 0) if (error < 0)
return error; return error;
...@@ -684,7 +684,7 @@ static int ptrace_bts_read_record(struct task_struct *child, size_t index, ...@@ -684,7 +684,7 @@ static int ptrace_bts_read_record(struct task_struct *child, size_t index,
if (bts_end <= bts_index) if (bts_end <= bts_index)
bts_index -= bts_end; bts_index -= bts_end;
error = ds_access_bts(child, bts_index, &bts_record); error = ds_access_bts(child->bts, bts_index, &bts_record);
if (error < 0) if (error < 0)
return error; return error;
...@@ -705,14 +705,14 @@ static int ptrace_bts_drain(struct task_struct *child, ...@@ -705,14 +705,14 @@ static int ptrace_bts_drain(struct task_struct *child,
size_t end, i; size_t end, i;
int error; int error;
error = ds_get_bts_index(child, &end); error = ds_get_bts_index(child->bts, &end);
if (error < 0) if (error < 0)
return error; return error;
if (size < (end * sizeof(struct bts_struct))) if (size < (end * sizeof(struct bts_struct)))
return -EIO; return -EIO;
error = ds_access_bts(child, 0, (const void **)&raw); error = ds_access_bts(child->bts, 0, (const void **)&raw);
if (error < 0) if (error < 0)
return error; return error;
...@@ -723,18 +723,13 @@ static int ptrace_bts_drain(struct task_struct *child, ...@@ -723,18 +723,13 @@ static int ptrace_bts_drain(struct task_struct *child,
return -EFAULT; return -EFAULT;
} }
error = ds_clear_bts(child); error = ds_clear_bts(child->bts);
if (error < 0) if (error < 0)
return error; return error;
return end; return end;
} }
static void ptrace_bts_ovfl(struct task_struct *child)
{
send_sig(child->thread.bts_ovfl_signal, child, 0);
}
static int ptrace_bts_config(struct task_struct *child, static int ptrace_bts_config(struct task_struct *child,
long cfg_size, long cfg_size,
const struct ptrace_bts_config __user *ucfg) const struct ptrace_bts_config __user *ucfg)
...@@ -760,23 +755,29 @@ static int ptrace_bts_config(struct task_struct *child, ...@@ -760,23 +755,29 @@ static int ptrace_bts_config(struct task_struct *child,
goto errout; goto errout;
if (cfg.flags & PTRACE_BTS_O_ALLOC) { if (cfg.flags & PTRACE_BTS_O_ALLOC) {
ds_ovfl_callback_t ovfl = NULL; bts_ovfl_callback_t ovfl = NULL;
unsigned int sig = 0; unsigned int sig = 0;
/* we ignore the error in case we were not tracing child */
(void)ds_release_bts(child);
if (cfg.flags & PTRACE_BTS_O_SIGNAL) { if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
if (!cfg.signal) if (!cfg.signal)
goto errout; goto errout;
error = -EOPNOTSUPP;
goto errout;
sig = cfg.signal; sig = cfg.signal;
ovfl = ptrace_bts_ovfl;
} }
error = ds_request_bts(child, /* base = */ NULL, cfg.size, ovfl); if (child->bts)
if (error < 0) (void)ds_release_bts(child->bts);
child->bts = ds_request_bts(child, /* base = */ NULL, cfg.size,
ovfl, /* th = */ (size_t)-1);
if (IS_ERR(child->bts)) {
error = PTR_ERR(child->bts);
child->bts = NULL;
goto errout; goto errout;
}
child->thread.bts_ovfl_signal = sig; child->thread.bts_ovfl_signal = sig;
} }
...@@ -823,15 +824,15 @@ static int ptrace_bts_status(struct task_struct *child, ...@@ -823,15 +824,15 @@ static int ptrace_bts_status(struct task_struct *child,
if (cfg_size < sizeof(cfg)) if (cfg_size < sizeof(cfg))
return -EIO; return -EIO;
error = ds_get_bts_end(child, &end); error = ds_get_bts_end(child->bts, &end);
if (error < 0) if (error < 0)
return error; return error;
error = ds_access_bts(child, /* index = */ 0, &base); error = ds_access_bts(child->bts, /* index = */ 0, &base);
if (error < 0) if (error < 0)
return error; return error;
error = ds_access_bts(child, /* index = */ end, &max); error = ds_access_bts(child->bts, /* index = */ end, &max);
if (error < 0) if (error < 0)
return error; return error;
...@@ -884,10 +885,7 @@ static int ptrace_bts_write_record(struct task_struct *child, ...@@ -884,10 +885,7 @@ static int ptrace_bts_write_record(struct task_struct *child,
return -EINVAL; return -EINVAL;
} }
/* The writing task will be the switched-to task on a context return ds_write_bts(child->bts, bts_record, bts_cfg.sizeof_bts);
* switch. It needs to write into the switched-from task's BTS
* buffer. */
return ds_unchecked_write_bts(child, bts_record, bts_cfg.sizeof_bts);
} }
void ptrace_bts_take_timestamp(struct task_struct *tsk, void ptrace_bts_take_timestamp(struct task_struct *tsk,
...@@ -972,13 +970,15 @@ void ptrace_disable(struct task_struct *child) ...@@ -972,13 +970,15 @@ void ptrace_disable(struct task_struct *child)
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif #endif
#ifdef CONFIG_X86_PTRACE_BTS #ifdef CONFIG_X86_PTRACE_BTS
(void)ds_release_bts(child); if (child->bts) {
(void)ds_release_bts(child->bts);
child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask;
if (!child->thread.debugctlmsr) if (!child->thread.debugctlmsr)
clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
}
#endif /* CONFIG_X86_PTRACE_BTS */ #endif /* CONFIG_X86_PTRACE_BTS */
} }
...@@ -1110,9 +1110,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -1110,9 +1110,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
(child, data, (struct ptrace_bts_config __user *)addr); (child, data, (struct ptrace_bts_config __user *)addr);
break; break;
case PTRACE_BTS_SIZE: case PTRACE_BTS_SIZE: {
ret = ds_get_bts_index(child, /* pos = */ NULL); size_t size;
ret = ds_get_bts_index(child->bts, &size);
if (ret == 0) {
BUG_ON(size != (int) size);
ret = (int) size;
}
break; break;
}
case PTRACE_BTS_GET: case PTRACE_BTS_GET:
ret = ptrace_bts_read_record ret = ptrace_bts_read_record
...@@ -1120,7 +1127,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -1120,7 +1127,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break; break;
case PTRACE_BTS_CLEAR: case PTRACE_BTS_CLEAR:
ret = ds_clear_bts(child); ret = ds_clear_bts(child->bts);
break; break;
case PTRACE_BTS_DRAIN: case PTRACE_BTS_DRAIN:
......
...@@ -96,6 +96,7 @@ struct exec_domain; ...@@ -96,6 +96,7 @@ struct exec_domain;
struct futex_pi_state; struct futex_pi_state;
struct robust_list_head; struct robust_list_head;
struct bio; struct bio;
struct bts_tracer;
/* /*
* List of flags we want to share for kernel threads, * List of flags we want to share for kernel threads,
...@@ -1161,6 +1162,14 @@ struct task_struct { ...@@ -1161,6 +1162,14 @@ struct task_struct {
struct list_head ptraced; struct list_head ptraced;
struct list_head ptrace_entry; struct list_head ptrace_entry;
#ifdef CONFIG_X86_PTRACE_BTS
/*
* This is the tracer handle for the ptrace BTS extension.
* This field actually belongs to the ptracer task.
*/
struct bts_tracer *bts;
#endif /* CONFIG_X86_PTRACE_BTS */
/* PID/PID hash table linkage. */ /* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX]; struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group; struct list_head thread_group;
......
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