perf, x86: Implement AMD IBS
Subsystems (2)
v4 cover perf, x86: Implement AMD IBS
This patch set adds support for AMD IBS to perf. It is a new implementation and unrelated to my previous postings last year. The main differences are:
* separate and independent from x86 perfctrs, IBS could be used without the x86 pmu,
* using dynamic pmu allocation, userspace uses sysfs to select the pmu,
* support for 64 bit counters,
* libperf based example code,
* shared IBS initialziation code for perf and oprofile.
The approach is still to collect raw sample data which should be the most important use case for application developers. The data format is the same as described in the IBS register specification.
Future work could be:
* better integration into the perf tool, use IBS for generic events where possible,
* support of the precise event sampling perf i/f,
* implementation of extended IBS features (e.g. ext. counter width),
* support of counting (perf stat),
* in-kernel IBS event parsing,
* IBS tracepoint support.
Changes for V2:
* Remove printks in pmu function stubs.
* Modify perf_event_ibs_init() to use ibs_caps directly.
* Added bit mask for msr offsets.
* Added caps field to raw sample format.
* Added caps check for IBS_OP_CUR_CNT emulation.
* Updated include header files to fix build errors on some distros.
* Note: I kept example code for reference, the patch must not be applied. I will come up with a sulution that integrates IBS into perf-report.
Changes for V3 (only patch #3 modified):
* rebased on Don's NMI V5 patch set
* disable per-task monitoring (mark pmu with perf_invalid_context), per-task monitoring can be added in a separate patch
Changes for V4 (only patch #3 modified):
* removed userland implementation from patch set, will post it as separate patch series
* fix returned number of handled nmis in perf_ibs_handle_irq()
-Robert
Robert Richter (4):
perf, x86: Implement IBS event configuration perf, x86: Implement IBS interrupt handler perf, x86: Implement IBS pmu control ops perf, x86: Implement 64 bit counter support for IBS
arch/x86/include/asm/msr-index.h | 5 + arch/x86/include/asm/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_amd_ibs.c | 438 +++++++++++++++++++++++++++++- 3 files changed, 438 insertions(+), 7 deletions(-)
Signature · 1 line
1.7.7
PATCH v4 1/4 perf, x86: Implement IBS event configuration
This patch implements perf configuration for AMD IBS. The IBS pmu is selected using the type attribute in sysfs. There are two types of ibs pmus, for instruction fetch (IBS_FETCH) and for instruction execution (IBS_OP):
/sys/bus/event_source/devices/ibs_fetch/type
/sys/bus/event_source/devices/ibs_op/type
Except for the sample period IBS can only be set up with raw config values and raw data samples. The event attributes for the syscall should be programmed like this (IBS_FETCH):
type = get_pmu_type("/sys/bus/event_source/devices/ibs_fetch/type");
memset(&attr, 0, sizeof(attr));
attr.type = type;
attr.sample_type = PERF_SAMPLE_CPU | PERF_SAMPLE_RAW;
attr.config = IBS_FETCH_CONFIG_DEFAULT;This implementation does not yet support 64 bit counters. It is limited to the hardware counter bit width which is 20 bits. 64 bit support can be added later.
V3:
* disable per-task monitoring (mark pmu with perf_invalid_context), per-task monitoring can be added in a separate patch
--- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 92 +++++++++++++++++++++++++++-- 1 files changed, 85 insertions(+), 7 deletions(-)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +85 -7
@@ -16,12 +16,67 @@ static u32 ibs_caps;#if defined(CONFIG_PERF_EVENTS ) && defined(CONFIG_CPU_SUP_AMD ) -static struct pmu perf_ibs; +#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) +#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT + +struct perf_ibs { +struct pmu pmu; +unsigned int msr; +u64 config_mask; +u64 cnt_mask; +u64 enable_mask; +}; + +static struct perf_ibs perf_ibs_fetch; +static struct perf_ibs perf_ibs_op; + +static struct perf_ibs *get_ibs_pmu (int type) +{ +if (perf_ibs_fetch.pmu.type == type) +return &perf_ibs_fetch; +if (perf_ibs_op.pmu.type == type) +return &perf_ibs_op; +return NULL ; +}static int perf_ibs_init (struct perf_event *event) { -if (perf_ibs.type != event->attr.type) +struct hw_perf_event *hwc = &event->hw; +struct perf_ibs *perf_ibs; +u64 max_cnt, config; + + perf_ibs =get_ibs_pmu (event->attr.type); +if (!perf_ibs)return -ENOENT ; + + config = event->attr.config; +if (config & ~perf_ibs->config_mask) +return -EINVAL ; + +if (hwc->sample_period) { +if (config & perf_ibs->cnt_mask) +/* raw max_cnt may not be set */ +return -EINVAL ; +if (hwc->sample_period &0x0f ) +/* lower 4 bits can not be set in ibs max cnt */ +return -EINVAL ; + max_cnt = hwc->sample_period >>4 ; +if (max_cnt & ~perf_ibs->cnt_mask) +/* out of range */ +return -EINVAL ; + config |= max_cnt; + }else { + max_cnt = config & perf_ibs->cnt_mask; + event->attr.sample_period = max_cnt <<4 ; + hwc->sample_period = event->attr.sample_period; + } + +if (!max_cnt) +return -EINVAL ; + + hwc->config_base = perf_ibs->msr; + hwc->config = config; +return 0 ; }
@@ -34,10 +89,32 @@ static void perf_ibs_del(struct perf_event *event, int flags) { } -static struct pmu perf_ibs = { - .event_init= perf_ibs_init, - .add= perf_ibs_add, - .del= perf_ibs_del, +static struct perf_ibs perf_ibs_fetch = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr =MSR_AMD64_IBSFETCHCTL , + .config_mask =IBS_FETCH_CONFIG_MASK , + .cnt_mask =IBS_FETCH_MAX_CNT , + .enable_mask =IBS_FETCH_ENABLE , +}; + +static struct perf_ibs perf_ibs_op = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr =MSR_AMD64_IBSOPCTL , + .config_mask =IBS_OP_CONFIG_MASK , + .cnt_mask =IBS_OP_MAX_CNT , + .enable_mask =IBS_OP_ENABLE , };static __init int perf_event_ibs_init (void )
@@ -45,7 +122,8 @@ static __init int perf_event_ibs_init(void)if (!ibs_caps)return -ENODEV ;/* ibs not supported by the cpu */ -perf_pmu_register (&perf_ibs,"ibs" ,-1 ); +perf_pmu_register (&perf_ibs_fetch.pmu,"ibs_fetch" ,-1 ); +perf_pmu_register (&perf_ibs_op.pmu,"ibs_op" ,-1 );printk (KERN_INFO "perf: AMD IBS detected (0x%08x)\n" , ibs_caps);return 0 ;
Signature · 1 line
1.7.7
[tip:perf/x86-ibs] perf/x86: Implement IBS event configuration
Commit-ID: 510419435c6948fb32959d691bf84eaba41ca474 Gitweb: http://git.kernel.org/tip/510419435c6948fb32959d691bf84eaba41ca474 Author: Robert Richter <[email protected]> AuthorDate: Thu, 15 Dec 2011 17:56:36 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 8 Mar 2012 11:35:21 +0100
perf/x86: Implement IBS event configuration
This patch implements perf configuration for AMD IBS. The IBS pmu is selected using the type attribute in sysfs. There are two types of ibs pmus, for instruction fetch (IBS_FETCH) and for instruction execution (IBS_OP):
/sys/bus/event_source/devices/ibs_fetch/type
/sys/bus/event_source/devices/ibs_op/type
Except for the sample period IBS can only be set up with raw config values and raw data samples. The event attributes for the syscall should be programmed like this (IBS_FETCH):
type = get_pmu_type("/sys/bus/event_source/devices/ibs_fetch/type");
memset(&attr, 0, sizeof(attr));
attr.type = type;
attr.sample_type = PERF_SAMPLE_CPU | PERF_SAMPLE_RAW;
attr.config = IBS_FETCH_CONFIG_DEFAULT;This implementation does not yet support 64 bit counters. It is limited to the hardware counter bit width which is 20 bits. 64 bit support can be added later.
--- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 92 +++++++++++++++++++++++++++-- 1 files changed, 85 insertions(+), 7 deletions(-)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +85 -7
@@ -16,12 +16,67 @@ static u32 ibs_caps;#if defined(CONFIG_PERF_EVENTS ) && defined(CONFIG_CPU_SUP_AMD ) -static struct pmu perf_ibs; +#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) +#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT + +struct perf_ibs { +struct pmu pmu; +unsigned int msr; +u64 config_mask; +u64 cnt_mask; +u64 enable_mask; +}; + +static struct perf_ibs perf_ibs_fetch; +static struct perf_ibs perf_ibs_op; + +static struct perf_ibs *get_ibs_pmu (int type) +{ +if (perf_ibs_fetch.pmu.type == type) +return &perf_ibs_fetch; +if (perf_ibs_op.pmu.type == type) +return &perf_ibs_op; +return NULL ; +}static int perf_ibs_init (struct perf_event *event) { -if (perf_ibs.type != event->attr.type) +struct hw_perf_event *hwc = &event->hw; +struct perf_ibs *perf_ibs; +u64 max_cnt, config; + + perf_ibs =get_ibs_pmu (event->attr.type); +if (!perf_ibs)return -ENOENT ; + + config = event->attr.config; +if (config & ~perf_ibs->config_mask) +return -EINVAL ; + +if (hwc->sample_period) { +if (config & perf_ibs->cnt_mask) +/* raw max_cnt may not be set */ +return -EINVAL ; +if (hwc->sample_period &0x0f ) +/* lower 4 bits can not be set in ibs max cnt */ +return -EINVAL ; + max_cnt = hwc->sample_period >>4 ; +if (max_cnt & ~perf_ibs->cnt_mask) +/* out of range */ +return -EINVAL ; + config |= max_cnt; + }else { + max_cnt = config & perf_ibs->cnt_mask; + event->attr.sample_period = max_cnt <<4 ; + hwc->sample_period = event->attr.sample_period; + } + +if (!max_cnt) +return -EINVAL ; + + hwc->config_base = perf_ibs->msr; + hwc->config = config; +return 0 ; }
@@ -34,10 +89,32 @@ static void perf_ibs_del(struct perf_event *event, int flags) { } -static struct pmu perf_ibs = { - .event_init= perf_ibs_init, - .add= perf_ibs_add, - .del= perf_ibs_del, +static struct perf_ibs perf_ibs_fetch = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr =MSR_AMD64_IBSFETCHCTL , + .config_mask =IBS_FETCH_CONFIG_MASK , + .cnt_mask =IBS_FETCH_MAX_CNT , + .enable_mask =IBS_FETCH_ENABLE , +}; + +static struct perf_ibs perf_ibs_op = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr =MSR_AMD64_IBSOPCTL , + .config_mask =IBS_OP_CONFIG_MASK , + .cnt_mask =IBS_OP_MAX_CNT , + .enable_mask =IBS_OP_ENABLE , };static __init int perf_event_ibs_init (void )
@@ -45,7 +122,8 @@ static __init int perf_event_ibs_init(void)if (!ibs_caps)return -ENODEV ;/* ibs not supported by the cpu */ -perf_pmu_register (&perf_ibs,"ibs" ,-1 ); +perf_pmu_register (&perf_ibs_fetch.pmu,"ibs_fetch" ,-1 ); +perf_pmu_register (&perf_ibs_op.pmu,"ibs_op" ,-1 );printk (KERN_INFO "perf: AMD IBS detected (0x%08x)\n" , ibs_caps);return 0 ;
PATCH v4 2/4 perf, x86: Implement IBS interrupt handler
In reply to Robert RichterThis patch implements code to handle ibs interrupts. If ibs data is available a raw perf_event data sample is created and sent back to the userland. This patch only implements the storage of ibs data in the raw sample, but this could be extended in a later patch by generating generic event data such as the rip from the ibs sampling data.
V2:
* Added bit mask for msr offsets.
* Added caps field to raw sample format.
* Rebase on Don's NMI patches that introduce register_nmi_handler().
--- arch/x86/include/asm/msr-index.h | 5 ++ arch/x86/kernel/cpu/perf_event_amd_ibs.c | 84 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 0 deletions(-)
arch/x86/include/asm/msr-index.h +5 -0
@@ -127,6 +127,8 @@#define MSR_AMD64_IBSFETCHCTL 0xc0011030#define MSR_AMD64_IBSFETCHLINAD 0xc0011031#define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 +#define MSR_AMD64_IBSFETCH_REG_COUNT 3 +#define MSR_AMD64_IBSFETCH_REG_MASK ((1UL<<MSR_AMD64_IBSFETCH_REG_COUNT)-1)#define MSR_AMD64_IBSOPCTL 0xc0011033#define MSR_AMD64_IBSOPRIP 0xc0011034#define MSR_AMD64_IBSOPDATA 0xc0011035
@@ -134,8 +136,11 @@#define MSR_AMD64_IBSOPDATA3 0xc0011037#define MSR_AMD64_IBSDCLINAD 0xc0011038#define MSR_AMD64_IBSDCPHYSAD 0xc0011039 +#define MSR_AMD64_IBSOP_REG_COUNT 7 +#define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1)#define MSR_AMD64_IBSCTL 0xc001103a#define MSR_AMD64_IBSBRTARGET 0xc001103b +#define MSR_AMD64_IBS_REG_COUNT_MAX 8/* includes MSR_AMD64_IBSBRTARGET */ /* Fam 15h MSRs */ #define MSR_F15H_PERF_CTL 0xc0010200
arch/x86/kernel/cpu/perf_event_amd_ibs.c +84 -0
116 lines not on this page · raw message
Signature · 1 line
1.7.7
[tip:perf/x86-ibs] perf/x86: Implement IBS interrupt handler
Commit-ID: b7074f1fbd6149eac1ec25063e4a364c39a85473 Gitweb: http://git.kernel.org/tip/b7074f1fbd6149eac1ec25063e4a364c39a85473 Author: Robert Richter <[email protected]> AuthorDate: Thu, 15 Dec 2011 17:56:37 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 8 Mar 2012 11:35:21 +0100
perf/x86: Implement IBS interrupt handler
This patch implements code to handle ibs interrupts. If ibs data is available a raw perf_event data sample is created and sent back to the userland. This patch only implements the storage of ibs data in the raw sample, but this could be extended in a later patch by generating generic event data such as the rip from the ibs sampling data.
--- arch/x86/include/asm/msr-index.h | 5 ++ arch/x86/kernel/cpu/perf_event_amd_ibs.c | 84 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 0 deletions(-)
arch/x86/include/asm/msr-index.h +5 -0
@@ -127,6 +127,8 @@#define MSR_AMD64_IBSFETCHCTL 0xc0011030#define MSR_AMD64_IBSFETCHLINAD 0xc0011031#define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 +#define MSR_AMD64_IBSFETCH_REG_COUNT 3 +#define MSR_AMD64_IBSFETCH_REG_MASK ((1UL<<MSR_AMD64_IBSFETCH_REG_COUNT)-1)#define MSR_AMD64_IBSOPCTL 0xc0011033#define MSR_AMD64_IBSOPRIP 0xc0011034#define MSR_AMD64_IBSOPDATA 0xc0011035
@@ -134,8 +136,11 @@#define MSR_AMD64_IBSOPDATA3 0xc0011037#define MSR_AMD64_IBSDCLINAD 0xc0011038#define MSR_AMD64_IBSDCPHYSAD 0xc0011039 +#define MSR_AMD64_IBSOP_REG_COUNT 7 +#define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1)#define MSR_AMD64_IBSCTL 0xc001103a#define MSR_AMD64_IBSBRTARGET 0xc001103b +#define MSR_AMD64_IBS_REG_COUNT_MAX 8/* includes MSR_AMD64_IBSBRTARGET */ /* Fam 15h MSRs */ #define MSR_F15H_PERF_CTL 0xc0010200
arch/x86/kernel/cpu/perf_event_amd_ibs.c +84 -0
116 lines not on this page · raw message
PATCH v4 3/4 perf, x86: Implement IBS pmu control ops
In reply to Robert RichterAdd code to control the IBS pmu. We need to maintain per-cpu states. Since some states are used and changed by the nmi handler, access to these states must be atomic.
V4:
* fix returned number of handled nmis in perf_ibs_handle_irq()
--- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 106 +++++++++++++++++++++++++++++- 1 files changed, 103 insertions(+), 3 deletions(-)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +103 -3
@@ -24,6 +24,19 @@ static u32 ibs_caps;#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT)#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT +enum ibs_states { +IBS_ENABLED =0 , +IBS_STARTED =1 , +IBS_STOPPING =2 , + +IBS_MAX_STATES , +}; + +struct cpu_perf_ibs { +struct perf_event *event; +unsigned long state[BITS_TO_LONGS (IBS_MAX_STATES )]; +}; +struct perf_ibs {struct pmu pmu;unsigned int msr;
@@ -33,6 +46,7 @@ struct perf_ibs {u64 valid_mask;unsigned long offset_mask[1 ];int offset_max; +struct cpu_perf_ibs __percpu *pcpu; };struct perf_ibs_data {
@@ -97,15 +111,66 @@ static int perf_ibs_init(struct perf_event *event)return 0 ; } +static void perf_ibs_start (struct perf_event *event,int flags) +{ +struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (test_and_set_bit (IBS_STARTED , pcpu->state)) +return ; + +wrmsrl (hwc->config_base, hwc->config | perf_ibs->enable_mask); +} + +static void perf_ibs_stop (struct perf_event *event,int flags) +{ +struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); +u64 val; + +if (!test_and_clear_bit (IBS_STARTED , pcpu->state)) +return ; + +set_bit (IBS_STOPPING , pcpu->state); + +rdmsrl (hwc->config_base, val); + val &= ~perf_ibs->enable_mask; +wrmsrl (hwc->config_base, val); +} +static int perf_ibs_add (struct perf_event *event,int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (test_and_set_bit (IBS_ENABLED , pcpu->state)) +return -ENOSPC ; + + pcpu->event = event; + +if (flags &PERF_EF_START ) +perf_ibs_start (event,PERF_EF_RELOAD ); +return 0 ; }static void perf_ibs_del (struct perf_event *event,int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (!test_and_clear_bit (IBS_ENABLED , pcpu->state)) +return ; + +perf_ibs_stop (event,0 ); + + pcpu->event =NULL ; } +static void perf_ibs_read (struct perf_event *event) { } +static struct perf_ibs perf_ibs_fetch = { .pmu = { .task_ctx_nr = perf_invalid_context,
@@ -113,6 +178,9 @@ static struct perf_ibs perf_ibs_fetch = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSFETCHCTL, .config_mask = IBS_FETCH_CONFIG_MASK,
@@ -130,6 +198,9 @@ static struct perf_ibs perf_ibs_op = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSOPCTL, .config_mask = IBS_OP_CONFIG_MASK,
@@ -142,7 +213,8 @@ static struct perf_ibs perf_ibs_op = {static int perf_ibs_handle_irq (struct perf_ibs *perf_ibs,struct pt_regs *iregs) { -struct perf_event *event =NULL ; +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); +struct perf_event *event = pcpu->event;struct hw_perf_event *hwc = &event->hw;struct perf_sample_data data;struct perf_raw_record raw;
@@ -152,6 +224,14 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)unsigned int msr;u64 *buf; +if (!test_bit (IBS_STARTED , pcpu->state)) { +/* Catch spurious interrupts after stopping IBS: */ +if (!test_and_clear_bit (IBS_STOPPING , pcpu->state)) +return 0 ; +rdmsrl (perf_ibs->msr, *ibs_data.regs); +return (*ibs_data.regs & perf_ibs->valid_mask) ?1 :0 ; + } + msr = hwc->config_base; buf = ibs_data.regs;rdmsrl (msr, *buf);
@@ -200,13 +280,33 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)return handled; } +static __init int perf_ibs_pmu_init (struct perf_ibs *perf_ibs,char *name) +{ +struct cpu_perf_ibs __percpu *pcpu; +int ret; + + pcpu = alloc_percpu(struct cpu_perf_ibs); +if (!pcpu) +return -ENOMEM ; + + perf_ibs->pcpu = pcpu; + + ret =perf_pmu_register (&perf_ibs->pmu, name,-1 ); +if (ret) { + perf_ibs->pcpu =NULL ; +free_percpu (pcpu); + } + +return ret; +} +static __init int perf_event_ibs_init (void ) {if (!ibs_caps)return -ENODEV ;/* ibs not supported by the cpu */ -perf_pmu_register (&perf_ibs_fetch.pmu,"ibs_fetch" ,-1 ); -perf_pmu_register (&perf_ibs_op.pmu,"ibs_op" ,-1 ); +perf_ibs_pmu_init (&perf_ibs_fetch,"ibs_fetch" ); +perf_ibs_pmu_init (&perf_ibs_op,"ibs_op" );register_nmi_handler (NMI_LOCAL , &perf_ibs_nmi_handler,0 ,"perf_ibs" );printk (KERN_INFO "perf: AMD IBS detected (0x%08x)\n" , ibs_caps);
Signature · 1 line
1.7.7
[tip:perf/x86-ibs] perf/x86: Implement IBS pmu control ops
Commit-ID: 4db2e8e6500d9ba6406f2714fa3968b39a325274 Gitweb: http://git.kernel.org/tip/4db2e8e6500d9ba6406f2714fa3968b39a325274 Author: Robert Richter <[email protected]> AuthorDate: Thu, 15 Dec 2011 17:56:38 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 8 Mar 2012 11:35:22 +0100
perf/x86: Implement IBS pmu control ops
Add code to control the IBS pmu. We need to maintain per-cpu states. Since some states are used and changed by the nmi handler, access to these states must be atomic.
--- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 106 +++++++++++++++++++++++++++++- 1 files changed, 103 insertions(+), 3 deletions(-)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +103 -3
@@ -24,6 +24,19 @@ static u32 ibs_caps;#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT)#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT +enum ibs_states { +IBS_ENABLED =0 , +IBS_STARTED =1 , +IBS_STOPPING =2 , + +IBS_MAX_STATES , +}; + +struct cpu_perf_ibs { +struct perf_event *event; +unsigned long state[BITS_TO_LONGS (IBS_MAX_STATES )]; +}; +struct perf_ibs {struct pmu pmu;unsigned int msr;
@@ -33,6 +46,7 @@ struct perf_ibs {u64 valid_mask;unsigned long offset_mask[1 ];int offset_max; +struct cpu_perf_ibs __percpu *pcpu; };struct perf_ibs_data {
@@ -97,15 +111,66 @@ static int perf_ibs_init(struct perf_event *event)return 0 ; } +static void perf_ibs_start (struct perf_event *event,int flags) +{ +struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (test_and_set_bit (IBS_STARTED , pcpu->state)) +return ; + +wrmsrl (hwc->config_base, hwc->config | perf_ibs->enable_mask); +} + +static void perf_ibs_stop (struct perf_event *event,int flags) +{ +struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); +u64 val; + +if (!test_and_clear_bit (IBS_STARTED , pcpu->state)) +return ; + +set_bit (IBS_STOPPING , pcpu->state); + +rdmsrl (hwc->config_base, val); + val &= ~perf_ibs->enable_mask; +wrmsrl (hwc->config_base, val); +} +static int perf_ibs_add (struct perf_event *event,int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (test_and_set_bit (IBS_ENABLED , pcpu->state)) +return -ENOSPC ; + + pcpu->event = event; + +if (flags &PERF_EF_START ) +perf_ibs_start (event,PERF_EF_RELOAD ); +return 0 ; }static void perf_ibs_del (struct perf_event *event,int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); + +if (!test_and_clear_bit (IBS_ENABLED , pcpu->state)) +return ; + +perf_ibs_stop (event,0 ); + + pcpu->event =NULL ; } +static void perf_ibs_read (struct perf_event *event) { } +static struct perf_ibs perf_ibs_fetch = { .pmu = { .task_ctx_nr = perf_invalid_context,
@@ -113,6 +178,9 @@ static struct perf_ibs perf_ibs_fetch = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSFETCHCTL, .config_mask = IBS_FETCH_CONFIG_MASK,
@@ -130,6 +198,9 @@ static struct perf_ibs perf_ibs_op = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSOPCTL, .config_mask = IBS_OP_CONFIG_MASK,
@@ -142,7 +213,8 @@ static struct perf_ibs perf_ibs_op = {static int perf_ibs_handle_irq (struct perf_ibs *perf_ibs,struct pt_regs *iregs) { -struct perf_event *event =NULL ; +struct cpu_perf_ibs *pcpu =this_cpu_ptr (perf_ibs->pcpu); +struct perf_event *event = pcpu->event;struct hw_perf_event *hwc = &event->hw;struct perf_sample_data data;struct perf_raw_record raw;
@@ -152,6 +224,14 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)unsigned int msr;u64 *buf; +if (!test_bit (IBS_STARTED , pcpu->state)) { +/* Catch spurious interrupts after stopping IBS: */ +if (!test_and_clear_bit (IBS_STOPPING , pcpu->state)) +return 0 ; +rdmsrl (perf_ibs->msr, *ibs_data.regs); +return (*ibs_data.regs & perf_ibs->valid_mask) ?1 :0 ; + } + msr = hwc->config_base; buf = ibs_data.regs;rdmsrl (msr, *buf);
@@ -200,13 +280,33 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)return handled; } +static __init int perf_ibs_pmu_init (struct perf_ibs *perf_ibs,char *name) +{ +struct cpu_perf_ibs __percpu *pcpu; +int ret; + + pcpu = alloc_percpu(struct cpu_perf_ibs); +if (!pcpu) +return -ENOMEM ; + + perf_ibs->pcpu = pcpu; + + ret =perf_pmu_register (&perf_ibs->pmu, name,-1 ); +if (ret) { + perf_ibs->pcpu =NULL ; +free_percpu (pcpu); + } + +return ret; +} +static __init int perf_event_ibs_init (void ) {if (!ibs_caps)return -ENODEV ;/* ibs not supported by the cpu */ -perf_pmu_register (&perf_ibs_fetch.pmu,"ibs_fetch" ,-1 ); -perf_pmu_register (&perf_ibs_op.pmu,"ibs_op" ,-1 ); +perf_ibs_pmu_init (&perf_ibs_fetch,"ibs_fetch" ); +perf_ibs_pmu_init (&perf_ibs_op,"ibs_op" );register_nmi_handler (NMI_LOCAL , &perf_ibs_nmi_handler,0 ,"perf_ibs" );printk (KERN_INFO "perf: AMD IBS detected (0x%08x)\n" , ibs_caps);
PATCH v4 4/4 perf, x86: Implement 64 bit counter support for IBS
In reply to Robert RichterThis patch implements 64 bit counter support for IBS. The sampling period is no longer limited to the hw counter width.
The functions perf_event_set_period() and perf_event_try_update() can be used as generic functions. They can replace similar code that is duplicate across architectures.
V2: Added caps check for IBS_OP_CUR_CNT emulation.
--- arch/x86/include/asm/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_amd_ibs.c | 204 +++++++++++++++++++++++++++--- 2 files changed, 185 insertions(+), 21 deletions(-)
arch/x86/include/asm/perf_event.h +2 -0
@@ -176,6 +176,8 @@ struct x86_pmu_capability {#define IBS_FETCH_MAX_CNT 0x0000FFFFULL/* IbsOpCtl bits */ +/* lower 4 bits of the current count are ignored: */ +#define IBS_OP_CUR_CNT (0xFFFF0ULL<<32)#define IBS_OP_CNT_CTL (1ULL<<19)#define IBS_OP_VAL (1ULL<<18)#define IBS_OP_ENABLE (1ULL<<17)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +183 -21
298 lines not on this page · raw message
Signature · 1 line
1.7.7
[tip:perf/x86-ibs] perf/x86: Implement 64-bit counter support for IBS
Commit-ID: db98c5faf8cb350212ea3af786cb3ba0d4e7a01e Gitweb: http://git.kernel.org/tip/db98c5faf8cb350212ea3af786cb3ba0d4e7a01e Author: Robert Richter <[email protected]> AuthorDate: Thu, 15 Dec 2011 17:56:39 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 8 Mar 2012 11:35:22 +0100
perf/x86: Implement 64-bit counter support for IBS
This patch implements 64 bit counter support for IBS. The sampling period is no longer limited to the hw counter width.
The functions perf_event_set_period() and perf_event_try_update() can be used as generic functions. They can replace similar code that is duplicate across architectures.
--- arch/x86/include/asm/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_amd_ibs.c | 204 +++++++++++++++++++++++++++--- 2 files changed, 185 insertions(+), 21 deletions(-)
arch/x86/include/asm/perf_event.h +2 -0
@@ -177,6 +177,8 @@ struct x86_pmu_capability {#define IBS_FETCH_MAX_CNT 0x0000FFFFULL/* IbsOpCtl bits */ +/* lower 4 bits of the current count are ignored: */ +#define IBS_OP_CUR_CNT (0xFFFF0ULL<<32)#define IBS_OP_CNT_CTL (1ULL<<19)#define IBS_OP_VAL (1ULL<<18)#define IBS_OP_ENABLE (1ULL<<17)
arch/x86/kernel/cpu/perf_event_amd_ibs.c +183 -21
298 lines not on this page · raw message
Ingo,
Quoted 12 lines · Robert Richter
Changes for V4 (only patch #3 modified):
* removed userland implementation from patch set, will post it as separate patch series
* fix returned number of handled nmis inperf_ibs_handle_irq()Robert Richter (4):
perf, x86: Implement IBS event configuration perf, x86: Implement IBS interrupt handler perf, x86: Implement IBS pmu control ops perf, x86: Implement 64 bit counter support for IBS
Quoted 4 lines
arch/x86/include/asm/msr-index.h | 5 + arch/x86/include/asm/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_amd_ibs.c | 438 +++++++++++++++++++++++++++++- 3 files changed, 438 insertions(+), 7 deletions(-)
Assuming that there is agreement about the perf tool ibs implementation, could you consider to apply the (kernel) patches? Not sure what Arnaldo's plan is for the userland. It would be great to have both ready for 3.3.
(Even if Peter is on vacation or so, except the fix in perf_ibs_handle_irq() the kernel patches are unchanged and Peter already agreed to apply them.)
Thanks,
-Robert
Signature · 2 lines
Advanced Micro Devices, Inc. Operating System Research Center