diff options
author | Jacob Shin <jacob.w.shin@gmail.com> | 2014-05-29 11:26:50 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2014-12-03 09:14:26 -0500 |
commit | d6d55f0b9d900673548515614b56ab55aa2c51f8 (patch) | |
tree | 2c5d9bdff4444fec201a014e071e718a0f34d8aa | |
parent | 4e6e311e596eadba30d4f56f64eae7d45611a01c (diff) |
perf/x86/amd: AMD support for bp_len > HW_BREAKPOINT_LEN_8
Implement hardware breakpoint address mask for AMD Family 16h and
above processors. CPUID feature bit indicates hardware support for
DRn_ADDR_MASK MSRs. These masks further qualify DRn/DR7 hardware
breakpoint addresses to allow matching of larger addresses ranges.
Valuable advice and pseudo code from Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Jacob Shin <jacob.w.shin@gmail.com>
Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: xiakaixu <xiakaixu@huawei.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
-rw-r--r-- | arch/x86/include/asm/cpufeature.h | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/debugreg.h | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/hw_breakpoint.h | 1 | ||||
-rw-r--r-- | arch/x86/include/uapi/asm/msr-index.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/amd.c | 19 | ||||
-rw-r--r-- | arch/x86/kernel/hw_breakpoint.c | 20 |
6 files changed, 47 insertions, 4 deletions
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 0bb1335313b2..53966d65591e 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
@@ -174,6 +174,7 @@ | |||
174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ | 174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ |
175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ | 175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ |
176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ | 176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ |
177 | #define X86_FEATURE_BPEXT (6*32+26) /* data breakpoint extension */ | ||
177 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ | 178 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ |
178 | 179 | ||
179 | /* | 180 | /* |
@@ -383,6 +384,7 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; | |||
383 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) | 384 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) |
384 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) | 385 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) |
385 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) | 386 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) |
387 | #define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT) | ||
386 | 388 | ||
387 | #if __GNUC__ >= 4 | 389 | #if __GNUC__ >= 4 |
388 | extern void warn_pre_alternatives(void); | 390 | extern void warn_pre_alternatives(void); |
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 61fd18b83b6c..12cb66f6d3a5 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h | |||
@@ -114,5 +114,10 @@ static inline void debug_stack_usage_inc(void) { } | |||
114 | static inline void debug_stack_usage_dec(void) { } | 114 | static inline void debug_stack_usage_dec(void) { } |
115 | #endif /* X86_64 */ | 115 | #endif /* X86_64 */ |
116 | 116 | ||
117 | #ifdef CONFIG_CPU_SUP_AMD | ||
118 | extern void set_dr_addr_mask(unsigned long mask, int dr); | ||
119 | #else | ||
120 | static inline void set_dr_addr_mask(unsigned long mask, int dr) { } | ||
121 | #endif | ||
117 | 122 | ||
118 | #endif /* _ASM_X86_DEBUGREG_H */ | 123 | #endif /* _ASM_X86_DEBUGREG_H */ |
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index ef1c4d2d41ec..6c98be864a75 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h | |||
@@ -12,6 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | struct arch_hw_breakpoint { | 13 | struct arch_hw_breakpoint { |
14 | unsigned long address; | 14 | unsigned long address; |
15 | unsigned long mask; | ||
15 | u8 len; | 16 | u8 len; |
16 | u8 type; | 17 | u8 type; |
17 | }; | 18 | }; |
diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index 8f02f6990759..b1fb4fae03d3 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h | |||
@@ -212,6 +212,10 @@ | |||
212 | /* Fam 16h MSRs */ | 212 | /* Fam 16h MSRs */ |
213 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 | 213 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 |
214 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 | 214 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 |
215 | #define MSR_F16H_DR1_ADDR_MASK 0xc0011019 | ||
216 | #define MSR_F16H_DR2_ADDR_MASK 0xc001101a | ||
217 | #define MSR_F16H_DR3_ADDR_MASK 0xc001101b | ||
218 | #define MSR_F16H_DR0_ADDR_MASK 0xc0011027 | ||
215 | 219 | ||
216 | /* Fam 15h MSRs */ | 220 | /* Fam 15h MSRs */ |
217 | #define MSR_F15H_PERF_CTL 0xc0010200 | 221 | #define MSR_F15H_PERF_CTL 0xc0010200 |
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 813d29d00a17..abe4ec760db3 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
@@ -870,3 +870,22 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) | |||
870 | 870 | ||
871 | return false; | 871 | return false; |
872 | } | 872 | } |
873 | |||
874 | void set_dr_addr_mask(unsigned long mask, int dr) | ||
875 | { | ||
876 | if (!cpu_has_bpext) | ||
877 | return; | ||
878 | |||
879 | switch (dr) { | ||
880 | case 0: | ||
881 | wrmsr(MSR_F16H_DR0_ADDR_MASK, mask, 0); | ||
882 | break; | ||
883 | case 1: | ||
884 | case 2: | ||
885 | case 3: | ||
886 | wrmsr(MSR_F16H_DR1_ADDR_MASK - 1 + dr, mask, 0); | ||
887 | break; | ||
888 | default: | ||
889 | break; | ||
890 | } | ||
891 | } | ||
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 3d5fb509bdeb..b5cb0c59ea87 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c | |||
@@ -126,6 +126,8 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
126 | *dr7 |= encode_dr7(i, info->len, info->type); | 126 | *dr7 |= encode_dr7(i, info->len, info->type); |
127 | 127 | ||
128 | set_debugreg(*dr7, 7); | 128 | set_debugreg(*dr7, 7); |
129 | if (info->mask) | ||
130 | set_dr_addr_mask(info->mask, i); | ||
129 | 131 | ||
130 | return 0; | 132 | return 0; |
131 | } | 133 | } |
@@ -161,6 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | |||
161 | *dr7 &= ~__encode_dr7(i, info->len, info->type); | 163 | *dr7 &= ~__encode_dr7(i, info->len, info->type); |
162 | 164 | ||
163 | set_debugreg(*dr7, 7); | 165 | set_debugreg(*dr7, 7); |
166 | if (info->mask) | ||
167 | set_dr_addr_mask(0, i); | ||
164 | } | 168 | } |
165 | 169 | ||
166 | static int get_hbp_len(u8 hbp_len) | 170 | static int get_hbp_len(u8 hbp_len) |
@@ -277,6 +281,8 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
277 | } | 281 | } |
278 | 282 | ||
279 | /* Len */ | 283 | /* Len */ |
284 | info->mask = 0; | ||
285 | |||
280 | switch (bp->attr.bp_len) { | 286 | switch (bp->attr.bp_len) { |
281 | case HW_BREAKPOINT_LEN_1: | 287 | case HW_BREAKPOINT_LEN_1: |
282 | info->len = X86_BREAKPOINT_LEN_1; | 288 | info->len = X86_BREAKPOINT_LEN_1; |
@@ -293,11 +299,17 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
293 | break; | 299 | break; |
294 | #endif | 300 | #endif |
295 | default: | 301 | default: |
296 | return -EINVAL; | 302 | if (!is_power_of_2(bp->attr.bp_len)) |
303 | return -EINVAL; | ||
304 | if (!cpu_has_bpext) | ||
305 | return -EOPNOTSUPP; | ||
306 | info->mask = bp->attr.bp_len - 1; | ||
307 | info->len = X86_BREAKPOINT_LEN_1; | ||
297 | } | 308 | } |
298 | 309 | ||
299 | return 0; | 310 | return 0; |
300 | } | 311 | } |
312 | |||
301 | /* | 313 | /* |
302 | * Validate the arch-specific HW Breakpoint register settings | 314 | * Validate the arch-specific HW Breakpoint register settings |
303 | */ | 315 | */ |
@@ -312,11 +324,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
312 | if (ret) | 324 | if (ret) |
313 | return ret; | 325 | return ret; |
314 | 326 | ||
315 | ret = -EINVAL; | ||
316 | |||
317 | switch (info->len) { | 327 | switch (info->len) { |
318 | case X86_BREAKPOINT_LEN_1: | 328 | case X86_BREAKPOINT_LEN_1: |
319 | align = 0; | 329 | align = 0; |
330 | if (info->mask) | ||
331 | align = info->mask; | ||
320 | break; | 332 | break; |
321 | case X86_BREAKPOINT_LEN_2: | 333 | case X86_BREAKPOINT_LEN_2: |
322 | align = 1; | 334 | align = 1; |
@@ -330,7 +342,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
330 | break; | 342 | break; |
331 | #endif | 343 | #endif |
332 | default: | 344 | default: |
333 | return ret; | 345 | WARN_ON_ONCE(1); |
334 | } | 346 | } |
335 | 347 | ||
336 | /* | 348 | /* |