Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  perf tools: Makefile: Use gcc to determine ARCH
  perf events, x86: Fix Intel Nehalem and Westmere last level cache event definitions
  hw_breakpoints, powerpc: Fix CONFIG_HAVE_HW_BREAKPOINT off-case in ptrace_set_debugreg()
  sh, hw_breakpoints: Fix racy access to ptrace breakpoints
  arm, hw_breakpoints: Fix racy access to ptrace breakpoints
  powerpc, hw_breakpoints: Fix racy access to ptrace breakpoints
  x86, hw_breakpoints: Fix racy access to ptrace breakpoints
  ptrace: Prepare to fix racy accesses on task breakpoints
This commit is contained in:
Linus Torvalds 2011-05-07 13:17:37 -07:00
commit 8b061610da
10 changed files with 144 additions and 54 deletions

View file

@ -767,12 +767,20 @@ long arch_ptrace(struct task_struct *child, long request,
#ifdef CONFIG_HAVE_HW_BREAKPOINT #ifdef CONFIG_HAVE_HW_BREAKPOINT
case PTRACE_GETHBPREGS: case PTRACE_GETHBPREGS:
if (ptrace_get_breakpoints(child) < 0)
return -ESRCH;
ret = ptrace_gethbpregs(child, addr, ret = ptrace_gethbpregs(child, addr,
(unsigned long __user *)data); (unsigned long __user *)data);
ptrace_put_breakpoints(child);
break; break;
case PTRACE_SETHBPREGS: case PTRACE_SETHBPREGS:
if (ptrace_get_breakpoints(child) < 0)
return -ESRCH;
ret = ptrace_sethbpregs(child, addr, ret = ptrace_sethbpregs(child, addr,
(unsigned long __user *)data); (unsigned long __user *)data);
ptrace_put_breakpoints(child);
break; break;
#endif #endif

View file

@ -933,12 +933,16 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
if (data && !(data & DABR_TRANSLATION)) if (data && !(data & DABR_TRANSLATION))
return -EIO; return -EIO;
#ifdef CONFIG_HAVE_HW_BREAKPOINT #ifdef CONFIG_HAVE_HW_BREAKPOINT
if (ptrace_get_breakpoints(task) < 0)
return -ESRCH;
bp = thread->ptrace_bps[0]; bp = thread->ptrace_bps[0];
if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) { if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) {
if (bp) { if (bp) {
unregister_hw_breakpoint(bp); unregister_hw_breakpoint(bp);
thread->ptrace_bps[0] = NULL; thread->ptrace_bps[0] = NULL;
} }
ptrace_put_breakpoints(task);
return 0; return 0;
} }
if (bp) { if (bp) {
@ -948,9 +952,12 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
(DABR_DATA_WRITE | DABR_DATA_READ), (DABR_DATA_WRITE | DABR_DATA_READ),
&attr.bp_type); &attr.bp_type);
ret = modify_user_hw_breakpoint(bp, &attr); ret = modify_user_hw_breakpoint(bp, &attr);
if (ret) if (ret) {
ptrace_put_breakpoints(task);
return ret; return ret;
}
thread->ptrace_bps[0] = bp; thread->ptrace_bps[0] = bp;
ptrace_put_breakpoints(task);
thread->dabr = data; thread->dabr = data;
return 0; return 0;
} }
@ -965,9 +972,12 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr,
ptrace_triggered, task); ptrace_triggered, task);
if (IS_ERR(bp)) { if (IS_ERR(bp)) {
thread->ptrace_bps[0] = NULL; thread->ptrace_bps[0] = NULL;
ptrace_put_breakpoints(task);
return PTR_ERR(bp); return PTR_ERR(bp);
} }
ptrace_put_breakpoints(task);
#endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif /* CONFIG_HAVE_HW_BREAKPOINT */
/* Move contents to the DABR register */ /* Move contents to the DABR register */

View file

@ -117,7 +117,11 @@ void user_enable_single_step(struct task_struct *child)
set_tsk_thread_flag(child, TIF_SINGLESTEP); set_tsk_thread_flag(child, TIF_SINGLESTEP);
if (ptrace_get_breakpoints(child) < 0)
return;
set_single_step(child, pc); set_single_step(child, pc);
ptrace_put_breakpoints(child);
} }
void user_disable_single_step(struct task_struct *child) void user_disable_single_step(struct task_struct *child)

