From: Robert R. <rob...@am...> - 2008-07-22 19:23:46
|
Patches #1-3 are not directly related to IBS. Patch #4 adds generic support of model specific initialization. Patches #10, #11 add the core implementation of IBS. Patches #12-24 contain code improvements and small fixes. |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:46
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index a3a2058..9c8c8c5 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -251,6 +251,7 @@ op_amd_handle_ibs(struct pt_regs * const regs, (unsigned int *)&ibs_op, IBS_OP_BEGIN); rdmsr(MSR_AMD64_IBSOPCTL, low, high); + high = 0; low &= ~IBS_OP_LOW_VALID_BIT; low |= IBS_OP_LOW_ENABLE; wrmsr(MSR_AMD64_IBSOPCTL, low, high); -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:47
|
Signed-off-by: Robert Richter <rob...@am...> --- drivers/oprofile/buffer_sync.c | 24 +++++++++++------------- 1 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 69a7327..615929f 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -268,6 +268,17 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) return cookie; } +static void increment_tail(struct oprofile_cpu_buffer *b) +{ + unsigned long new_tail = b->tail_pos + 1; + + rmb(); + + if (new_tail < b->buffer_size) + b->tail_pos = new_tail; + else + b->tail_pos = 0; +} static unsigned long last_cookie = INVALID_COOKIE; @@ -417,19 +428,6 @@ static unsigned long get_slots(struct oprofile_cpu_buffer *b) } -static void increment_tail(struct oprofile_cpu_buffer *b) -{ - unsigned long new_tail = b->tail_pos + 1; - - rmb(); - - if (new_tail < b->buffer_size) - b->tail_pos = new_tail; - else - b->tail_pos = 0; -} - - /* Move tasks along towards death. Any tasks on dead_tasks * will definitely have no remaining references in any * CPU buffers at this point, because we use two lists, -- 1.5.5.4 |
From: Ingo M. <mi...@el...> - 2008-07-26 09:56:15
|
* Robert Richter <rob...@am...> wrote: > +static void increment_tail(struct oprofile_cpu_buffer *b) > +{ > + unsigned long new_tail = b->tail_pos + 1; > + > + rmb(); this barrier needs a comment. Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:48
|
These functions contain code for all AMD CPUs. The new names fit better. Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/nmi_int.c | 8 ++++---- arch/x86/oprofile/op_model_athlon.c | 28 ++++++++++++++-------------- arch/x86/oprofile/op_x86_model.h | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index b96cfd3..4e42b50 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -411,21 +411,21 @@ int __init op_nmi_init(struct oprofile_operations *ops) default: return -ENODEV; case 6: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "i386/athlon"; break; case 0xf: - model = &op_athlon_spec; + model = &op_amd_spec; /* Actually it could be i386/hammer too, but give user space an consistent name. */ cpu_type = "x86-64/hammer"; break; case 0x10: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "x86-64/family10"; break; case 0x11: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "x86-64/family11h"; break; } diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index d25d7f1..40ecb02 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -45,9 +45,9 @@ static unsigned long reset_value[NUM_COUNTERS]; -/* functions for op_athlon_spec */ +/* functions for op_amd_spec */ -static void athlon_fill_in_addresses(struct op_msrs * const msrs) +static void op_amd_fill_in_addresses(struct op_msrs * const msrs) { int i; @@ -67,7 +67,7 @@ static void athlon_fill_in_addresses(struct op_msrs * const msrs) } -static void athlon_setup_ctrs(struct op_msrs const * const msrs) +static void op_amd_setup_ctrs(struct op_msrs const * const msrs) { unsigned int low, high; int i; @@ -116,7 +116,7 @@ static void athlon_setup_ctrs(struct op_msrs const * const msrs) } -static int athlon_check_ctrs(struct pt_regs * const regs, +static int op_amd_check_ctrs(struct pt_regs * const regs, struct op_msrs const * const msrs) { unsigned int low, high; @@ -137,7 +137,7 @@ static int athlon_check_ctrs(struct pt_regs * const regs, } -static void athlon_start(struct op_msrs const * const msrs) +static void op_amd_start(struct op_msrs const * const msrs) { unsigned int low, high; int i; @@ -151,7 +151,7 @@ static void athlon_start(struct op_msrs const * const msrs) } -static void athlon_stop(struct op_msrs const * const msrs) +static void op_amd_stop(struct op_msrs const * const msrs) { unsigned int low, high; int i; @@ -167,7 +167,7 @@ static void athlon_stop(struct op_msrs const * const msrs) } } -static void athlon_shutdown(struct op_msrs const * const msrs) +static void op_amd_shutdown(struct op_msrs const * const msrs) { int i; @@ -190,15 +190,15 @@ static void op_amd_exit(void) { } -struct op_x86_model_spec const op_athlon_spec = { +struct op_x86_model_spec const op_amd_spec = { .init = op_amd_init, .exit = op_amd_exit, .num_counters = NUM_COUNTERS, .num_controls = NUM_CONTROLS, - .fill_in_addresses = &athlon_fill_in_addresses, - .setup_ctrs = &athlon_setup_ctrs, - .check_ctrs = &athlon_check_ctrs, - .start = &athlon_start, - .stop = &athlon_stop, - .shutdown = &athlon_shutdown + .fill_in_addresses = &op_amd_fill_in_addresses, + .setup_ctrs = &op_amd_setup_ctrs, + .check_ctrs = &op_amd_check_ctrs, + .start = &op_amd_start, + .stop = &op_amd_stop, + .shutdown = &op_amd_shutdown }; diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index ee9ca96..05a0261 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h @@ -48,6 +48,6 @@ struct op_x86_model_spec { extern struct op_x86_model_spec const op_ppro_spec; extern struct op_x86_model_spec const op_p4_spec; extern struct op_x86_model_spec const op_p4_ht2_spec; -extern struct op_x86_model_spec const op_athlon_spec; +extern struct op_x86_model_spec const op_amd_spec; #endif /* OP_X86_MODEL_H */ -- 1.5.5.4 |
From: Ingo M. <mi...@el...> - 2008-07-26 09:55:49
|
* Robert Richter <rob...@am...> wrote: > -struct op_x86_model_spec const op_athlon_spec = { > +struct op_x86_model_spec const op_amd_spec = { > .init = op_amd_init, > .exit = op_amd_exit, > .num_counters = NUM_COUNTERS, > .num_controls = NUM_CONTROLS, > - .fill_in_addresses = &athlon_fill_in_addresses, > - .setup_ctrs = &athlon_setup_ctrs, > - .check_ctrs = &athlon_check_ctrs, > - .start = &athlon_start, > - .stop = &athlon_stop, > - .shutdown = &athlon_shutdown > + .fill_in_addresses = &op_amd_fill_in_addresses, > + .setup_ctrs = &op_amd_setup_ctrs, > + .check_ctrs = &op_amd_check_ctrs, > + .start = &op_amd_start, > + .stop = &op_amd_stop, > + .shutdown = &op_amd_shutdown > }; minor style comment: while we are at touching these initializers, could you also do a followup cleanup patch that uses the standard initializer style of arch/x86 that have the initializer values aligned vertically? Sample: static const struct file_operations ptdump_fops = { .open = ptdump_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; Thanks, Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:48
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 22 ++++++++++++++++++++++ 1 files changed, 22 insertions(+), 0 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 0d83903..1acb067 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -361,6 +361,26 @@ static void op_amd_shutdown(struct op_msrs const * const msrs) } } +#ifndef CONFIG_SMP + +/* no IBS support */ + +static void setup_ibs(void) +{ + ibs_allowed = 0; +} + +static void clear_ibs_nmi(void) {} + +static int op_amd_init(struct oprofile_operations *ops) +{ + return 0; +} + +static void op_amd_exit(void) {} + +#else + static u8 ibs_eilvt_off; static inline void apic_init_ibs_nmi_per_cpu(void *arg) @@ -505,6 +525,8 @@ static void op_amd_exit(void) clear_ibs_nmi(); } +#endif + struct op_x86_model_spec const op_amd_spec = { .init = op_amd_init, .exit = op_amd_exit, -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:48
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/kernel/apic_32.c | 3 +++ arch/x86/kernel/apic_64.c | 3 +++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c index d6c8983..fad94b0 100644 --- a/arch/x86/kernel/apic_32.c +++ b/arch/x86/kernel/apic_32.c @@ -646,6 +646,9 @@ int setup_profiling_timer(unsigned int multiplier) * * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. */ #define APIC_EILVT_LVTOFF_MCE 0 diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 7f1f030..42bf69f 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -205,6 +205,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) * * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. */ #define APIC_EILVT_LVTOFF_MCE 0 -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:49
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/nmi_int.c | 14 ++++++++------ arch/x86/oprofile/op_model_athlon.c | 18 +++++++++++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 4e42b50..f46d8fc 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -454,6 +454,14 @@ int __init op_nmi_init(struct oprofile_operations *ops) return -ENODEV; } + /* default values, can be overwritten by model */ + ops->create_files = nmi_create_files; + ops->setup = nmi_setup; + ops->shutdown = nmi_shutdown; + ops->start = nmi_start; + ops->stop = nmi_stop; + ops->cpu_type = cpu_type; + if (model->init) ret = model->init(ops); if (ret) @@ -461,12 +469,6 @@ int __init op_nmi_init(struct oprofile_operations *ops) init_sysfs(); using_nmi = 1; - ops->create_files = nmi_create_files; - ops->setup = nmi_setup; - ops->shutdown = nmi_shutdown; - ops->start = nmi_start; - ops->stop = nmi_stop; - ops->cpu_type = cpu_type; printk(KERN_INFO "oprofile: using NMI interrupt.\n"); return 0; } diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 2650b12..0d83903 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -446,13 +446,25 @@ static void clear_ibs_nmi(void) on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); } +static int (*create_arch_files)(struct super_block * sb, struct dentry * root); + static int setup_ibs_files(struct super_block * sb, struct dentry * root) { char buf[12]; struct dentry *dir; + int ret = 0; + + /* architecture specific files */ + if (create_arch_files) + ret = create_arch_files(sb, root); + + if (ret) + return ret; if (!ibs_allowed) - return 0; + return ret; + + /* model specific files */ /* setup some reasonable defaults */ ibs_config.max_cnt_fetch = 250000; @@ -482,11 +494,15 @@ static int setup_ibs_files(struct super_block * sb, struct dentry * root) static int op_amd_init(struct oprofile_operations *ops) { + setup_ibs(); + create_arch_files = ops->create_files; + ops->create_files = setup_ibs_files; return 0; } static void op_amd_exit(void) { + clear_ibs_nmi(); } struct op_x86_model_spec const op_amd_spec = { -- 1.5.5.4 |
From: Ingo M. <mi...@el...> - 2008-07-26 10:09:46
|
* Robert Richter <rob...@am...> wrote: > + /* default values, can be overwritten by model */ > + ops->create_files = nmi_create_files; > + ops->setup = nmi_setup; > + ops->shutdown = nmi_shutdown; > + ops->start = nmi_start; > + ops->stop = nmi_stop; > + ops->cpu_type = cpu_type; i know you are moving existing code around, but sill it helps readability if you align these vertically too, like: > + /* default values, can be overwritten by model */ > + ops->create_files = nmi_create_files; > + ops->setup = nmi_setup; > + ops->shutdown = nmi_shutdown; > + ops->start = nmi_start; > + ops->stop = nmi_stop; > + ops->cpu_type = cpu_type; if i look at the aligned variant during review, i can see it immediately that it's fine and that left and right matches up conceptually. I can also see it, without having to look anywhere else, that ->cpu_type is special. > @@ -482,11 +494,15 @@ static int setup_ibs_files(struct super_block * sb, struct dentry * root) > > static int op_amd_init(struct oprofile_operations *ops) > { > + setup_ibs(); > + create_arch_files = ops->create_files; > + ops->create_files = setup_ibs_files; > return 0; the (non-)locking might be a bit racy here: the oprofilefs entries are set up first in setup_ibs() and made visible before you override them. oprofilefs entries should be made visible at the very last step, when all pointers are at their final versions and are stable already. Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:53
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 284c456..ce73236 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -382,7 +382,7 @@ static int pfm_amd64_setup_eilvt(void) u32 value = 0; /* per CPU setup */ - on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 0, 1); + on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); nodes = 0; cpu_cfg = NULL; @@ -443,7 +443,7 @@ static void setup_ibs(void) static void clear_ibs_nmi(void) { if (ibs_allowed) - on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1, 1); + on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); } static void setup_ibs_files(struct super_block *sb, struct dentry *root) -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:54
|
Signed-off-by: Robert Richter <rob...@am...> --- include/linux/oprofile.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 041bb31..bcb8f72 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -36,6 +36,8 @@ #define XEN_ENTER_SWITCH_CODE 10 #define SPU_PROFILING_CODE 11 #define SPU_CTX_SWITCH_CODE 12 +#define IBS_FETCH_CODE 13 +#define IBS_OP_CODE 14 struct super_block; struct dentry; -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:54
|
Signed-off-by: Robert Richter <rob...@am...> --- include/linux/pci_ids.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d8507eb..d1a52d8 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -497,6 +497,11 @@ #define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 #define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 #define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 #define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 #define PCI_DEVICE_ID_AMD_SCSI 0x2020 -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:54
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/Kconfig | 14 ++++++++++++++ arch/x86/oprofile/op_model_athlon.c | 33 +++++++++++++++++++++++---------- drivers/oprofile/buffer_sync.c | 6 ++++++ drivers/oprofile/cpu_buffer.c | 4 ++++ 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index ad89a33..62be4e2 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -13,6 +13,20 @@ config OPROFILE If unsure, say N. +config OPROFILE_IBS + bool "OProfile AMD IBS support (EXPERIMENTAL)" + default n + depends on OPROFILE && SMP && X86 + help + Instruction-Based Sampling (IBS) is a new profiling + technique that provides rich, precise program performance + information. IBS is introduced by AMD Family10h processors + (AMD Opteron Quad-Core processor âBarcelonaâ) to overcome + the limitations of conventional performance counter + sampling. + + If unsure, say N. + config HAVE_OPROFILE def_bool n diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 9c8c8c5..fb6015c 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -47,6 +47,10 @@ #define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) #define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) +static unsigned long reset_value[NUM_COUNTERS]; + +#ifdef CONFIG_OPROFILE_IBS + /* IbsFetchCtl bits/masks */ #define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */ #define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */ @@ -104,7 +108,6 @@ struct ibs_op_sample { */ static void clear_ibs_nmi(void); -static unsigned long reset_value[NUM_COUNTERS]; static int ibs_allowed; /* AMD Family10h and later */ struct op_ibs_config { @@ -118,6 +121,8 @@ struct op_ibs_config { static struct op_ibs_config ibs_config; +#endif + /* functions for op_amd_spec */ static void op_amd_fill_in_addresses(struct op_msrs * const msrs) @@ -188,6 +193,8 @@ static void op_amd_setup_ctrs(struct op_msrs const * const msrs) } } +#ifdef CONFIG_OPROFILE_IBS + static inline int op_amd_handle_ibs(struct pt_regs * const regs, struct op_msrs const * const msrs) @@ -261,6 +268,8 @@ op_amd_handle_ibs(struct pt_regs * const regs, return 1; } +#endif + static int op_amd_check_ctrs(struct pt_regs * const regs, struct op_msrs const * const msrs) { @@ -277,7 +286,9 @@ static int op_amd_check_ctrs(struct pt_regs * const regs, } } +#ifdef CONFIG_OPROFILE_IBS op_amd_handle_ibs(regs, msrs); +#endif /* See op_model_ppro.c */ return 1; @@ -294,6 +305,8 @@ static void op_amd_start(struct op_msrs const * const msrs) CTRL_WRITE(low, high, msrs, i); } } + +#ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; high = IBS_FETCH_HIGH_ENABLE; @@ -305,6 +318,7 @@ static void op_amd_start(struct op_msrs const * const msrs) high = 0; wrmsr(MSR_AMD64_IBSOPCTL, low, high); } +#endif } @@ -323,6 +337,7 @@ static void op_amd_stop(struct op_msrs const * const msrs) CTRL_WRITE(low, high, msrs, i); } +#ifdef CONFIG_OPROFILE_IBS if (ibs_allowed && ibs_config.fetch_enabled) { low = 0; /* clear max count and enable */ high = 0; @@ -334,6 +349,7 @@ static void op_amd_stop(struct op_msrs const * const msrs) high = 0; wrmsr(MSR_AMD64_IBSOPCTL, low, high); } +#endif } static void op_amd_shutdown(struct op_msrs const * const msrs) @@ -350,17 +366,10 @@ static void op_amd_shutdown(struct op_msrs const * const msrs) } } -#ifndef CONFIG_SMP +#ifndef CONFIG_OPROFILE_IBS /* no IBS support */ -static void setup_ibs(void) -{ - ibs_allowed = 0; -} - -static void clear_ibs_nmi(void) {} - static int op_amd_init(struct oprofile_operations *ops) { return 0; @@ -441,8 +450,12 @@ static void setup_ibs(void) if (!ibs_allowed) return; - if (pfm_amd64_setup_eilvt()) + if (pfm_amd64_setup_eilvt()) { ibs_allowed = 0; + return; + } + + printk(KERN_INFO "oprofile: AMD IBS detected\n"); } diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index e1782d2..ed98227 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -328,6 +328,8 @@ static void add_trace_begin(void) add_event_entry(TRACE_BEGIN_CODE); } +#ifdef CONFIG_OPROFILE_IBS + #define IBS_FETCH_CODE_SIZE 2 #define IBS_OP_CODE_SIZE 5 #define IBS_EIP(offset) \ @@ -390,6 +392,8 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, } } +#endif + static void add_sample_entry(unsigned long offset, unsigned long event) { add_event_entry(offset); @@ -586,6 +590,7 @@ void sync_buffer(int cpu) } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); +#ifdef CONFIG_OPROFILE_IBS } else if (s->event == IBS_FETCH_BEGIN) { state = sb_bt_start; add_ibs_begin(cpu_buf, @@ -594,6 +599,7 @@ void sync_buffer(int cpu) state = sb_bt_start; add_ibs_begin(cpu_buf, IBS_OP_CODE, in_kernel, mm); +#endif } else { struct mm_struct *oldmm = mm; diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index c9ac4e1..aba905b 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -253,6 +253,8 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) oprofile_add_ext_sample(pc, regs, event, is_kernel); } +#ifdef CONFIG_OPROFILE_IBS + #define MAX_IBS_SAMPLE_SIZE 14 static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) @@ -318,6 +320,8 @@ void oprofile_add_ibs_sample(struct pt_regs *const regs, oprofile_ops.backtrace(regs, backtrace_depth); } +#endif + void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); -- 1.5.5.4 |
From: Ingo M. <mi...@el...> - 2008-07-26 10:15:27
|
* Robert Richter <rob...@am...> wrote: > +config OPROFILE_IBS > + bool "OProfile AMD IBS support (EXPERIMENTAL)" > + default n > + depends on OPROFILE && SMP && X86 > + help > + Instruction-Based Sampling (IBS) is a new profiling > + technique that provides rich, precise program performance > + information. IBS is introduced by AMD Family10h processors > + (AMD Opteron Quad-Core processor ???Barcelona???) to overcome > + the limitations of conventional performance counter > + sampling. weird UTF characters in the description - please try to use ASCII in such general Kconfig help-texts. also, it would be nice to have a pure technical description. I.e. something like: > + Instruction-Based Sampling (IBS) is a new hardware feature > + that provides precise, non-statistical profiling > + information. IBS is available in AMD Family10h processors. > + (AMD Barcelona). > +#ifdef CONFIG_OPROFILE_IBS > op_amd_handle_ibs(regs, msrs); > +#endif please hide #ifdefs by making the op_amd_handle_ibs() function general. Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:55
|
From: Barry Kasindorf <bar...@am...> This patchset supports the new profiling hardware available in the latest AMD CPUs in the oProfile driver. Signed-off-by: Barry Kasindorf <bar...@am...> Signed-off-by: Robert Richter <rob...@am...> --- drivers/oprofile/buffer_sync.c | 72 +++++++++++++++++++++++++++++++++++++++- drivers/oprofile/cpu_buffer.c | 68 +++++++++++++++++++++++++++++++++++++- drivers/oprofile/cpu_buffer.h | 2 + 3 files changed, 140 insertions(+), 2 deletions(-) diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 615929f..e1782d2 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -5,6 +5,7 @@ * @remark Read the file COPYING * * @author John Levon <le...@mo...> + * @author Barry Kasindorf * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the @@ -272,7 +273,7 @@ static void increment_tail(struct oprofile_cpu_buffer *b) { unsigned long new_tail = b->tail_pos + 1; - rmb(); + rmb(); /* be sure fifo pointers are synchromized */ if (new_tail < b->buffer_size) b->tail_pos = new_tail; @@ -327,6 +328,67 @@ static void add_trace_begin(void) add_event_entry(TRACE_BEGIN_CODE); } +#define IBS_FETCH_CODE_SIZE 2 +#define IBS_OP_CODE_SIZE 5 +#define IBS_EIP(offset) \ + (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) +#define IBS_EVENT(offset) \ + (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) + +/* + * Add IBS fetch and op entries to event buffer + */ +static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, + int in_kernel, struct mm_struct *mm) +{ + unsigned long rip; + int i, count; + unsigned long ibs_cookie = 0; + off_t offset; + + increment_tail(cpu_buf); /* move to RIP entry */ + + rip = IBS_EIP(cpu_buf->tail_pos); + +#ifdef __LP64__ + rip += IBS_EVENT(cpu_buf->tail_pos) << 32; +#endif + + if (mm) { + ibs_cookie = lookup_dcookie(mm, rip, &offset); + + if (ibs_cookie == NO_COOKIE) + offset = rip; + if (ibs_cookie == INVALID_COOKIE) { + atomic_inc(&oprofile_stats.sample_lost_no_mapping); + offset = rip; + } + if (ibs_cookie != last_cookie) { + add_cookie_switch(ibs_cookie); + last_cookie = ibs_cookie; + } + } else + offset = rip; + + add_event_entry(ESCAPE_CODE); + add_event_entry(code); + add_event_entry(offset); /* Offset from Dcookie */ + + /* we send the Dcookie offset, but send the raw Linear Add also*/ + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); + + if (code == IBS_FETCH_CODE) + count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ + else + count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ + + for (i = 0; i < count; i++) { + increment_tail(cpu_buf); + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); + } +} static void add_sample_entry(unsigned long offset, unsigned long event) { @@ -524,6 +586,14 @@ void sync_buffer(int cpu) } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); + } else if (s->event == IBS_FETCH_BEGIN) { + state = sb_bt_start; + add_ibs_begin(cpu_buf, + IBS_FETCH_CODE, in_kernel, mm); + } else if (s->event == IBS_OP_BEGIN) { + state = sb_bt_start; + add_ibs_begin(cpu_buf, + IBS_OP_CODE, in_kernel, mm); } else { struct mm_struct *oldmm = mm; diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 2450b3a..c9ac4e1 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -5,6 +5,7 @@ * @remark Read the file COPYING * * @author John Levon <le...@mo...> + * @author Barry Kasindorf <bar...@am...> * * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. @@ -207,7 +208,7 @@ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, return 1; } -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) { if (nr_available_slots(cpu_buf) < 4) { cpu_buf->sample_lost_overflow++; @@ -252,6 +253,71 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) oprofile_add_ext_sample(pc, regs, event, is_kernel); } +#define MAX_IBS_SAMPLE_SIZE 14 +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) +{ + struct task_struct *task; + + cpu_buf->sample_received++; + + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { + cpu_buf->sample_lost_overflow++; + return 0; + } + + is_kernel = !!is_kernel; + + /* notice a switch from user->kernel or vice versa */ + if (cpu_buf->last_is_kernel != is_kernel) { + cpu_buf->last_is_kernel = is_kernel; + add_code(cpu_buf, is_kernel); + } + + /* notice a task switch */ + if (!is_kernel) { + task = current; + + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + add_code(cpu_buf, (unsigned long)task); + } + } + + add_code(cpu_buf, ibs_code); + add_sample(cpu_buf, ibs[0], ibs[1]); + add_sample(cpu_buf, ibs[2], ibs[3]); + add_sample(cpu_buf, ibs[4], ibs[5]); + + if (ibs_code == IBS_OP_BEGIN) { + add_sample(cpu_buf, ibs[6], ibs[7]); + add_sample(cpu_buf, ibs[8], ibs[9]); + add_sample(cpu_buf, ibs[10], ibs[11]); + } + + return 1; +} + +void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code) +{ + int is_kernel = !user_mode(regs); + unsigned long pc = profile_pc(regs); + + struct oprofile_cpu_buffer *cpu_buf = + &per_cpu(cpu_buffer, smp_processor_id()); + + if (!backtrace_depth) { + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); + return; + } + + /* if log_sample() fails we can't backtrace since we lost the source + * of this event */ + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) + oprofile_ops.backtrace(regs, backtrace_depth); +} + void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index c3e366b..9c44d00 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -55,5 +55,7 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); /* transient events for the CPU buffer -> event buffer */ #define CPU_IS_KERNEL 1 #define CPU_TRACE_BEGIN 2 +#define IBS_FETCH_BEGIN 3 +#define IBS_OP_BEGIN 4 #endif /* OPROFILE_CPU_BUFFER_H */ -- 1.5.5.4 |
From: Carl L. <ce...@us...> - 2008-07-23 21:11:03
|
On Tue, 2008-07-22 at 21:08 +0200, Robert Richter wrote: > From: Barry Kasindorf <bar...@am...> > > This patchset supports the new profiling hardware available in the > latest AMD CPUs in the oProfile driver. > > Signed-off-by: Barry Kasindorf <bar...@am...> > Signed-off-by: Robert Richter <rob...@am...> > --- > drivers/oprofile/buffer_sync.c | 72 +++++++++++++++++++++++++++++++++++++++- > drivers/oprofile/cpu_buffer.c | 68 +++++++++++++++++++++++++++++++++++++- > drivers/oprofile/cpu_buffer.h | 2 + > 3 files changed, 140 insertions(+), 2 deletions(-) > > diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c > index 615929f..e1782d2 100644 > --- a/drivers/oprofile/buffer_sync.c > +++ b/drivers/oprofile/buffer_sync.c > @@ -5,6 +5,7 @@ > * @remark Read the file COPYING > * > * @author John Levon <le...@mo...> > + * @author Barry Kasindorf > * > * This is the core of the buffer management. Each > * CPU buffer is processed and entered into the > @@ -272,7 +273,7 @@ static void increment_tail(struct oprofile_cpu_buffer *b) > { > unsigned long new_tail = b->tail_pos + 1; > > - rmb(); > + rmb(); /* be sure fifo pointers are synchromized */ > > if (new_tail < b->buffer_size) > b->tail_pos = new_tail; > @@ -327,6 +328,67 @@ static void add_trace_begin(void) > add_event_entry(TRACE_BEGIN_CODE); > } > > +#define IBS_FETCH_CODE_SIZE 2 > +#define IBS_OP_CODE_SIZE 5 > +#define IBS_EIP(offset) \ > + (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) > +#define IBS_EVENT(offset) \ > + (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) > + > +/* > + * Add IBS fetch and op entries to event buffer > + */ > +static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, > + int in_kernel, struct mm_struct *mm) > +{ > + unsigned long rip; > + int i, count; > + unsigned long ibs_cookie = 0; > + off_t offset; > + > + increment_tail(cpu_buf); /* move to RIP entry */ > + > + rip = IBS_EIP(cpu_buf->tail_pos); > + > +#ifdef __LP64__ > + rip += IBS_EVENT(cpu_buf->tail_pos) << 32; > +#endif > + > + if (mm) { > + ibs_cookie = lookup_dcookie(mm, rip, &offset); > + > + if (ibs_cookie == NO_COOKIE) > + offset = rip; > + if (ibs_cookie == INVALID_COOKIE) { > + atomic_inc(&oprofile_stats.sample_lost_no_mapping); > + offset = rip; > + } > + if (ibs_cookie != last_cookie) { > + add_cookie_switch(ibs_cookie); > + last_cookie = ibs_cookie; > + } > + } else > + offset = rip; > + > + add_event_entry(ESCAPE_CODE); > + add_event_entry(code); > + add_event_entry(offset); /* Offset from Dcookie */ > + > + /* we send the Dcookie offset, but send the raw Linear Add also*/ > + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); > + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); > + > + if (code == IBS_FETCH_CODE) > + count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ > + else > + count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ > + > + for (i = 0; i < count; i++) { > + increment_tail(cpu_buf); > + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); > + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); > + } > +} In general, I think it would be good to make the IBS stuff as arch independent as possible. Could you move the above function to the arch specific code. Here a generic function pointer could be exported which would be assigned to the arch specific routine. This would enable another architecture to reuse this code. > > static void add_sample_entry(unsigned long offset, unsigned long event) > { > @@ -524,6 +586,14 @@ void sync_buffer(int cpu) > } else if (s->event == CPU_TRACE_BEGIN) { > state = sb_bt_start; > add_trace_begin(); > + } else if (s->event == IBS_FETCH_BEGIN) { > + state = sb_bt_start; > + add_ibs_begin(cpu_buf, > + IBS_FETCH_CODE, in_kernel, mm); > + } else if (s->event == IBS_OP_BEGIN) { > + state = sb_bt_start; > + add_ibs_begin(cpu_buf, > + IBS_OP_CODE, in_kernel, mm); > } else { > struct mm_struct *oldmm = mm; > If you made the log_ibs_sample() more generic, as discussed below, then the #defines could be changed to generic names and thus allow other architectures to leverage the same code. > diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c > index 2450b3a..c9ac4e1 100644 > --- a/drivers/oprofile/cpu_buffer.c > +++ b/drivers/oprofile/cpu_buffer.c > @@ -5,6 +5,7 @@ > * @remark Read the file COPYING > * > * @author John Levon <le...@mo...> > + * @author Barry Kasindorf <bar...@am...> > * > * Each CPU has a local buffer that stores PC value/event > * pairs. We also log context switches when we notice them. > @@ -207,7 +208,7 @@ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, > return 1; > } > > -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) > +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) > { > if (nr_available_slots(cpu_buf) < 4) { > cpu_buf->sample_lost_overflow++; > @@ -252,6 +253,71 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) > oprofile_add_ext_sample(pc, regs, event, is_kernel); > } > > +#define MAX_IBS_SAMPLE_SIZE 14 > +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, > + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) > +{ > + struct task_struct *task; > + > + cpu_buf->sample_received++; > + > + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { > + cpu_buf->sample_lost_overflow++; > + return 0; > + } > + > + is_kernel = !!is_kernel; > + > + /* notice a switch from user->kernel or vice versa */ > + if (cpu_buf->last_is_kernel != is_kernel) { > + cpu_buf->last_is_kernel = is_kernel; > + add_code(cpu_buf, is_kernel); > + } > + > + /* notice a task switch */ > + if (!is_kernel) { > + task = current; > + > + if (cpu_buf->last_task != task) { > + cpu_buf->last_task = task; > + add_code(cpu_buf, (unsigned long)task); > + } > + } > + > + add_code(cpu_buf, ibs_code); > + add_sample(cpu_buf, ibs[0], ibs[1]); > + add_sample(cpu_buf, ibs[2], ibs[3]); > + add_sample(cpu_buf, ibs[4], ibs[5]); > + > + if (ibs_code == IBS_OP_BEGIN) { > + add_sample(cpu_buf, ibs[6], ibs[7]); > + add_sample(cpu_buf, ibs[8], ibs[9]); > + add_sample(cpu_buf, ibs[10], ibs[11]); > + } > + > + return 1; > +} > + Can we make this function a bit more general? Specifically, suppose static int log_generic_sample (struct oprofile_cpu_buffer *cpu_buf, unsigned int *array, int num_entries) If we just passed in an array of data that is ready to just be copied directly to the kernel buffer in a for loop: for (i=0; i<num_entries; i=i+2) add_sample(cpu_buf, array[i], array[i+1]); The arch specific code would have to do the if(ibs_code == IBS_OP_BEGIN) code to either add the stuff into the input array or not. The appropriate number of entries would be passed. The if (cpu_buf->last_is_kernel != is_kernel) statement might be hard to do from the architecture code and might have to stay in the log_generic_sample. It would be good if we could find a way to also move this line of code to the arch specific code to setup the generic array of data to pass to log_generic_sample(). Maybe the arch specific code could have an array of last_is_kernel to refer to when preparing the data for the log_generic_sample() call. By making this more generic and flexible in the number of entries and pushing the logic of what to put into the array into the arch specific code, it could be used by other architectures. I had something along the lines of the above log_generic_sample() in a version of a patch for the CELL architecture. On CELL, I have to take samples from the various SPUs and push them into the kernel buffer. I had tried to do it using the per CPU buffers. I ended up dropping the approach for other reasons. The point is I can see where something a little more generic/flexible could be used by other architectures. > +void oprofile_add_ibs_sample(struct pt_regs *const regs, > + unsigned int * const ibs_sample, u8 code) > +{ > + int is_kernel = !user_mode(regs); > + unsigned long pc = profile_pc(regs); > + > + struct oprofile_cpu_buffer *cpu_buf = > + &per_cpu(cpu_buffer, smp_processor_id()); > + > + if (!backtrace_depth) { > + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); > + return; > + } > + > + /* if log_sample() fails we can't backtrace since we lost the source > + * of this event */ > + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) > + oprofile_ops.backtrace(regs, backtrace_depth); > +} > + > void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) > { > struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); > diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h > index c3e366b..9c44d00 100644 > --- a/drivers/oprofile/cpu_buffer.h > +++ b/drivers/oprofile/cpu_buffer.h > @@ -55,5 +55,7 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); > /* transient events for the CPU buffer -> event buffer */ > #define CPU_IS_KERNEL 1 > #define CPU_TRACE_BEGIN 2 > +#define IBS_FETCH_BEGIN 3 > +#define IBS_OP_BEGIN 4 > > #endif /* OPROFILE_CPU_BUFFER_H */ |
From: Robert R. <rob...@am...> - 2008-07-23 20:20:04
|
On 23.07.08 13:01:57, Carl Love wrote: > > On Tue, 2008-07-22 at 21:08 +0200, Robert Richter wrote: > > From: Barry Kasindorf <bar...@am...> > > > > This patchset supports the new profiling hardware available in the > > latest AMD CPUs in the oProfile driver. > > > > Signed-off-by: Barry Kasindorf <bar...@am...> > > Signed-off-by: Robert Richter <rob...@am...> > > --- > > drivers/oprofile/buffer_sync.c | 72 +++++++++++++++++++++++++++++++++++++++- > > drivers/oprofile/cpu_buffer.c | 68 +++++++++++++++++++++++++++++++++++++- > > drivers/oprofile/cpu_buffer.h | 2 + > > 3 files changed, 140 insertions(+), 2 deletions(-) > > > > diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c > > index 615929f..e1782d2 100644 > > --- a/drivers/oprofile/buffer_sync.c > > +++ b/drivers/oprofile/buffer_sync.c > > @@ -5,6 +5,7 @@ > > * @remark Read the file COPYING > > * > > * @author John Levon <le...@mo...> > > + * @author Barry Kasindorf > > * > > * This is the core of the buffer management. Each > > * CPU buffer is processed and entered into the > > @@ -272,7 +273,7 @@ static void increment_tail(struct oprofile_cpu_buffer *b) > > { > > unsigned long new_tail = b->tail_pos + 1; > > > > - rmb(); > > + rmb(); /* be sure fifo pointers are synchromized */ > > > > if (new_tail < b->buffer_size) > > b->tail_pos = new_tail; > > @@ -327,6 +328,67 @@ static void add_trace_begin(void) > > add_event_entry(TRACE_BEGIN_CODE); > > } > > > > +#define IBS_FETCH_CODE_SIZE 2 > > +#define IBS_OP_CODE_SIZE 5 > > +#define IBS_EIP(offset) \ > > + (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) > > +#define IBS_EVENT(offset) \ > > + (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) > > + > > +/* > > + * Add IBS fetch and op entries to event buffer > > + */ > > +static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, > > + int in_kernel, struct mm_struct *mm) > > +{ > > + unsigned long rip; > > + int i, count; > > + unsigned long ibs_cookie = 0; > > + off_t offset; > > + > > + increment_tail(cpu_buf); /* move to RIP entry */ > > + > > + rip = IBS_EIP(cpu_buf->tail_pos); > > + > > +#ifdef __LP64__ > > + rip += IBS_EVENT(cpu_buf->tail_pos) << 32; > > +#endif > > + > > + if (mm) { > > + ibs_cookie = lookup_dcookie(mm, rip, &offset); > > + > > + if (ibs_cookie == NO_COOKIE) > > + offset = rip; > > + if (ibs_cookie == INVALID_COOKIE) { > > + atomic_inc(&oprofile_stats.sample_lost_no_mapping); > > + offset = rip; > > + } > > + if (ibs_cookie != last_cookie) { > > + add_cookie_switch(ibs_cookie); > > + last_cookie = ibs_cookie; > > + } > > + } else > > + offset = rip; > > + > > + add_event_entry(ESCAPE_CODE); > > + add_event_entry(code); > > + add_event_entry(offset); /* Offset from Dcookie */ > > + > > + /* we send the Dcookie offset, but send the raw Linear Add also*/ > > + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); > > + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); > > + > > + if (code == IBS_FETCH_CODE) > > + count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ > > + else > > + count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ > > + > > + for (i = 0; i < count; i++) { > > + increment_tail(cpu_buf); > > + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); > > + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); > > + } > > +} > > In general, I think it would be good to make the IBS stuff as arch > independent as possible. Could you move the above function to the arch > specific code. Here a generic function pointer could be exported which > would be assigned to the arch specific routine. This would enable > another architecture to reuse this code. Yes, this will change too (see my mail to Maynard). No IBS outside of op_model_amd.c. The solution is copy the samples directly in sync_buffer() until a new escape code is received. Internals of the samples (type, size, etc.) are not needed in this case. The address convertion will use existion functions as well. -Robert > > > > > static void add_sample_entry(unsigned long offset, unsigned long event) > > { > > @@ -524,6 +586,14 @@ void sync_buffer(int cpu) > > } else if (s->event == CPU_TRACE_BEGIN) { > > state = sb_bt_start; > > add_trace_begin(); > > + } else if (s->event == IBS_FETCH_BEGIN) { > > + state = sb_bt_start; > > + add_ibs_begin(cpu_buf, > > + IBS_FETCH_CODE, in_kernel, mm); > > + } else if (s->event == IBS_OP_BEGIN) { > > + state = sb_bt_start; > > + add_ibs_begin(cpu_buf, > > + IBS_OP_CODE, in_kernel, mm); > > } else { > > struct mm_struct *oldmm = mm; > > > > If you made the log_ibs_sample() more generic, as discussed below, then > the #defines could be changed to generic names and thus allow other > architectures to leverage the same code. > > > > diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c > > index 2450b3a..c9ac4e1 100644 > > --- a/drivers/oprofile/cpu_buffer.c > > +++ b/drivers/oprofile/cpu_buffer.c > > @@ -5,6 +5,7 @@ > > * @remark Read the file COPYING > > * > > * @author John Levon <le...@mo...> > > + * @author Barry Kasindorf <bar...@am...> > > * > > * Each CPU has a local buffer that stores PC value/event > > * pairs. We also log context switches when we notice them. > > @@ -207,7 +208,7 @@ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, > > return 1; > > } > > > > -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) > > +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) > > { > > if (nr_available_slots(cpu_buf) < 4) { > > cpu_buf->sample_lost_overflow++; > > @@ -252,6 +253,71 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) > > oprofile_add_ext_sample(pc, regs, event, is_kernel); > > } > > > > +#define MAX_IBS_SAMPLE_SIZE 14 > > +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, > > + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) > > +{ > > + struct task_struct *task; > > + > > + cpu_buf->sample_received++; > > + > > + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { > > + cpu_buf->sample_lost_overflow++; > > + return 0; > > + } > > + > > + is_kernel = !!is_kernel; > > + > > + /* notice a switch from user->kernel or vice versa */ > > + if (cpu_buf->last_is_kernel != is_kernel) { > > + cpu_buf->last_is_kernel = is_kernel; > > + add_code(cpu_buf, is_kernel); > > + } > > + > > + /* notice a task switch */ > > + if (!is_kernel) { > > + task = current; > > + > > + if (cpu_buf->last_task != task) { > > + cpu_buf->last_task = task; > > + add_code(cpu_buf, (unsigned long)task); > > + } > > + } > > + > > + add_code(cpu_buf, ibs_code); > > + add_sample(cpu_buf, ibs[0], ibs[1]); > > + add_sample(cpu_buf, ibs[2], ibs[3]); > > + add_sample(cpu_buf, ibs[4], ibs[5]); > > + > > + if (ibs_code == IBS_OP_BEGIN) { > > + add_sample(cpu_buf, ibs[6], ibs[7]); > > + add_sample(cpu_buf, ibs[8], ibs[9]); > > + add_sample(cpu_buf, ibs[10], ibs[11]); > > + } > > + > > + return 1; > > +} > > + > > Can we make this function a bit more general? Specifically, suppose > > static int log_generic_sample (struct oprofile_cpu_buffer *cpu_buf, > unsigned int *array, int num_entries) > > If we just passed in an array of data that is ready to just be copied > directly to > the kernel buffer in a for loop: > > for (i=0; i<num_entries; i=i+2) > > add_sample(cpu_buf, array[i], array[i+1]); > > The arch specific code would have to do the if(ibs_code == IBS_OP_BEGIN) > code to either add the stuff into the input array or not. The > appropriate number of entries would be passed. > > The if (cpu_buf->last_is_kernel != is_kernel) statement might be hard > to do from the architecture code and might have to stay in the > log_generic_sample. It would be good if we could find a way to also move > this line of code to the arch specific code to setup the generic array > of data to pass to log_generic_sample(). Maybe the arch specific code > could have an array of last_is_kernel to refer to when preparing the > data for the log_generic_sample() call. > > By making this more generic and flexible in the number of entries and > pushing the logic of what to put into the array into the arch specific > code, it could be used by other architectures. I had something along > the lines of the above log_generic_sample() in a version of a patch for > the CELL architecture. On CELL, I have to take samples from the various > SPUs and push them into the kernel buffer. I had tried to do it using > the per CPU buffers. I ended up dropping the approach for other > reasons. The point is I can see where something a little more > generic/flexible could be used by other architectures. > > > +void oprofile_add_ibs_sample(struct pt_regs *const regs, > > + unsigned int * const ibs_sample, u8 code) > > +{ > > + int is_kernel = !user_mode(regs); > > + unsigned long pc = profile_pc(regs); > > + > > + struct oprofile_cpu_buffer *cpu_buf = > > + &per_cpu(cpu_buffer, smp_processor_id()); > > + > > + if (!backtrace_depth) { > > + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); > > + return; > > + } > > + > > + /* if log_sample() fails we can't backtrace since we lost the source > > + * of this event */ > > + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) > > + oprofile_ops.backtrace(regs, backtrace_depth); > > +} > > + > > void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) > > { > > struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); > > diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h > > index c3e366b..9c44d00 100644 > > --- a/drivers/oprofile/cpu_buffer.h > > +++ b/drivers/oprofile/cpu_buffer.h > > @@ -55,5 +55,7 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); > > /* transient events for the CPU buffer -> event buffer */ > > #define CPU_IS_KERNEL 1 > > #define CPU_TRACE_BEGIN 2 > > +#define IBS_FETCH_BEGIN 3 > > +#define IBS_OP_BEGIN 4 > > > > #endif /* OPROFILE_CPU_BUFFER_H */ > > -- Advanced Micro Devices, Inc. Operating System Research Center email: rob...@am... |
From: Ingo M. <mi...@el...> - 2008-07-26 09:58:36
|
* Robert Richter <rob...@am...> wrote: > @@ -272,7 +273,7 @@ static void increment_tail(struct oprofile_cpu_buffer *b) > { > unsigned long new_tail = b->tail_pos + 1; > > - rmb(); > + rmb(); /* be sure fifo pointers are synchromized */ ignore my previous mail :-) > + if (ibs_code == IBS_OP_BEGIN) { > + add_sample(cpu_buf, ibs[6], ibs[7]); > + add_sample(cpu_buf, ibs[8], ibs[9]); > + add_sample(cpu_buf, ibs[10], ibs[11]); > + } style problem. > + int is_kernel = !user_mode(regs); > + unsigned long pc = profile_pc(regs); > + > + struct oprofile_cpu_buffer *cpu_buf = > + &per_cpu(cpu_buffer, smp_processor_id()); please dont put newlines in the middle of variable definitions. > /* transient events for the CPU buffer -> event buffer */ > #define CPU_IS_KERNEL 1 > #define CPU_TRACE_BEGIN 2 > +#define IBS_FETCH_BEGIN 3 > +#define IBS_OP_BEGIN 4 vertical alignment would help readability i guess. Plus use an enum instead of a macro. Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:55
|
Signed-off-by: Robert Richter <rob...@am...> --- drivers/oprofile/buffer_sync.c | 111 ++++++++++++++++++++------------------- 1 files changed, 57 insertions(+), 54 deletions(-) diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 9304c45..69a7327 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -33,7 +33,7 @@ #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" - + static LIST_HEAD(dying_tasks); static LIST_HEAD(dead_tasks); static cpumask_t marked_cpus = CPU_MASK_NONE; @@ -48,10 +48,11 @@ static void process_task_mortuary(void); * Can be invoked from softirq via RCU callback due to * call_rcu() of the task struct, hence the _irqsave. */ -static int task_free_notify(struct notifier_block * self, unsigned long val, void * data) +static int +task_free_notify(struct notifier_block *self, unsigned long val, void *data) { unsigned long flags; - struct task_struct * task = data; + struct task_struct *task = data; spin_lock_irqsave(&task_mortuary, flags); list_add(&task->tasks, &dying_tasks); spin_unlock_irqrestore(&task_mortuary, flags); @@ -62,13 +63,14 @@ static int task_free_notify(struct notifier_block * self, unsigned long val, voi /* The task is on its way out. A sync of the buffer means we can catch * any remaining samples for this task. */ -static int task_exit_notify(struct notifier_block * self, unsigned long val, void * data) +static int +task_exit_notify(struct notifier_block *self, unsigned long val, void *data) { /* To avoid latency problems, we only process the current CPU, * hoping that most samples for the task are on this CPU */ sync_buffer(raw_smp_processor_id()); - return 0; + return 0; } @@ -77,11 +79,12 @@ static int task_exit_notify(struct notifier_block * self, unsigned long val, voi * we don't lose any. This does not have to be exact, it's a QoI issue * only. */ -static int munmap_notify(struct notifier_block * self, unsigned long val, void * data) +static int +munmap_notify(struct notifier_block *self, unsigned long val, void *data) { unsigned long addr = (unsigned long)data; - struct mm_struct * mm = current->mm; - struct vm_area_struct * mpnt; + struct mm_struct *mm = current->mm; + struct vm_area_struct *mpnt; down_read(&mm->mmap_sem); @@ -99,11 +102,12 @@ static int munmap_notify(struct notifier_block * self, unsigned long val, void * return 0; } - + /* We need to be told about new modules so we don't attribute to a previously * loaded module, or drop the samples on the floor. */ -static int module_load_notify(struct notifier_block * self, unsigned long val, void * data) +static int +module_load_notify(struct notifier_block *self, unsigned long val, void *data) { #ifdef CONFIG_MODULES if (val != MODULE_STATE_COMING) @@ -118,7 +122,7 @@ static int module_load_notify(struct notifier_block * self, unsigned long val, v return 0; } - + static struct notifier_block task_free_nb = { .notifier_call = task_free_notify, }; @@ -135,7 +139,7 @@ static struct notifier_block module_load_nb = { .notifier_call = module_load_notify, }; - + static void end_sync(void) { end_cpu_work(); @@ -208,14 +212,14 @@ static inline unsigned long fast_get_dcookie(struct path *path) * not strictly necessary but allows oprofile to associate * shared-library samples with particular applications */ -static unsigned long get_exec_dcookie(struct mm_struct * mm) +static unsigned long get_exec_dcookie(struct mm_struct *mm) { unsigned long cookie = NO_COOKIE; - struct vm_area_struct * vma; - + struct vm_area_struct *vma; + if (!mm) goto out; - + for (vma = mm->mmap; vma; vma = vma->vm_next) { if (!vma->vm_file) continue; @@ -235,13 +239,14 @@ out: * sure to do this lookup before a mm->mmap modification happens so * we don't lose track. */ -static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) +static unsigned long +lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) { unsigned long cookie = NO_COOKIE; - struct vm_area_struct * vma; + struct vm_area_struct *vma; for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { - + if (addr < vma->vm_start || addr >= vma->vm_end) continue; @@ -265,7 +270,7 @@ static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, o static unsigned long last_cookie = INVALID_COOKIE; - + static void add_cpu_switch(int i) { add_event_entry(ESCAPE_CODE); @@ -278,16 +283,16 @@ static void add_kernel_ctx_switch(unsigned int in_kernel) { add_event_entry(ESCAPE_CODE); if (in_kernel) - add_event_entry(KERNEL_ENTER_SWITCH_CODE); + add_event_entry(KERNEL_ENTER_SWITCH_CODE); else - add_event_entry(KERNEL_EXIT_SWITCH_CODE); + add_event_entry(KERNEL_EXIT_SWITCH_CODE); } - + static void -add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) +add_user_ctx_switch(struct task_struct const *task, unsigned long cookie) { add_event_entry(ESCAPE_CODE); - add_event_entry(CTX_SWITCH_CODE); + add_event_entry(CTX_SWITCH_CODE); add_event_entry(task->pid); add_event_entry(cookie); /* Another code for daemon back-compat */ @@ -296,7 +301,7 @@ add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) add_event_entry(task->tgid); } - + static void add_cookie_switch(unsigned long cookie) { add_event_entry(ESCAPE_CODE); @@ -304,7 +309,7 @@ static void add_cookie_switch(unsigned long cookie) add_event_entry(cookie); } - + static void add_trace_begin(void) { add_event_entry(ESCAPE_CODE); @@ -319,13 +324,13 @@ static void add_sample_entry(unsigned long offset, unsigned long event) } -static int add_us_sample(struct mm_struct * mm, struct op_sample * s) +static int add_us_sample(struct mm_struct *mm, struct op_sample *s) { unsigned long cookie; off_t offset; - - cookie = lookup_dcookie(mm, s->eip, &offset); - + + cookie = lookup_dcookie(mm, s->eip, &offset); + if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); return 0; @@ -341,13 +346,13 @@ static int add_us_sample(struct mm_struct * mm, struct op_sample * s) return 1; } - + /* Add a sample to the global event buffer. If possible the * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. */ static int -add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) +add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) { if (in_kernel) { add_sample_entry(s->eip, s->event); @@ -359,9 +364,9 @@ add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) } return 0; } - -static void release_mm(struct mm_struct * mm) + +static void release_mm(struct mm_struct *mm) { if (!mm) return; @@ -370,9 +375,9 @@ static void release_mm(struct mm_struct * mm) } -static struct mm_struct * take_tasks_mm(struct task_struct * task) +static struct mm_struct *take_tasks_mm(struct task_struct *task) { - struct mm_struct * mm = get_task_mm(task); + struct mm_struct *mm = get_task_mm(task); if (mm) down_read(&mm->mmap_sem); return mm; @@ -383,10 +388,10 @@ static inline int is_code(unsigned long val) { return val == ESCAPE_CODE; } - + /* "acquire" as many cpu buffer slots as we can */ -static unsigned long get_slots(struct oprofile_cpu_buffer * b) +static unsigned long get_slots(struct oprofile_cpu_buffer *b) { unsigned long head = b->head_pos; unsigned long tail = b->tail_pos; @@ -412,7 +417,7 @@ static unsigned long get_slots(struct oprofile_cpu_buffer * b) } -static void increment_tail(struct oprofile_cpu_buffer * b) +static void increment_tail(struct oprofile_cpu_buffer *b) { unsigned long new_tail = b->tail_pos + 1; @@ -435,8 +440,8 @@ static void process_task_mortuary(void) { unsigned long flags; LIST_HEAD(local_dead_tasks); - struct task_struct * task; - struct task_struct * ttask; + struct task_struct *task; + struct task_struct *ttask; spin_lock_irqsave(&task_mortuary, flags); @@ -493,7 +498,7 @@ void sync_buffer(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); struct mm_struct *mm = NULL; - struct task_struct * new; + struct task_struct *new; unsigned long cookie = 0; int in_kernel = 1; unsigned int i; @@ -501,7 +506,7 @@ void sync_buffer(int cpu) unsigned long available; mutex_lock(&buffer_mutex); - + add_cpu_switch(cpu); /* Remember, only we can modify tail_pos */ @@ -509,8 +514,8 @@ void sync_buffer(int cpu) available = get_slots(cpu_buf); for (i = 0; i < available; ++i) { - struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; - + struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; + if (is_code(s->eip)) { if (s->event <= CPU_IS_KERNEL) { /* kernel/userspace switch */ @@ -522,7 +527,7 @@ void sync_buffer(int cpu) state = sb_bt_start; add_trace_begin(); } else { - struct mm_struct * oldmm = mm; + struct mm_struct *oldmm = mm; /* userspace context switch */ new = (struct task_struct *)s->event; @@ -533,13 +538,11 @@ void sync_buffer(int cpu) cookie = get_exec_dcookie(mm); add_user_ctx_switch(new, cookie); } - } else { - if (state >= sb_bt_start && - !add_sample(mm, s, in_kernel)) { - if (state == sb_bt_start) { - state = sb_bt_ignore; - atomic_inc(&oprofile_stats.bt_lost_no_mapping); - } + } else if (state >= sb_bt_start && + !add_sample(mm, s, in_kernel)) { + if (state == sb_bt_start) { + state = sb_bt_ignore; + atomic_inc(&oprofile_stats.bt_lost_no_mapping); } } -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:56
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 90193b1..284c456 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -73,6 +73,11 @@ /* MSR to set the IBS control register APIC LVT offset */ #define IBS_LVT_OFFSET_PCI 0x1CC +/* The function interface needs to be fixed, something like add + data. Should then be added to linux/oprofile.h. */ +extern void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code); + struct ibs_fetch_sample { /* MSRC001_1031 IBS Fetch Linear Address Register */ unsigned int ibs_fetch_lin_addr_low; -- 1.5.5.4 |
From: Ingo M. <mi...@el...> - 2008-07-26 10:05:39
|
* Robert Richter <rob...@am...> wrote: > Signed-off-by: Robert Richter <rob...@am...> > --- > arch/x86/oprofile/op_model_athlon.c | 5 +++++ > 1 files changed, 5 insertions(+), 0 deletions(-) > > diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c > index 90193b1..284c456 100644 > --- a/arch/x86/oprofile/op_model_athlon.c > +++ b/arch/x86/oprofile/op_model_athlon.c > @@ -73,6 +73,11 @@ > /* MSR to set the IBS control register APIC LVT offset */ > #define IBS_LVT_OFFSET_PCI 0x1CC > > +/* The function interface needs to be fixed, something like add > + data. Should then be added to linux/oprofile.h. */ please use standard comment style. Ingo |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:57
|
From: Barry Kasindorf <bar...@am...> This patch add support for AMD Family 11h CPUs. Signed-off-by: Barry Kasindorf <bar...@am...> Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/nmi_int.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 7f3329b..cf28a02 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -422,6 +422,10 @@ int __init op_nmi_init(struct oprofile_operations *ops) model = &op_athlon_spec; cpu_type = "x86-64/family10"; break; + case 0x11: + model = &op_athlon_spec; + cpu_type = "x86-64/family11h"; + break; } break; -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:57
|
Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 45 +++++++++++++++++++++------------- 1 files changed, 28 insertions(+), 17 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 229e0b4..a2c8e2e 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -195,27 +195,18 @@ static void op_amd_setup_ctrs(struct op_msrs const * const msrs) } } - -static int op_amd_check_ctrs(struct pt_regs * const regs, - struct op_msrs const * const msrs) +static inline int +op_amd_handle_ibs(struct pt_regs * const regs, + struct op_msrs const * const msrs) { unsigned int low, high; - int i; struct ibs_fetch_sample ibs_fetch; struct ibs_op_sample ibs_op; - for (i = 0 ; i < NUM_COUNTERS; ++i) { - if (!reset_value[i]) - continue; - CTR_READ(low, high, msrs, i); - if (CTR_OVERFLOWED(low)) { - oprofile_add_sample(regs, i); - CTR_WRITE(reset_value[i], msrs, i); - } - } + if (!ibs_allowed) + return 1; - /*If AMD and IBS is available */ - if (ibs_allowed && ibs_config.fetch_enabled) { + if (ibs_config.fetch_enabled) { rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); if (high & IBS_FETCH_VALID_BIT) { ibs_fetch.ibs_fetch_ctl_high = high; @@ -240,7 +231,7 @@ static int op_amd_check_ctrs(struct pt_regs * const regs, } } - if (ibs_allowed && ibs_config.op_enabled) { + if (ibs_config.op_enabled) { rdmsr(MSR_AMD64_IBSOPCTL, low, high); if (low & IBS_OP_VALID_BIT) { rdmsr(MSR_AMD64_IBSOPRIP, low, high); @@ -273,10 +264,30 @@ static int op_amd_check_ctrs(struct pt_regs * const regs, } } - /* See op_model_ppro.c */ return 1; } +static int op_amd_check_ctrs(struct pt_regs * const regs, + struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + for (i = 0 ; i < NUM_COUNTERS; ++i) { + if (!reset_value[i]) + continue; + CTR_READ(low, high, msrs, i); + if (CTR_OVERFLOWED(low)) { + oprofile_add_sample(regs, i); + CTR_WRITE(reset_value[i], msrs, i); + } + } + + op_amd_handle_ibs(regs, msrs); + + /* See op_model_ppro.c */ + return 1; +} static void op_amd_start(struct op_msrs const * const msrs) { -- 1.5.5.4 |
From: Robert R. <rob...@am...> - 2008-07-22 19:23:59
|
From: Barry Kasindorf <bar...@am...> This patchset supports the new profiling hardware available in the latest AMD CPUs in the oProfile driver. Signed-off-by: Barry Kasindorf <bar...@am...> Signed-off-by: Robert Richter <rob...@am...> --- arch/x86/oprofile/op_model_athlon.c | 257 +++++++++++++++++++++++++++++++++++ 1 files changed, 257 insertions(+), 0 deletions(-) diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c index 40ecb02..229e0b4 100644 --- a/arch/x86/oprofile/op_model_athlon.c +++ b/arch/x86/oprofile/op_model_athlon.c @@ -9,9 +9,13 @@ * @author Philippe Elie * @author Graydon Hoare * @author Robert Richter <rob...@am...> + * @author Barry Kasindorf */ #include <linux/oprofile.h> +#include <linux/device.h> +#include <linux/pci.h> + #include <asm/ptrace.h> #include <asm/msr.h> #include <asm/nmi.h> @@ -43,7 +47,83 @@ #define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) #define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) +#define IBS_FETCH_CTL_HIGH_MASK 0xFFFFFFFF +/* high dword bit IbsFetchCtl[bit 49] */ +#define IBS_FETCH_VALID_BIT (1UL << 17) +/* high dword bit IbsFetchCtl[bit 52] */ +#define IBS_FETCH_PHY_ADDR_VALID_BIT (1UL << 20) +/* high dword bit IbsFetchCtl[bit 48] */ +#define IBS_FETCH_ENABLE (1UL << 16) + +#define IBS_FETCH_CTL_CNT_MASK 0x00000000FFFF0000UL +#define IBS_FETCH_CTL_MAX_CNT_MASK 0x000000000000FFFFUL + +/*IbsOpCtl masks/bits */ +#define IBS_OP_VALID_BIT (1ULL<<18) /* IbsOpCtl[bit18] */ +#define IBS_OP_ENABLE (1ULL<<17) /* IBS_OP_ENABLE[bit17]*/ + +/* Codes used in cpu_buffer.c */ +#define IBS_FETCH_BEGIN 3 +#define IBS_OP_BEGIN 4 + +/*IbsOpData3 masks */ +#define IBS_CTL_LVT_OFFSET_VALID_BIT (1ULL<<8) + +/*PCI Extended Configuration Constants */ +/* MSR to set the IBS control register APIC LVT offset */ +#define IBS_LVT_OFFSET_PCI 0x1CC + +struct ibs_fetch_sample { + /* MSRC001_1031 IBS Fetch Linear Address Register */ + unsigned int ibs_fetch_lin_addr_low; + unsigned int ibs_fetch_lin_addr_high; + /* MSRC001_1030 IBS Fetch Control Register */ + unsigned int ibs_fetch_ctl_low; + unsigned int ibs_fetch_ctl_high; + /* MSRC001_1032 IBS Fetch Physical Address Register */ + unsigned int ibs_fetch_phys_addr_low; + unsigned int ibs_fetch_phys_addr_high; +}; + +struct ibs_op_sample { + /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ + unsigned int ibs_op_rip_low; + unsigned int ibs_op_rip_high; + /* MSRC001_1035 IBS Op Data Register */ + unsigned int ibs_op_data1_low; + unsigned int ibs_op_data1_high; + /* MSRC001_1036 IBS Op Data 2 Register */ + unsigned int ibs_op_data2_low; + unsigned int ibs_op_data2_high; + /* MSRC001_1037 IBS Op Data 3 Register */ + unsigned int ibs_op_data3_low; + unsigned int ibs_op_data3_high; + /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ + unsigned int ibs_dc_linear_low; + unsigned int ibs_dc_linear_high; + /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ + unsigned int ibs_dc_phys_low; + unsigned int ibs_dc_phys_high; +}; + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ +*/ +static void clear_ibs_nmi(void); + static unsigned long reset_value[NUM_COUNTERS]; +static int ibs_allowed; /* AMD Family10h and later */ + +struct op_ibs_config { + unsigned long op_enabled; + unsigned long fetch_enabled; + unsigned long max_cnt_fetch; + unsigned long max_cnt_op; + unsigned long rand_en; + unsigned long dispatched_ops; +}; + +static struct op_ibs_config ibs_config; /* functions for op_amd_spec */ @@ -121,6 +201,8 @@ static int op_amd_check_ctrs(struct pt_regs * const regs, { unsigned int low, high; int i; + struct ibs_fetch_sample ibs_fetch; + struct ibs_op_sample ibs_op; for (i = 0 ; i < NUM_COUNTERS; ++i) { if (!reset_value[i]) @@ -132,6 +214,65 @@ static int op_amd_check_ctrs(struct pt_regs * const regs, } } + /*If AMD and IBS is available */ + if (ibs_allowed && ibs_config.fetch_enabled) { + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + if (high & IBS_FETCH_VALID_BIT) { + ibs_fetch.ibs_fetch_ctl_high = high; + ibs_fetch.ibs_fetch_ctl_low = low; + rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); + ibs_fetch.ibs_fetch_lin_addr_high = high; + ibs_fetch.ibs_fetch_lin_addr_low = low; + rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); + ibs_fetch.ibs_fetch_phys_addr_high = high; + ibs_fetch.ibs_fetch_phys_addr_low = low; + + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_fetch, + IBS_FETCH_BEGIN); + + /*reenable the IRQ */ + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + high &= ~(IBS_FETCH_VALID_BIT); + high |= IBS_FETCH_ENABLE; + low &= IBS_FETCH_CTL_MAX_CNT_MASK; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + } + + if (ibs_allowed && ibs_config.op_enabled) { + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + if (low & IBS_OP_VALID_BIT) { + rdmsr(MSR_AMD64_IBSOPRIP, low, high); + ibs_op.ibs_op_rip_low = low; + ibs_op.ibs_op_rip_high = high; + rdmsr(MSR_AMD64_IBSOPDATA, low, high); + ibs_op.ibs_op_data1_low = low; + ibs_op.ibs_op_data1_high = high; + rdmsr(MSR_AMD64_IBSOPDATA2, low, high); + ibs_op.ibs_op_data2_low = low; + ibs_op.ibs_op_data2_high = high; + rdmsr(MSR_AMD64_IBSOPDATA3, low, high); + ibs_op.ibs_op_data3_low = low; + ibs_op.ibs_op_data3_high = high; + rdmsr(MSR_AMD64_IBSDCLINAD, low, high); + ibs_op.ibs_dc_linear_low = low; + ibs_op.ibs_dc_linear_high = high; + rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); + ibs_op.ibs_dc_phys_low = low; + ibs_op.ibs_dc_phys_high = high; + + /* reenable the IRQ */ + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_op, + IBS_OP_BEGIN); + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + low &= ~(IBS_OP_VALID_BIT); + low |= IBS_OP_ENABLE; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } + } + /* See op_model_ppro.c */ return 1; } @@ -148,6 +289,17 @@ static void op_amd_start(struct op_msrs const * const msrs) CTRL_WRITE(low, high, msrs, i); } } + if (ibs_allowed && ibs_config.fetch_enabled) { + low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; + high = IBS_FETCH_ENABLE; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + IBS_OP_ENABLE; + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } } @@ -165,6 +317,18 @@ static void op_amd_stop(struct op_msrs const * const msrs) CTRL_SET_INACTIVE(low); CTRL_WRITE(low, high, msrs, i); } + + if (ibs_allowed && ibs_config.fetch_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } } static void op_amd_shutdown(struct op_msrs const * const msrs) @@ -181,6 +345,99 @@ static void op_amd_shutdown(struct op_msrs const * const msrs) } } +static inline void apic_init_ibs_nmi_per_cpu(void *arg) +{ + setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); +} + +static inline void apic_clear_ibs_nmi_per_cpu(void *arg) +{ + setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); +} + +/* + * initialize the APIC for the IBS interrupts + * if needed on AMD Family10h rev B0 and later + */ +static void setup_ibs(void) +{ + struct pci_dev *gh_device = NULL; + u32 low, high; + u8 vector; + + ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); + + if (!ibs_allowed) + return; + + /* This gets the APIC_EILVT_LVTOFF_IBS value */ + vector = setup_APIC_eilvt_ibs(0, 0, 1); + + /*see if the IBS control register is already set correctly*/ + /*remove this when we know for sure it is done + in the kernel init*/ + rdmsr(MSR_AMD64_IBSCTL, low, high); + if ((low & (IBS_CTL_LVT_OFFSET_VALID_BIT | vector)) != + (IBS_CTL_LVT_OFFSET_VALID_BIT | vector)) { + + /**** Be sure to run loop until NULL is returned to + decrement reference count on any pci_dev structures + returned ****/ + while ((gh_device = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_10H_NB_MISC, gh_device)) + != NULL) { + /* This code may change if we can find a proper + * way to get at the PCI extended config space */ + pci_write_config_dword( + gh_device, IBS_LVT_OFFSET_PCI, + (vector | IBS_CTL_LVT_OFFSET_VALID_BIT)); + } + } + on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1, 1); +} + + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h + * rev B0 and later */ +static void clear_ibs_nmi(void) +{ + if (ibs_allowed) + on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1, 1); +} + +static void setup_ibs_files(struct super_block *sb, struct dentry *root) +{ + char buf[12]; + struct dentry *dir; + + if (!ibs_allowed) + return; + + /* setup some reasonable defaults */ + ibs_config.max_cnt_fetch = 250000; + ibs_config.fetch_enabled = 0; + ibs_config.max_cnt_op = 250000; + ibs_config.op_enabled = 0; + ibs_config.dispatched_ops = 1; + snprintf(buf, sizeof(buf), "ibs_fetch"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "rand_enable", + &ibs_config.rand_en); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.fetch_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_fetch); + snprintf(buf, sizeof(buf), "ibs_uops"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.op_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_op); + oprofilefs_create_ulong(sb, dir, "dispatched_ops", + &ibs_config.dispatched_ops); +} + static int op_amd_init(struct oprofile_operations *ops) { return 0; -- 1.5.5.4 |