View file

@ -184,26 +184,23 @@ static __initconst const u64 snb_hw_cache_event_ids
}, },
}, },
[ C(LL ) ] = { [ C(LL ) ] = {
/*
* TBD: Need Off-core Response Performance Monitoring support
*/
[ C(OP_READ) ] = { [ C(OP_READ) ] = {
/* OFFCORE_RESPONSE_0.ANY_DATA.LOCAL_CACHE */ /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_1.ANY_DATA.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01bb, [ C(RESULT_MISS) ] = 0x01b7,
}, },
[ C(OP_WRITE) ] = { [ C(OP_WRITE) ] = {
/* OFFCORE_RESPONSE_0.ANY_RFO.LOCAL_CACHE */ /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_1.ANY_RFO.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01bb, [ C(RESULT_MISS) ] = 0x01b7,
}, },
[ C(OP_PREFETCH) ] = { [ C(OP_PREFETCH) ] = {
/* OFFCORE_RESPONSE_0.PREFETCH.LOCAL_CACHE */ /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_1.PREFETCH.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01bb, [ C(RESULT_MISS) ] = 0x01b7,
}, },
}, },
[ C(DTLB) ] = { [ C(DTLB) ] = {
@ -285,26 +282,26 @@ static __initconst const u64 westmere_hw_cache_event_ids
}, },
[ C(LL ) ] = { [ C(LL ) ] = {
[ C(OP_READ) ] = { [ C(OP_READ) ] = {
/* OFFCORE_RESPONSE_0.ANY_DATA.LOCAL_CACHE */ /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_1.ANY_DATA.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01bb, [ C(RESULT_MISS) ] = 0x01b7,
}, },
/* /*
* Use RFO, not WRITEBACK, because a write miss would typically occur * Use RFO, not WRITEBACK, because a write miss would typically occur
* on RFO. * on RFO.
*/ */
[ C(OP_WRITE) ] = { [ C(OP_WRITE) ] = {
/* OFFCORE_RESPONSE_1.ANY_RFO.LOCAL_CACHE */ /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01bb, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_0.ANY_RFO.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01b7, [ C(RESULT_MISS) ] = 0x01b7,
}, },
[ C(OP_PREFETCH) ] = { [ C(OP_PREFETCH) ] = {
/* OFFCORE_RESPONSE_0.PREFETCH.LOCAL_CACHE */ /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */
[ C(RESULT_ACCESS) ] = 0x01b7, [ C(RESULT_ACCESS) ] = 0x01b7,
/* OFFCORE_RESPONSE_1.PREFETCH.ANY_LLC_MISS */ /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */
[ C(RESULT_MISS) ] = 0x01bb, [ C(RESULT_MISS) ] = 0x01b7,
}, },
}, },
[ C(DTLB) ] = { [ C(DTLB) ] = {
@ -352,16 +349,36 @@ static __initconst const u64 westmere_hw_cache_event_ids
}; };
/* /*
* OFFCORE_RESPONSE MSR bits (subset), See IA32 SDM Vol 3 30.6.1.3 * Nehalem/Westmere MSR_OFFCORE_RESPONSE bits;
* See IA32 SDM Vol 3B 30.6.1.3
*/ */
#define DMND_DATA_RD (1 << 0) #define NHM_DMND_DATA_RD (1 << 0)
#define DMND_RFO (1 << 1) #define NHM_DMND_RFO (1 << 1)
#define DMND_WB (1 << 3) #define NHM_DMND_IFETCH (1 << 2)
#define PF_DATA_RD (1 << 4) #define NHM_DMND_WB (1 << 3)
#define PF_DATA_RFO (1 << 5) #define NHM_PF_DATA_RD (1 << 4)
#define RESP_UNCORE_HIT (1 << 8) #define NHM_PF_DATA_RFO (1 << 5)
#define RESP_MISS (0xf600) /* non uncore hit */ #define NHM_PF_IFETCH (1 << 6)
#define NHM_OFFCORE_OTHER (1 << 7)
#define NHM_UNCORE_HIT (1 << 8)
#define NHM_OTHER_CORE_HIT_SNP (1 << 9)
#define NHM_OTHER_CORE_HITM (1 << 10)
/* reserved */
#define NHM_REMOTE_CACHE_FWD (1 << 12)
#define NHM_REMOTE_DRAM (1 << 13)
#define NHM_LOCAL_DRAM (1 << 14)
#define NHM_NON_DRAM (1 << 15)
#define NHM_ALL_DRAM (NHM_REMOTE_DRAM|NHM_LOCAL_DRAM)
#define NHM_DMND_READ (NHM_DMND_DATA_RD)
#define NHM_DMND_WRITE (NHM_DMND_RFO|NHM_DMND_WB)
#define NHM_DMND_PREFETCH (NHM_PF_DATA_RD|NHM_PF_DATA_RFO)
#define NHM_L3_HIT (NHM_UNCORE_HIT|NHM_OTHER_CORE_HIT_SNP|NHM_OTHER_CORE_HITM)
#define NHM_L3_MISS (NHM_NON_DRAM|NHM_ALL_DRAM|NHM_REMOTE_CACHE_FWD)
#define NHM_L3_ACCESS (NHM_L3_HIT|NHM_L3_MISS)
static __initconst const u64 nehalem_hw_cache_extra_regs static __initconst const u64 nehalem_hw_cache_extra_regs
[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_MAX]
@ -370,16 +387,16 @@ static __initconst const u64 nehalem_hw_cache_extra_regs
{ {
[ C(LL ) ] = { [ C(LL ) ] = {
[ C(OP_READ) ] = { [ C(OP_READ) ] = {
[ C(RESULT_ACCESS) ] = DMND_DATA_RD|RESP_UNCORE_HIT, [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_L3_ACCESS,
[ C(RESULT_MISS) ] = DMND_DATA_RD|RESP_MISS, [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_L3_MISS,
}, },
[ C(OP_WRITE) ] = { [ C(OP_WRITE) ] = {
[ C(RESULT_ACCESS) ] = DMND_RFO|DMND_WB|RESP_UNCORE_HIT, [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_L3_ACCESS,
[ C(RESULT_MISS) ] = DMND_RFO|DMND_WB|RESP_MISS, [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_L3_MISS,
}, },
[ C(OP_PREFETCH) ] = { [ C(OP_PREFETCH) ] = {
[ C(RESULT_ACCESS) ] = PF_DATA_RD|PF_DATA_RFO|RESP_UNCORE_HIT, [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_L3_ACCESS,
[ C(RESULT_MISS) ] = PF_DATA_RD|PF_DATA_RFO|RESP_MISS, [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_L3_MISS,
}, },
} }
}; };

View file

@ -608,6 +608,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
unsigned len, type; unsigned len, type;
struct perf_event *bp; struct perf_event *bp;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
data &= ~DR_CONTROL_RESERVED; data &= ~DR_CONTROL_RESERVED;
old_dr7 = ptrace_get_dr7(thread->ptrace_bps); old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
restore: restore:
@ -655,6 +658,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
} }
goto restore; goto restore;
} }
ptrace_put_breakpoints(tsk);
return ((orig_ret < 0) ? orig_ret : rc); return ((orig_ret < 0) ? orig_ret : rc);
} }
@ -668,10 +674,17 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
if (n < HBP_NUM) { if (n < HBP_NUM) {
struct perf_event *bp; struct perf_event *bp;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
bp = thread->ptrace_bps[n]; bp = thread->ptrace_bps[n];
if (!bp) if (!bp)
return 0; val = 0;
val = bp->hw.info.address; else
val = bp->hw.info.address;
ptrace_put_breakpoints(tsk);
} else if (n == 6) { } else if (n == 6) {
val = thread->debugreg6; val = thread->debugreg6;
} else if (n == 7) { } else if (n == 7) {
@ -686,6 +699,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
struct perf_event *bp; struct perf_event *bp;
struct thread_struct *t = &tsk->thread; struct thread_struct *t = &tsk->thread;
struct perf_event_attr attr; struct perf_event_attr attr;
int err = 0;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
if (!t->ptrace_bps[nr]) { if (!t->ptrace_bps[nr]) {
ptrace_breakpoint_init(&attr); ptrace_breakpoint_init(&attr);
@ -709,24 +726,23 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
* writing for the user. And anyway this is the previous * writing for the user. And anyway this is the previous
* behaviour. * behaviour.
*/ */
if (IS_ERR(bp)) if (IS_ERR(bp)) {
return PTR_ERR(bp); err = PTR_ERR(bp);
goto put;
}
t->ptrace_bps[nr] = bp; t->ptrace_bps[nr] = bp;
} else { } else {
int err;
bp = t->ptrace_bps[nr]; bp = t->ptrace_bps[nr];
attr = bp->attr; attr = bp->attr;
attr.bp_addr = addr; attr.bp_addr = addr;
err = modify_user_hw_breakpoint(bp, &attr); err = modify_user_hw_breakpoint(bp, &attr);
if (err)
return err;
} }
put:
return 0; ptrace_put_breakpoints(tsk);
return err;
} }
/* /*

View file

@ -189,6 +189,10 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
child->ptrace = current->ptrace; child->ptrace = current->ptrace;
__ptrace_link(child, current->parent); __ptrace_link(child, current->parent);
} }
#ifdef CONFIG_HAVE_HW_BREAKPOINT
atomic_set(&child->ptrace_bp_refcnt, 1);
#endif
} }
/** /**
@ -350,6 +354,13 @@ 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);
#endif #ifdef CONFIG_HAVE_HW_BREAKPOINT
extern int ptrace_get_breakpoints(struct task_struct *tsk);
extern void ptrace_put_breakpoints(struct task_struct *tsk);
#else
static inline void ptrace_put_breakpoints(struct task_struct *tsk) { }
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
#endif /* __KERNEL */
#endif #endif

View file

@ -1537,6 +1537,9 @@ struct task_struct {
unsigned long memsw_nr_pages; /* uncharged mem+swap usage */ unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
} memcg_batch; } memcg_batch;
#endif #endif
#ifdef CONFIG_HAVE_HW_BREAKPOINT
atomic_t ptrace_bp_refcnt;
#endif
}; };
/* Future-safe accessor for struct task_struct's cpus_allowed. */ /* Future-safe accessor for struct task_struct's cpus_allowed. */

View file

@ -1016,7 +1016,7 @@ NORET_TYPE void do_exit(long code)
/* /*
* FIXME: do that only when needed, using sched_exit tracepoint * FIXME: do that only when needed, using sched_exit tracepoint
*/ */
flush_ptrace_hw_breakpoint(tsk); ptrace_put_breakpoints(tsk);
exit_notify(tsk, group_dead); exit_notify(tsk, group_dead);
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA

View file

@ -22,6 +22,7 @@
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/regset.h> #include <linux/regset.h>
#include <linux/hw_breakpoint.h>
/* /*
@ -879,3 +880,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
return ret; return ret;
} }
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
#ifdef CONFIG_HAVE_HW_BREAKPOINT
int ptrace_get_breakpoints(struct task_struct *tsk)
{
if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt))
return 0;
return -1;
}
void ptrace_put_breakpoints(struct task_struct *tsk)
{
if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt))
flush_ptrace_hw_breakpoint(tsk);
}
#endif /* CONFIG_HAVE_HW_BREAKPOINT */

View file

@ -35,15 +35,21 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ ) -e s/sh[234].*/sh/ )
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
# Additional ARCH settings for x86 # Additional ARCH settings for x86
ifeq ($(ARCH),i386) ifeq ($(ARCH),i386)
ARCH := x86 ARCH := x86
endif endif
ifeq ($(ARCH),x86_64) ifeq ($(ARCH),x86_64)
RAW_ARCH := x86_64 ARCH := x86
ARCH := x86 IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1)
ARCH_CFLAGS := -DARCH_X86_64 ifeq (${IS_X86_64}, 1)
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S RAW_ARCH := x86_64
ARCH_CFLAGS := -DARCH_X86_64
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S
endif
endif endif
# #
@ -119,8 +125,6 @@ lib = lib
export prefix bindir sharedir sysconfdir export prefix bindir sharedir sysconfdir
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
RM = rm -f RM = rm -f
MKDIR = mkdir MKDIR = mkdir
FIND = find FIND = find