From 6f859c0e96f0737a543610a189d12420c569110f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 16:41:04 -0700 Subject: sparc64: Add detection for features new in SPARC-T4. Compare and branch, pause, and the various new cryptographic opcodes. We advertise the crypto opcodes to userspace using one hwcap bit, HWCAP_SPARC_CRYPTO. This essentially indicates that the %cfr register can be interrograted and used to determine exactly which crypto opcodes are available on the current cpu. We use the %cfr register to report all of the crypto opcodes available in the bootup CPU caps log message, and via /proc/cpuinfo. Signed-off-by: David S. Miller --- arch/sparc/include/asm/elf_64.h | 9 ++++++ arch/sparc/include/asm/pstate.h | 14 +++++++++ arch/sparc/kernel/setup_64.c | 67 +++++++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 12 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h index 7df8b7f544d..370ca1e71ff 100644 --- a/arch/sparc/include/asm/elf_64.h +++ b/arch/sparc/include/asm/elf_64.h @@ -86,6 +86,15 @@ #define AV_SPARC_IMA 0x00400000 /* integer multiply-add */ #define AV_SPARC_ASI_CACHE_SPARING \ 0x00800000 /* cache sparing ASIs available */ +#define AV_SPARC_PAUSE 0x01000000 /* PAUSE available */ +#define AV_SPARC_CBCOND 0x02000000 /* CBCOND insns available */ + +/* Solaris decided to enumerate every single crypto instruction type + * in the AT_HWCAP bits. This is wasteful, since if crypto is present, + * you still need to look in the CFR register to see if the opcode is + * really available. So we simply advertise only "crypto" support. + */ +#define HWCAP_SPARC_CRYPTO 0x04000000 /* CRYPTO insns available */ #define CORE_DUMP_USE_REGSET diff --git a/arch/sparc/include/asm/pstate.h b/arch/sparc/include/asm/pstate.h index a26a53777bb..6dfa8f8c974 100644 --- a/arch/sparc/include/asm/pstate.h +++ b/arch/sparc/include/asm/pstate.h @@ -88,4 +88,18 @@ #define VERS_MAXTL _AC(0x000000000000ff00,UL) /* Max Trap Level. */ #define VERS_MAXWIN _AC(0x000000000000001f,UL) /* Max RegWindow Idx.*/ +/* Compatability Feature Register (%asr26), SPARC-T4 and later */ +#define CFR_AES _AC(0x0000000000000001,UL) /* Supports AES opcodes */ +#define CFR_DES _AC(0x0000000000000002,UL) /* Supports DES opcodes */ +#define CFR_KASUMI _AC(0x0000000000000004,UL) /* Supports KASUMI opcodes */ +#define CFR_CAMELIA _AC(0x0000000000000008,UL) /* Supports CAMELIA opcodes */ +#define CFR_MD5 _AC(0x0000000000000010,UL) /* Supports MD5 opcodes */ +#define CFR_SHA1 _AC(0x0000000000000020,UL) /* Supports SHA1 opcodes */ +#define CFR_SHA256 _AC(0x0000000000000040,UL) /* Supports SHA256 opcodes */ +#define CFR_SHA512 _AC(0x0000000000000080,UL) /* Supports SHA512 opcodes */ +#define CFR_MPMUL _AC(0x0000000000000100,UL) /* Supports MPMUL opcodes */ +#define CFR_MONTMUL _AC(0x0000000000000200,UL) /* Supports MONTMUL opcodes */ +#define CFR_MONTSQR _AC(0x0000000000000400,UL) /* Supports MONTSQR opcodes */ +#define CFR_CRC32C _AC(0x0000000000000800,UL) /* Supports CRC32C opcodes */ + #endif /* !(_SPARC64_PSTATE_H) */ diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 1414d16712b..0800e71d8a8 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -340,7 +340,12 @@ static const char *hwcaps[] = { */ "mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2", "ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau", - "ima", "cspare", + "ima", "cspare", "pause", "cbcond", +}; + +static const char *crypto_hwcaps[] = { + "aes", "des", "kasumi", "camellia", "md5", "sha1", "sha256", + "sha512", "mpmul", "montmul", "montsqr", "crc32c", }; void cpucap_info(struct seq_file *m) @@ -357,27 +362,61 @@ void cpucap_info(struct seq_file *m) printed++; } } + if (caps & HWCAP_SPARC_CRYPTO) { + unsigned long cfr; + + __asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr)); + for (i = 0; i < ARRAY_SIZE(crypto_hwcaps); i++) { + unsigned long bit = 1UL << i; + if (cfr & bit) { + seq_printf(m, "%s%s", + printed ? "," : "", crypto_hwcaps[i]); + printed++; + } + } + } seq_putc(m, '\n'); } +static void __init report_one_hwcap(int *printed, const char *name) +{ + if ((*printed) == 0) + printk(KERN_INFO "CPU CAPS: ["); + printk(KERN_CONT "%s%s", + (*printed) ? "," : "", name); + if (++(*printed) == 8) { + printk(KERN_CONT "]\n"); + *printed = 0; + } +} + +static void __init report_crypto_hwcaps(int *printed) +{ + unsigned long cfr; + int i; + + __asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr)); + + for (i = 0; i < ARRAY_SIZE(crypto_hwcaps); i++) { + unsigned long bit = 1UL << i; + if (cfr & bit) + report_one_hwcap(printed, crypto_hwcaps[i]); + } +} + static void __init report_hwcaps(unsigned long caps) { int i, printed = 0; - printk(KERN_INFO "CPU CAPS: ["); for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { unsigned long bit = 1UL << i; - if (caps & bit) { - printk(KERN_CONT "%s%s", - printed ? "," : "", hwcaps[i]); - if (++printed == 8) { - printk(KERN_CONT "]\n"); - printk(KERN_INFO "CPU CAPS: ["); - printed = 0; - } - } + if (caps & bit) + report_one_hwcap(&printed, hwcaps[i]); } - printk(KERN_CONT "]\n"); + if (caps & HWCAP_SPARC_CRYPTO) + report_crypto_hwcaps(&printed); + if (printed != 0) + printk(KERN_CONT "]\n"); } static unsigned long __init mdesc_cpu_hwcap_list(void) @@ -411,6 +450,10 @@ static unsigned long __init mdesc_cpu_hwcap_list(void) break; } } + for (i = 0; i < ARRAY_SIZE(crypto_hwcaps); i++) { + if (!strcmp(prop, crypto_hwcaps[i])) + caps |= HWCAP_SPARC_CRYPTO; + } plen = strlen(prop) + 1; prop += plen; -- cgit v1.2.3-18-g5258 From 8c79bfa51101354853f0f5d3b02435cec429da51 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 20:35:41 -0700 Subject: sparc64: Add hypervisor interfaces for SPARC-T4 perf counter access. Unlike for previous chips, access to the perf-counter control registers are all hyper-privileged. Therefore, access to them must go through a hypervisor interface. Signed-off-by: David S. Miller --- arch/sparc/include/asm/hypervisor.h | 11 +++++++++++ arch/sparc/kernel/hvapi.c | 1 + arch/sparc/kernel/hvcalls.S | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/hypervisor.h b/arch/sparc/include/asm/hypervisor.h index 015a761eaa3..ca121f0fa3e 100644 --- a/arch/sparc/include/asm/hypervisor.h +++ b/arch/sparc/include/asm/hypervisor.h @@ -2934,6 +2934,16 @@ extern unsigned long sun4v_reboot_data_set(unsigned long ra, unsigned long len); #endif +#define HV_FAST_VT_GET_PERFREG 0x184 +#define HV_FAST_VT_SET_PERFREG 0x185 + +#ifndef __ASSEMBLY__ +extern unsigned long sun4v_vt_get_perfreg(unsigned long reg_num, + unsigned long *reg_val); +extern unsigned long sun4v_vt_set_perfreg(unsigned long reg_num, + unsigned long reg_val); +#endif + /* Function numbers for HV_CORE_TRAP. */ #define HV_CORE_SET_VER 0x00 #define HV_CORE_PUTCHAR 0x01 @@ -2964,6 +2974,7 @@ extern unsigned long sun4v_reboot_data_set(unsigned long ra, #define HV_GRP_NIU 0x0204 #define HV_GRP_VF_CPU 0x0205 #define HV_GRP_KT_CPU 0x0209 +#define HV_GRP_VT_CPU 0x020c #define HV_GRP_DIAG 0x0300 #ifndef __ASSEMBLY__ diff --git a/arch/sparc/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c index 8593672838f..1032df43ec9 100644 --- a/arch/sparc/kernel/hvapi.c +++ b/arch/sparc/kernel/hvapi.c @@ -45,6 +45,7 @@ static struct api_info api_table[] = { { .group = HV_GRP_NIU, }, { .group = HV_GRP_VF_CPU, }, { .group = HV_GRP_KT_CPU, }, + { .group = HV_GRP_VT_CPU, }, { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, }; diff --git a/arch/sparc/kernel/hvcalls.S b/arch/sparc/kernel/hvcalls.S index 58d60de4d65..f3ab509b76a 100644 --- a/arch/sparc/kernel/hvcalls.S +++ b/arch/sparc/kernel/hvcalls.S @@ -805,3 +805,19 @@ ENTRY(sun4v_reboot_data_set) retl nop ENDPROC(sun4v_reboot_data_set) + +ENTRY(sun4v_vt_get_perfreg) + mov %o1, %o4 + mov HV_FAST_VT_GET_PERFREG, %o5 + ta HV_FAST_TRAP + stx %o1, [%o4] + retl + nop +ENDPROC(sun4v_vt_get_perfreg) + +ENTRY(sun4v_vt_set_perfreg) + mov HV_FAST_VT_SET_PERFREG, %o5 + ta HV_FAST_TRAP + retl + nop +ENDPROC(sun4v_vt_set_perfreg) -- cgit v1.2.3-18-g5258 From 0bab20ba4c95f56355c24a0b9f03eb486c2a267d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 21:16:22 -0700 Subject: sparc64: Add 'reg_num' argument to pcr_ops methods. SPARC-T4 and later have multiple PCR registers, one for each PIC counter. Signed-off-by: David S. Miller --- arch/sparc/include/asm/pcr.h | 4 ++-- arch/sparc/kernel/nmi.c | 14 +++++++------- arch/sparc/kernel/pcr.c | 9 ++++++--- arch/sparc/kernel/perf_event.c | 14 +++++++------- 4 files changed, 22 insertions(+), 19 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h index 288d7beba05..55e23416eef 100644 --- a/arch/sparc/include/asm/pcr.h +++ b/arch/sparc/include/asm/pcr.h @@ -2,8 +2,8 @@ #define __PCR_H struct pcr_ops { - u64 (*read)(void); - void (*write)(u64); + u64 (*read)(unsigned long); + void (*write)(unsigned long, u64); }; extern const struct pcr_ops *pcr_ops; diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index eb1c1f010a4..95df720a14a 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -109,7 +109,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) touched = 1; else - pcr_ops->write(PCR_PIC_PRIV); + pcr_ops->write(0, PCR_PIC_PRIV); sum = local_cpu_data().irq0_irqs; if (__get_cpu_var(nmi_touch)) { @@ -127,7 +127,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) } if (__get_cpu_var(wd_enabled)) { write_pic(picl_value(nmi_hz)); - pcr_ops->write(pcr_enable); + pcr_ops->write(0, pcr_enable); } restore_hardirq_stack(orig_sp); @@ -166,7 +166,7 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) void stop_nmi_watchdog(void *unused) { - pcr_ops->write(PCR_PIC_PRIV); + pcr_ops->write(0, PCR_PIC_PRIV); __get_cpu_var(wd_enabled) = 0; atomic_dec(&nmi_active); } @@ -223,10 +223,10 @@ void start_nmi_watchdog(void *unused) __get_cpu_var(wd_enabled) = 1; atomic_inc(&nmi_active); - pcr_ops->write(PCR_PIC_PRIV); + pcr_ops->write(0, PCR_PIC_PRIV); write_pic(picl_value(nmi_hz)); - pcr_ops->write(pcr_enable); + pcr_ops->write(0, pcr_enable); } static void nmi_adjust_hz_one(void *unused) @@ -234,10 +234,10 @@ static void nmi_adjust_hz_one(void *unused) if (!__get_cpu_var(wd_enabled)) return; - pcr_ops->write(PCR_PIC_PRIV); + pcr_ops->write(0, PCR_PIC_PRIV); write_pic(picl_value(nmi_hz)); - pcr_ops->write(pcr_enable); + pcr_ops->write(0, pcr_enable); } void nmi_adjust_hz(unsigned int new_hz) diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 0ce0dd2332a..3d9ab5be23d 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -60,16 +60,18 @@ void arch_irq_work_raise(void) const struct pcr_ops *pcr_ops; EXPORT_SYMBOL_GPL(pcr_ops); -static u64 direct_pcr_read(void) +static u64 direct_pcr_read(unsigned long reg_num) { u64 val; + WARN_ON_ONCE(reg_num != 0); read_pcr(val); return val; } -static void direct_pcr_write(u64 val) +static void direct_pcr_write(unsigned long reg_num, u64 val) { + WARN_ON_ONCE(reg_num != 0); write_pcr(val); } @@ -78,10 +80,11 @@ static const struct pcr_ops direct_pcr_ops = { .write = direct_pcr_write, }; -static void n2_pcr_write(u64 val) +static void n2_pcr_write(unsigned long reg_num, u64 val) { unsigned long ret; + WARN_ON_ONCE(reg_num != 0); if (val & PCR_N2_HTRACE) { ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); if (ret != HV_EOK) diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 5713957dcb8..e1c9848c39c 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -564,7 +564,7 @@ static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_ val |= hwc->config; cpuc->pcr = val; - pcr_ops->write(cpuc->pcr); + pcr_ops->write(0, cpuc->pcr); } static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx) @@ -578,7 +578,7 @@ static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw val |= nop; cpuc->pcr = val; - pcr_ops->write(cpuc->pcr); + pcr_ops->write(0, cpuc->pcr); } static u32 read_pmc(int idx) @@ -736,7 +736,7 @@ static void sparc_pmu_enable(struct pmu *pmu) cpuc->pcr = pcr | cpuc->event[0]->hw.config_base; } - pcr_ops->write(cpuc->pcr); + pcr_ops->write(0, cpuc->pcr); } static void sparc_pmu_disable(struct pmu *pmu) @@ -755,7 +755,7 @@ static void sparc_pmu_disable(struct pmu *pmu) sparc_pmu->hv_bit | sparc_pmu->irq_bit); cpuc->pcr = val; - pcr_ops->write(cpuc->pcr); + pcr_ops->write(0, cpuc->pcr); } static int active_event_index(struct cpu_hw_events *cpuc, @@ -856,7 +856,7 @@ static void perf_stop_nmi_watchdog(void *unused) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); stop_nmi_watchdog(NULL); - cpuc->pcr = pcr_ops->read(); + cpuc->pcr = pcr_ops->read(0); } void perf_event_grab_pmc(void) @@ -1264,7 +1264,7 @@ void perf_event_print_debug(void) cpu = smp_processor_id(); - pcr = pcr_ops->read(); + pcr = pcr_ops->read(0); read_pic(pic); pr_info("\n"); @@ -1306,7 +1306,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, * overflow so we don't lose any events. */ if (sparc_pmu->irq_bit) - pcr_ops->write(cpuc->pcr); + pcr_ops->write(0, cpuc->pcr); for (i = 0; i < cpuc->n_events; i++) { struct perf_event *event = cpuc->event[i]; -- cgit v1.2.3-18-g5258 From 09d053c797f4a559af0647e4283b9b9ec0682d10 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 23:19:32 -0700 Subject: sparc64: Abstract away PIC register accesses. And, like for the PCR, allow indexing of different PIC register numbers. This also removes all of the non-__KERNEL__ bits from asm/perfctr.h, nothing kernel side should include it any more. Signed-off-by: David S. Miller --- arch/sparc/include/asm/pcr.h | 6 ++++-- arch/sparc/include/asm/perfctr.h | 30 --------------------------- arch/sparc/kernel/nmi.c | 21 +++++++++---------- arch/sparc/kernel/pcr.c | 45 ++++++++++++++++++++++++++++++++-------- arch/sparc/kernel/perf_event.c | 23 ++++++++++---------- 5 files changed, 61 insertions(+), 64 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h index 55e23416eef..d53abf75198 100644 --- a/arch/sparc/include/asm/pcr.h +++ b/arch/sparc/include/asm/pcr.h @@ -2,8 +2,10 @@ #define __PCR_H struct pcr_ops { - u64 (*read)(unsigned long); - void (*write)(unsigned long, u64); + u64 (*read_pcr)(unsigned long); + void (*write_pcr)(unsigned long, u64); + u64 (*read_pic)(unsigned long); + void (*write_pic)(unsigned long, u64); }; extern const struct pcr_ops *pcr_ops; diff --git a/arch/sparc/include/asm/perfctr.h b/arch/sparc/include/asm/perfctr.h index 3332d2cba6c..214feefa577 100644 --- a/arch/sparc/include/asm/perfctr.h +++ b/arch/sparc/include/asm/perfctr.h @@ -54,11 +54,6 @@ enum perfctr_opcode { PERFCTR_GETPCR }; -/* I don't want the kernel's namespace to be polluted with this - * stuff when this file is included. --DaveM - */ -#ifndef __KERNEL__ - #define PRIV 0x00000001 #define SYS 0x00000002 #define USR 0x00000004 @@ -168,29 +163,4 @@ struct vcounter_struct { unsigned long long vcnt1; }; -#else /* !(__KERNEL__) */ - -#ifndef CONFIG_SPARC32 - -/* Performance counter register access. */ -#define read_pcr(__p) __asm__ __volatile__("rd %%pcr, %0" : "=r" (__p)) -#define write_pcr(__p) __asm__ __volatile__("wr %0, 0x0, %%pcr" : : "r" (__p)) -#define read_pic(__p) __asm__ __volatile__("rd %%pic, %0" : "=r" (__p)) - -/* Blackbird errata workaround. See commentary in - * arch/sparc64/kernel/smp.c:smp_percpu_timer_interrupt() - * for more information. - */ -#define write_pic(__p) \ - __asm__ __volatile__("ba,pt %%xcc, 99f\n\t" \ - " nop\n\t" \ - ".align 64\n" \ - "99:wr %0, 0x0, %%pic\n\t" \ - "rd %%pic, %%g0" : : "r" (__p)) -#define reset_pic() write_pic(0) - -#endif /* !CONFIG_SPARC32 */ - -#endif /* !(__KERNEL__) */ - #endif /* !(PERF_COUNTER_API) */ diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 95df720a14a..ef22b9bacf1 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "kstack.h" @@ -109,7 +108,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) touched = 1; else - pcr_ops->write(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, PCR_PIC_PRIV); sum = local_cpu_data().irq0_irqs; if (__get_cpu_var(nmi_touch)) { @@ -126,8 +125,8 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) __this_cpu_write(alert_counter, 0); } if (__get_cpu_var(wd_enabled)) { - write_pic(picl_value(nmi_hz)); - pcr_ops->write(0, pcr_enable); + pcr_ops->write_pic(0, picl_value(nmi_hz)); + pcr_ops->write_pcr(0, pcr_enable); } restore_hardirq_stack(orig_sp); @@ -166,7 +165,7 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) void stop_nmi_watchdog(void *unused) { - pcr_ops->write(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, PCR_PIC_PRIV); __get_cpu_var(wd_enabled) = 0; atomic_dec(&nmi_active); } @@ -223,10 +222,10 @@ void start_nmi_watchdog(void *unused) __get_cpu_var(wd_enabled) = 1; atomic_inc(&nmi_active); - pcr_ops->write(0, PCR_PIC_PRIV); - write_pic(picl_value(nmi_hz)); + pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pic(0, picl_value(nmi_hz)); - pcr_ops->write(0, pcr_enable); + pcr_ops->write_pcr(0, pcr_enable); } static void nmi_adjust_hz_one(void *unused) @@ -234,10 +233,10 @@ static void nmi_adjust_hz_one(void *unused) if (!__get_cpu_var(wd_enabled)) return; - pcr_ops->write(0, PCR_PIC_PRIV); - write_pic(picl_value(nmi_hz)); + pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pic(0, picl_value(nmi_hz)); - pcr_ops->write(0, pcr_enable); + pcr_ops->write_pcr(0, pcr_enable); } void nmi_adjust_hz(unsigned int new_hz) diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 3d9ab5be23d..e82ae89666f 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -14,7 +14,6 @@ #include #include #include -#include /* This code is shared between various users of the performance * counters. Users will be oprofile, pseudo-NMI watchdog, and the @@ -65,19 +64,45 @@ static u64 direct_pcr_read(unsigned long reg_num) u64 val; WARN_ON_ONCE(reg_num != 0); - read_pcr(val); + __asm__ __volatile__("rd %%pcr, %0" : "=r" (val)); return val; } static void direct_pcr_write(unsigned long reg_num, u64 val) { WARN_ON_ONCE(reg_num != 0); - write_pcr(val); + __asm__ __volatile__("wr %0, 0x0, %%pcr" : : "r" (val)); +} + +static u64 direct_pic_read(unsigned long reg_num) +{ + u64 val; + + WARN_ON_ONCE(reg_num != 0); + __asm__ __volatile__("rd %%pic, %0" : "=r" (val)); + return val; +} + +static void direct_pic_write(unsigned long reg_num, u64 val) +{ + WARN_ON_ONCE(reg_num != 0); + + /* Blackbird errata workaround. See commentary in + * arch/sparc64/kernel/smp.c:smp_percpu_timer_interrupt() + * for more information. + */ + __asm__ __volatile__("ba,pt %%xcc, 99f\n\t" + " nop\n\t" + ".align 64\n" + "99:wr %0, 0x0, %%pic\n\t" + "rd %%pic, %%g0" : : "r" (val)); } static const struct pcr_ops direct_pcr_ops = { - .read = direct_pcr_read, - .write = direct_pcr_write, + .read_pcr = direct_pcr_read, + .write_pcr = direct_pcr_write, + .read_pic = direct_pic_read, + .write_pic = direct_pic_write, }; static void n2_pcr_write(unsigned long reg_num, u64 val) @@ -88,14 +113,16 @@ static void n2_pcr_write(unsigned long reg_num, u64 val) if (val & PCR_N2_HTRACE) { ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); if (ret != HV_EOK) - write_pcr(val); + direct_pcr_write(reg_num, val); } else - write_pcr(val); + direct_pcr_write(reg_num, val); } static const struct pcr_ops n2_pcr_ops = { - .read = direct_pcr_read, - .write = n2_pcr_write, + .read_pcr = direct_pcr_read, + .write_pcr = n2_pcr_write, + .read_pic = direct_pic_read, + .write_pic = direct_pic_write, }; static unsigned long perf_hsvc_group; diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index e1c9848c39c..dd12aa35805 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "kernel.h" @@ -564,7 +563,7 @@ static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_ val |= hwc->config; cpuc->pcr = val; - pcr_ops->write(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr); } static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx) @@ -578,14 +577,14 @@ static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw val |= nop; cpuc->pcr = val; - pcr_ops->write(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr); } static u32 read_pmc(int idx) { u64 val; - read_pic(val); + val = pcr_ops->read_pic(0); if (idx == PIC_UPPER_INDEX) val >>= 32; @@ -603,10 +602,10 @@ static void write_pmc(int idx, u64 val) mask = ((u64) 0xffffffff) << shift; val <<= shift; - read_pic(pic); + pic = pcr_ops->read_pic(0); pic &= ~mask; pic |= val; - write_pic(pic); + pcr_ops->write_pic(0, pic); } static u64 sparc_perf_event_update(struct perf_event *event, @@ -736,7 +735,7 @@ static void sparc_pmu_enable(struct pmu *pmu) cpuc->pcr = pcr | cpuc->event[0]->hw.config_base; } - pcr_ops->write(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr); } static void sparc_pmu_disable(struct pmu *pmu) @@ -755,7 +754,7 @@ static void sparc_pmu_disable(struct pmu *pmu) sparc_pmu->hv_bit | sparc_pmu->irq_bit); cpuc->pcr = val; - pcr_ops->write(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr); } static int active_event_index(struct cpu_hw_events *cpuc, @@ -856,7 +855,7 @@ static void perf_stop_nmi_watchdog(void *unused) struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); stop_nmi_watchdog(NULL); - cpuc->pcr = pcr_ops->read(0); + cpuc->pcr = pcr_ops->read_pcr(0); } void perf_event_grab_pmc(void) @@ -1264,8 +1263,8 @@ void perf_event_print_debug(void) cpu = smp_processor_id(); - pcr = pcr_ops->read(0); - read_pic(pic); + pcr = pcr_ops->read_pcr(0); + pic = pcr_ops->read_pic(0); pr_info("\n"); pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n", @@ -1306,7 +1305,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, * overflow so we don't lose any events. */ if (sparc_pmu->irq_bit) - pcr_ops->write(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr); for (i = 0; i < cpuc->n_events; i++) { struct perf_event *event = cpuc->event[i]; -- cgit v1.2.3-18-g5258 From 73a6b0538c131d489fe7a2581deddb72faca496b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 23:26:01 -0700 Subject: sparc64: Abstract away the NMI PIC counter computation. Signed-off-by: David S. Miller --- arch/sparc/include/asm/pcr.h | 15 +-------------- arch/sparc/kernel/nmi.c | 6 +++--- arch/sparc/kernel/pcr.c | 18 ++++++++++++++++-- 3 files changed, 20 insertions(+), 19 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h index d53abf75198..4c71bec2065 100644 --- a/arch/sparc/include/asm/pcr.h +++ b/arch/sparc/include/asm/pcr.h @@ -6,6 +6,7 @@ struct pcr_ops { void (*write_pcr)(unsigned long, u64); u64 (*read_pic)(unsigned long); void (*write_pic)(unsigned long, u64); + u64 (*nmi_picl_value)(unsigned int nmi_hz); }; extern const struct pcr_ops *pcr_ops; @@ -29,20 +30,6 @@ extern void schedule_deferred_pcr_work(void); #define PCR_N2_SL1_SHIFT 27 #define PCR_N2_OV1 0x80000000 -extern unsigned int picl_shift; - -/* In order to commonize as much of the implementation as - * possible, we use PICH as our counter. Mostly this is - * to accommodate Niagara-1 which can only count insn cycles - * in PICH. - */ -static inline u64 picl_value(unsigned int nmi_hz) -{ - u32 delta = local_cpu_data().clock_tick / (nmi_hz << picl_shift); - - return ((u64)((0 - delta) & 0xffffffff)) << 32; -} - extern u64 pcr_enable; extern int pcr_arch_init(void); diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index ef22b9bacf1..4c45158d4c8 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -125,7 +125,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) __this_cpu_write(alert_counter, 0); } if (__get_cpu_var(wd_enabled)) { - pcr_ops->write_pic(0, picl_value(nmi_hz)); + pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); pcr_ops->write_pcr(0, pcr_enable); } @@ -223,7 +223,7 @@ void start_nmi_watchdog(void *unused) atomic_inc(&nmi_active); pcr_ops->write_pcr(0, PCR_PIC_PRIV); - pcr_ops->write_pic(0, picl_value(nmi_hz)); + pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); pcr_ops->write_pcr(0, pcr_enable); } @@ -234,7 +234,7 @@ static void nmi_adjust_hz_one(void *unused) return; pcr_ops->write_pcr(0, PCR_PIC_PRIV); - pcr_ops->write_pic(0, picl_value(nmi_hz)); + pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); pcr_ops->write_pcr(0, pcr_enable); } diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index e82ae89666f..494af3227f3 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -27,7 +27,6 @@ (0xff << PCR_N2_MASK1_SHIFT)) u64 pcr_enable; -unsigned int picl_shift; /* Performance counter interrupts run unmasked at PIL level 15. * Therefore we can't do things like wakeups and other work @@ -98,11 +97,19 @@ static void direct_pic_write(unsigned long reg_num, u64 val) "rd %%pic, %%g0" : : "r" (val)); } +static u64 direct_picl_value(unsigned int nmi_hz) +{ + u32 delta = local_cpu_data().clock_tick / nmi_hz; + + return ((u64)((0 - delta) & 0xffffffff)) << 32; +} + static const struct pcr_ops direct_pcr_ops = { .read_pcr = direct_pcr_read, .write_pcr = direct_pcr_write, .read_pic = direct_pic_read, .write_pic = direct_pic_write, + .nmi_picl_value = direct_picl_value, }; static void n2_pcr_write(unsigned long reg_num, u64 val) @@ -118,11 +125,19 @@ static void n2_pcr_write(unsigned long reg_num, u64 val) direct_pcr_write(reg_num, val); } +static u64 n2_picl_value(unsigned int nmi_hz) +{ + u32 delta = local_cpu_data().clock_tick / (nmi_hz << 2); + + return ((u64)((0 - delta) & 0xffffffff)) << 32; +} + static const struct pcr_ops n2_pcr_ops = { .read_pcr = direct_pcr_read, .write_pcr = n2_pcr_write, .read_pic = direct_pic_read, .write_pic = direct_pic_write, + .nmi_picl_value = n2_picl_value, }; static unsigned long perf_hsvc_group; @@ -180,7 +195,6 @@ int __init pcr_arch_init(void) case hypervisor: pcr_ops = &n2_pcr_ops; pcr_enable = PCR_N2_ENABLE; - picl_shift = 2; break; case cheetah: -- cgit v1.2.3-18-g5258 From ce4a925c29208cf48084d9fa174d965a65246a8d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 16 Aug 2012 23:31:59 -0700 Subject: sparc64: Abstract away the %pcr values used to enable/disable NMI We assumed PCR_PIC_PRIV can always be used to disable it, but that won't be true for SPARC-T4. This allows us also to get rid of some messy defines used in only one location. Signed-off-by: David S. Miller --- arch/sparc/include/asm/pcr.h | 4 ++-- arch/sparc/kernel/nmi.c | 14 +++++++------- arch/sparc/kernel/pcr.c | 37 +++++++++++++++++-------------------- 3 files changed, 26 insertions(+), 29 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h index 4c71bec2065..9ebc7f3840d 100644 --- a/arch/sparc/include/asm/pcr.h +++ b/arch/sparc/include/asm/pcr.h @@ -7,6 +7,8 @@ struct pcr_ops { u64 (*read_pic)(unsigned long); void (*write_pic)(unsigned long, u64); u64 (*nmi_picl_value)(unsigned int nmi_hz); + u64 pcr_nmi_enable; + u64 pcr_nmi_disable; }; extern const struct pcr_ops *pcr_ops; @@ -30,8 +32,6 @@ extern void schedule_deferred_pcr_work(void); #define PCR_N2_SL1_SHIFT 27 #define PCR_N2_OV1 0x80000000 -extern u64 pcr_enable; - extern int pcr_arch_init(void); #endif /* __PCR_H */ diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 4c45158d4c8..6479256fd5a 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -108,7 +108,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) pt_regs_trap_type(regs), SIGINT) == NOTIFY_STOP) touched = 1; else - pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); sum = local_cpu_data().irq0_irqs; if (__get_cpu_var(nmi_touch)) { @@ -126,7 +126,7 @@ notrace __kprobes void perfctr_irq(int irq, struct pt_regs *regs) } if (__get_cpu_var(wd_enabled)) { pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); - pcr_ops->write_pcr(0, pcr_enable); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_enable); } restore_hardirq_stack(orig_sp); @@ -165,7 +165,7 @@ static void report_broken_nmi(int cpu, int *prev_nmi_count) void stop_nmi_watchdog(void *unused) { - pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); __get_cpu_var(wd_enabled) = 0; atomic_dec(&nmi_active); } @@ -222,10 +222,10 @@ void start_nmi_watchdog(void *unused) __get_cpu_var(wd_enabled) = 1; atomic_inc(&nmi_active); - pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); - pcr_ops->write_pcr(0, pcr_enable); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_enable); } static void nmi_adjust_hz_one(void *unused) @@ -233,10 +233,10 @@ static void nmi_adjust_hz_one(void *unused) if (!__get_cpu_var(wd_enabled)) return; - pcr_ops->write_pcr(0, PCR_PIC_PRIV); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_disable); pcr_ops->write_pic(0, pcr_ops->nmi_picl_value(nmi_hz)); - pcr_ops->write_pcr(0, pcr_enable); + pcr_ops->write_pcr(0, pcr_ops->pcr_nmi_enable); } void nmi_adjust_hz(unsigned int new_hz) diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 494af3227f3..e408fc5d0c1 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -20,14 +20,6 @@ * perf_event support layer. */ -#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) -#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \ - PCR_N2_TOE_OV1 | \ - (2 << PCR_N2_SL1_SHIFT) | \ - (0xff << PCR_N2_MASK1_SHIFT)) - -u64 pcr_enable; - /* Performance counter interrupts run unmasked at PIL level 15. * Therefore we can't do things like wakeups and other work * that expects IRQ disabling to be adhered to in locking etc. @@ -105,11 +97,13 @@ static u64 direct_picl_value(unsigned int nmi_hz) } static const struct pcr_ops direct_pcr_ops = { - .read_pcr = direct_pcr_read, - .write_pcr = direct_pcr_write, - .read_pic = direct_pic_read, - .write_pic = direct_pic_write, - .nmi_picl_value = direct_picl_value, + .read_pcr = direct_pcr_read, + .write_pcr = direct_pcr_write, + .read_pic = direct_pic_read, + .write_pic = direct_pic_write, + .nmi_picl_value = direct_picl_value, + .pcr_nmi_enable = (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE), + .pcr_nmi_disable = PCR_PIC_PRIV, }; static void n2_pcr_write(unsigned long reg_num, u64 val) @@ -133,11 +127,16 @@ static u64 n2_picl_value(unsigned int nmi_hz) } static const struct pcr_ops n2_pcr_ops = { - .read_pcr = direct_pcr_read, - .write_pcr = n2_pcr_write, - .read_pic = direct_pic_read, - .write_pic = direct_pic_write, - .nmi_picl_value = n2_picl_value, + .read_pcr = direct_pcr_read, + .write_pcr = n2_pcr_write, + .read_pic = direct_pic_read, + .write_pic = direct_pic_write, + .nmi_picl_value = n2_picl_value, + .pcr_nmi_enable = (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | + PCR_N2_TOE_OV1 | + (2 << PCR_N2_SL1_SHIFT) | + (0xff << PCR_N2_MASK1_SHIFT)), + .pcr_nmi_disable = PCR_PIC_PRIV, }; static unsigned long perf_hsvc_group; @@ -194,13 +193,11 @@ int __init pcr_arch_init(void) switch (tlb_type) { case hypervisor: pcr_ops = &n2_pcr_ops; - pcr_enable = PCR_N2_ENABLE; break; case cheetah: case cheetah_plus: pcr_ops = &direct_pcr_ops; - pcr_enable = PCR_SUN4U_ENABLE; break; case spitfire: -- cgit v1.2.3-18-g5258 From 6faaeb8ea30e55c9fd7cf65d05f3ce44973d1d12 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 00:20:39 -0700 Subject: sparc64: Add PCR ops for SPARC-T4. This is enough to get the NMIs working, more work is needed for perf events. Signed-off-by: David S. Miller --- arch/sparc/include/asm/asi.h | 4 ++- arch/sparc/include/asm/pcr.h | 13 +++++++ arch/sparc/kernel/pcr.c | 83 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/asi.h b/arch/sparc/include/asm/asi.h index 61ebe7411ce..cc0006dc5d4 100644 --- a/arch/sparc/include/asm/asi.h +++ b/arch/sparc/include/asm/asi.h @@ -141,7 +141,8 @@ /* SpitFire and later extended ASIs. The "(III)" marker designates * UltraSparc-III and later specific ASIs. The "(CMT)" marker designates * Chip Multi Threading specific ASIs. "(NG)" designates Niagara specific - * ASIs, "(4V)" designates SUN4V specific ASIs. + * ASIs, "(4V)" designates SUN4V specific ASIs. "(NG4)" designates SPARC-T4 + * and later ASIs. */ #define ASI_PHYS_USE_EC 0x14 /* PADDR, E-cachable */ #define ASI_PHYS_BYPASS_EC_E 0x15 /* PADDR, E-bit */ @@ -243,6 +244,7 @@ #define ASI_UDBL_CONTROL_R 0x7f /* External UDB control regs rd low*/ #define ASI_INTR_R 0x7f /* IRQ vector dispatch read */ #define ASI_INTR_DATAN_R 0x7f /* (III) In irq vector data reg N */ +#define ASI_PIC 0xb0 /* (NG4) PIC registers */ #define ASI_PST8_P 0xc0 /* Primary, 8 8-bit, partial */ #define ASI_PST8_S 0xc1 /* Secondary, 8 8-bit, partial */ #define ASI_PST16_P 0xc2 /* Primary, 4 16-bit, partial */ diff --git a/arch/sparc/include/asm/pcr.h b/arch/sparc/include/asm/pcr.h index 9ebc7f3840d..942bb17f60c 100644 --- a/arch/sparc/include/asm/pcr.h +++ b/arch/sparc/include/asm/pcr.h @@ -32,6 +32,19 @@ extern void schedule_deferred_pcr_work(void); #define PCR_N2_SL1_SHIFT 27 #define PCR_N2_OV1 0x80000000 +#define PCR_N4_OV 0x00000001 /* PIC overflow */ +#define PCR_N4_TOE 0x00000002 /* Trap On Event */ +#define PCR_N4_UTRACE 0x00000004 /* Trace user events */ +#define PCR_N4_STRACE 0x00000008 /* Trace supervisor events */ +#define PCR_N4_HTRACE 0x00000010 /* Trace hypervisor events */ +#define PCR_N4_MASK 0x000007e0 /* Event mask */ +#define PCR_N4_MASK_SHIFT 5 +#define PCR_N4_SL 0x0000f800 /* Event Select */ +#define PCR_N4_SL_SHIFT 11 +#define PCR_N4_PICNPT 0x00010000 /* PIC non-privileged trap */ +#define PCR_N4_PICNHT 0x00020000 /* PIC non-hypervisor trap */ +#define PCR_N4_NTC 0x00040000 /* Next-To-Commit wrap */ + extern int pcr_arch_init(void); #endif /* __PCR_H */ diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index e408fc5d0c1..269af58497a 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* This code is shared between various users of the performance @@ -139,6 +140,57 @@ static const struct pcr_ops n2_pcr_ops = { .pcr_nmi_disable = PCR_PIC_PRIV, }; +static u64 n4_pcr_read(unsigned long reg_num) +{ + unsigned long val; + + (void) sun4v_vt_get_perfreg(reg_num, &val); + + return val; +} + +static void n4_pcr_write(unsigned long reg_num, u64 val) +{ + (void) sun4v_vt_set_perfreg(reg_num, val); +} + +static u64 n4_pic_read(unsigned long reg_num) +{ + unsigned long val; + + __asm__ __volatile__("ldxa [%1] %2, %0" + : "=r" (val) + : "r" (reg_num * 0x8UL), "i" (ASI_PIC)); + + return val; +} + +static void n4_pic_write(unsigned long reg_num, u64 val) +{ + __asm__ __volatile__("stxa %0, [%1] %2" + : /* no outputs */ + : "r" (val), "r" (reg_num * 0x8UL), "i" (ASI_PIC)); +} + +static u64 n4_picl_value(unsigned int nmi_hz) +{ + u32 delta = local_cpu_data().clock_tick / (nmi_hz << 2); + + return ((u64)((0 - delta) & 0xffffffff)); +} + +static const struct pcr_ops n4_pcr_ops = { + .read_pcr = n4_pcr_read, + .write_pcr = n4_pcr_write, + .read_pic = n4_pic_read, + .write_pic = n4_pic_write, + .nmi_picl_value = n4_picl_value, + .pcr_nmi_enable = (PCR_N4_PICNPT | PCR_N4_STRACE | + PCR_N4_UTRACE | PCR_N4_TOE | + (26 << PCR_N4_SL_SHIFT)), + .pcr_nmi_disable = PCR_N4_PICNPT, +}; + static unsigned long perf_hsvc_group; static unsigned long perf_hsvc_major; static unsigned long perf_hsvc_minor; @@ -159,6 +211,10 @@ static int __init register_perf_hsvc(void) perf_hsvc_group = HV_GRP_KT_CPU; break; + case SUN4V_CHIP_NIAGARA4: + perf_hsvc_group = HV_GRP_VT_CPU; + break; + default: return -ENODEV; } @@ -183,6 +239,29 @@ static void __init unregister_perf_hsvc(void) sun4v_hvapi_unregister(perf_hsvc_group); } +static int __init setup_sun4v_pcr_ops(void) +{ + int ret = 0; + + switch (sun4v_chip_type) { + case SUN4V_CHIP_NIAGARA1: + case SUN4V_CHIP_NIAGARA2: + case SUN4V_CHIP_NIAGARA3: + pcr_ops = &n2_pcr_ops; + break; + + case SUN4V_CHIP_NIAGARA4: + pcr_ops = &n4_pcr_ops; + break; + + default: + ret = -ENODEV; + break; + } + + return ret; +} + int __init pcr_arch_init(void) { int err = register_perf_hsvc(); @@ -192,7 +271,9 @@ int __init pcr_arch_init(void) switch (tlb_type) { case hypervisor: - pcr_ops = &n2_pcr_ops; + err = setup_sun4v_pcr_ops(); + if (err) + goto out_unregister; break; case cheetah: -- cgit v1.2.3-18-g5258 From b38e99f5bdf62f37d7552311fef1bff00bec6308 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 02:31:10 -0700 Subject: sparc64: Add perf_event abstractions for orthogonal PMUs. Starting with SPARC-T4 we have a seperate PCR control register for each performance counter, and there are absolutely no restrictions on what events can run on which counters. Add flags that we can use to elide the conflict and dependency logic used to handle older chips. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index dd12aa35805..11b424bb0b2 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -148,6 +148,9 @@ struct sparc_pmu { int irq_bit; int upper_nop; int lower_nop; + unsigned int flags; +#define SPARC_PMU_ALL_EXCLUDES_SAME 0x00000001 +#define SPARC_PMU_HAS_CONFLICTS 0x00000002 }; static const struct perf_event_map ultra3_perfmon_event_map[] = { @@ -272,6 +275,8 @@ static const struct sparc_pmu ultra3_pmu = { .event_mask = 0x3f, .upper_nop = 0x1c, .lower_nop = 0x14, + .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | + SPARC_PMU_HAS_CONFLICTS), }; /* Niagara1 is very limited. The upper PIC is hard-locked to count @@ -401,6 +406,8 @@ static const struct sparc_pmu niagara1_pmu = { .event_mask = 0x7, .upper_nop = 0x0, .lower_nop = 0x0, + .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | + SPARC_PMU_HAS_CONFLICTS), }; static const struct perf_event_map niagara2_perfmon_event_map[] = { @@ -529,6 +536,8 @@ static const struct sparc_pmu niagara2_pmu = { .irq_bit = 0x30, .upper_nop = 0x220, .lower_nop = 0x220, + .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | + SPARC_PMU_HAS_CONFLICTS), }; static const struct sparc_pmu *sparc_pmu __read_mostly; @@ -944,6 +953,14 @@ static int sparc_check_constraints(struct perf_event **evts, if (n_ev > MAX_HWEVENTS) return -1; + if (!(sparc_pmu->flags & SPARC_PMU_HAS_CONFLICTS)) { + int i; + + for (i = 0; i < n_ev; i++) + evts[i]->hw.idx = i; + return 0; + } + msk0 = perf_event_get_msk(events[0]); if (n_ev == 1) { if (msk0 & PIC_LOWER) @@ -999,6 +1016,9 @@ static int check_excludes(struct perf_event **evts, int n_prev, int n_new) struct perf_event *event; int i, n, first; + if (!(sparc_pmu->flags & SPARC_PMU_ALL_EXCLUDES_SAME)) + return 0; + n = n_prev + n_new; if (n <= 1) return 0; -- cgit v1.2.3-18-g5258 From 59660495e80e7eabc726c301ddc46afd2ce1bcac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 02:33:44 -0700 Subject: sparc64: Allow max hw perf events to be variable. Now specified in sparc_pmu descriptor. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 11b424bb0b2..f7b9ae39c26 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -151,6 +151,7 @@ struct sparc_pmu { unsigned int flags; #define SPARC_PMU_ALL_EXCLUDES_SAME 0x00000001 #define SPARC_PMU_HAS_CONFLICTS 0x00000002 + int max_hw_events; }; static const struct perf_event_map ultra3_perfmon_event_map[] = { @@ -277,6 +278,7 @@ static const struct sparc_pmu ultra3_pmu = { .lower_nop = 0x14, .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), + .max_hw_events = 2, }; /* Niagara1 is very limited. The upper PIC is hard-locked to count @@ -408,6 +410,7 @@ static const struct sparc_pmu niagara1_pmu = { .lower_nop = 0x0, .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), + .max_hw_events = 2, }; static const struct perf_event_map niagara2_perfmon_event_map[] = { @@ -538,6 +541,7 @@ static const struct sparc_pmu niagara2_pmu = { .lower_nop = 0x220, .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), + .max_hw_events = 2, }; static const struct sparc_pmu *sparc_pmu __read_mostly; @@ -950,7 +954,7 @@ static int sparc_check_constraints(struct perf_event **evts, if (!n_ev) return 0; - if (n_ev > MAX_HWEVENTS) + if (n_ev > sparc_pmu->max_hw_events) return -1; if (!(sparc_pmu->flags & SPARC_PMU_HAS_CONFLICTS)) { @@ -1078,7 +1082,7 @@ static int sparc_pmu_add(struct perf_event *event, int ef_flags) perf_pmu_disable(event->pmu); n0 = cpuc->n_events; - if (n0 >= MAX_HWEVENTS) + if (n0 >= sparc_pmu->max_hw_events) goto out; cpuc->event[n0] = event; @@ -1174,7 +1178,7 @@ static int sparc_pmu_event_init(struct perf_event *event) n = 0; if (event->group_leader != event) { n = collect_events(event->group_leader, - MAX_HWEVENTS - 1, + sparc_pmu->max_hw_events - 1, evts, events, current_idx_dmy); if (n < 0) return -EINVAL; -- cgit v1.2.3-18-g5258 From 5344303ca8dad9881def6cfb45ad01201dba16de Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 02:37:06 -0700 Subject: sparc64: Abstract PMC read/write behind sparc_pmu. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 68 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 30 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index f7b9ae39c26..fbd80299a4b 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -141,6 +141,8 @@ struct sparc_pmu { const struct perf_event_map *(*event_map)(int); const cache_map_t *cache_map; int max_events; + u32 (*read_pmc)(int); + void (*write_pmc)(int, u64); int upper_shift; int lower_shift; int event_mask; @@ -154,6 +156,34 @@ struct sparc_pmu { int max_hw_events; }; +static u32 sparc_default_read_pmc(int idx) +{ + u64 val; + + val = pcr_ops->read_pic(0); + if (idx == PIC_UPPER_INDEX) + val >>= 32; + + return val & 0xffffffff; +} + +static void sparc_default_write_pmc(int idx, u64 val) +{ + u64 shift, mask, pic; + + shift = 0; + if (idx == PIC_UPPER_INDEX) + shift = 32; + + mask = ((u64) 0xffffffff) << shift; + val <<= shift; + + pic = pcr_ops->read_pic(0); + pic &= ~mask; + pic |= val; + pcr_ops->write_pic(0, pic); +} + static const struct perf_event_map ultra3_perfmon_event_map[] = { [PERF_COUNT_HW_CPU_CYCLES] = { 0x0000, PIC_UPPER | PIC_LOWER }, [PERF_COUNT_HW_INSTRUCTIONS] = { 0x0001, PIC_UPPER | PIC_LOWER }, @@ -271,6 +301,8 @@ static const struct sparc_pmu ultra3_pmu = { .event_map = ultra3_event_map, .cache_map = &ultra3_cache_map, .max_events = ARRAY_SIZE(ultra3_perfmon_event_map), + .read_pmc = sparc_default_read_pmc, + .write_pmc = sparc_default_write_pmc, .upper_shift = 11, .lower_shift = 4, .event_mask = 0x3f, @@ -403,6 +435,8 @@ static const struct sparc_pmu niagara1_pmu = { .event_map = niagara1_event_map, .cache_map = &niagara1_cache_map, .max_events = ARRAY_SIZE(niagara1_perfmon_event_map), + .read_pmc = sparc_default_read_pmc, + .write_pmc = sparc_default_write_pmc, .upper_shift = 0, .lower_shift = 4, .event_mask = 0x7, @@ -532,6 +566,8 @@ static const struct sparc_pmu niagara2_pmu = { .event_map = niagara2_event_map, .cache_map = &niagara2_cache_map, .max_events = ARRAY_SIZE(niagara2_perfmon_event_map), + .read_pmc = sparc_default_read_pmc, + .write_pmc = sparc_default_write_pmc, .upper_shift = 19, .lower_shift = 6, .event_mask = 0xfff, @@ -593,34 +629,6 @@ static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw pcr_ops->write_pcr(0, cpuc->pcr); } -static u32 read_pmc(int idx) -{ - u64 val; - - val = pcr_ops->read_pic(0); - if (idx == PIC_UPPER_INDEX) - val >>= 32; - - return val & 0xffffffff; -} - -static void write_pmc(int idx, u64 val) -{ - u64 shift, mask, pic; - - shift = 0; - if (idx == PIC_UPPER_INDEX) - shift = 32; - - mask = ((u64) 0xffffffff) << shift; - val <<= shift; - - pic = pcr_ops->read_pic(0); - pic &= ~mask; - pic |= val; - pcr_ops->write_pic(0, pic); -} - static u64 sparc_perf_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx) { @@ -630,7 +638,7 @@ static u64 sparc_perf_event_update(struct perf_event *event, again: prev_raw_count = local64_read(&hwc->prev_count); - new_raw_count = read_pmc(idx); + new_raw_count = sparc_pmu->read_pmc(idx); if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, new_raw_count) != prev_raw_count) @@ -670,7 +678,7 @@ static int sparc_perf_event_set_period(struct perf_event *event, local64_set(&hwc->prev_count, (u64)-left); - write_pmc(idx, (u64)(-left) & 0xffffffff); + sparc_pmu->write_pmc(idx, (u64)(-left) & 0xffffffff); perf_event_update_userpage(event); -- cgit v1.2.3-18-g5258 From 7ac2ed286f9338ea6437831096cc36ce8395b6fc Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 02:41:32 -0700 Subject: sparc64: Specify user and supervisor trace PCR bits in sparc_pmu. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index fbd80299a4b..1ab676bd13f 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -146,6 +146,8 @@ struct sparc_pmu { int upper_shift; int lower_shift; int event_mask; + int user_bit; + int priv_bit; int hv_bit; int irq_bit; int upper_nop; @@ -306,6 +308,8 @@ static const struct sparc_pmu ultra3_pmu = { .upper_shift = 11, .lower_shift = 4, .event_mask = 0x3f, + .user_bit = PCR_UTRACE, + .priv_bit = PCR_STRACE, .upper_nop = 0x1c, .lower_nop = 0x14, .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | @@ -440,6 +444,8 @@ static const struct sparc_pmu niagara1_pmu = { .upper_shift = 0, .lower_shift = 4, .event_mask = 0x7, + .user_bit = PCR_UTRACE, + .priv_bit = PCR_STRACE, .upper_nop = 0x0, .lower_nop = 0x0, .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | @@ -571,7 +577,9 @@ static const struct sparc_pmu niagara2_pmu = { .upper_shift = 19, .lower_shift = 6, .event_mask = 0xfff, - .hv_bit = 0x8, + .user_bit = PCR_UTRACE, + .priv_bit = PCR_STRACE, + .hv_bit = PCR_N2_HTRACE, .irq_bit = 0x30, .upper_nop = 0x220, .lower_nop = 0x220, @@ -771,7 +779,7 @@ static void sparc_pmu_disable(struct pmu *pmu) cpuc->n_added = 0; val = cpuc->pcr; - val &= ~(PCR_UTRACE | PCR_STRACE | + val &= ~(sparc_pmu->user_bit | sparc_pmu->priv_bit | sparc_pmu->hv_bit | sparc_pmu->irq_bit); cpuc->pcr = val; @@ -1177,9 +1185,9 @@ static int sparc_pmu_event_init(struct perf_event *event) /* We save the enable bits in the config_base. */ hwc->config_base = sparc_pmu->irq_bit; if (!attr->exclude_user) - hwc->config_base |= PCR_UTRACE; + hwc->config_base |= sparc_pmu->user_bit; if (!attr->exclude_kernel) - hwc->config_base |= PCR_STRACE; + hwc->config_base |= sparc_pmu->priv_bit; if (!attr->exclude_hv) hwc->config_base |= sparc_pmu->hv_bit; -- cgit v1.2.3-18-g5258 From 3f1a20972239e3f66720c34d9009ae9cc9ddffba Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 02:51:21 -0700 Subject: sparc64: Prepare perf event layer for handling multiple PCR registers. Make the per-cpu pcr save area an array instead of one u64. Describe how many PCR and PIC registers the chip has in the sparc_pmu descriptor. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 72 ++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 27 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 1ab676bd13f..9be089abb5d 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -54,6 +54,7 @@ */ #define MAX_HWEVENTS 2 +#define MAX_PCRS 1 #define MAX_PERIOD ((1UL << 32) - 1) #define PIC_UPPER_INDEX 0 @@ -89,8 +90,8 @@ struct cpu_hw_events { */ int current_idx[MAX_HWEVENTS]; - /* Software copy of %pcr register on this cpu. */ - u64 pcr; + /* Software copy of %pcr register(s) on this cpu. */ + u64 pcr[MAX_HWEVENTS]; /* Enabled/disable state. */ int enabled; @@ -156,6 +157,8 @@ struct sparc_pmu { #define SPARC_PMU_ALL_EXCLUDES_SAME 0x00000001 #define SPARC_PMU_HAS_CONFLICTS 0x00000002 int max_hw_events; + int num_pcrs; + int num_pic_regs; }; static u32 sparc_default_read_pmc(int idx) @@ -315,6 +318,8 @@ static const struct sparc_pmu ultra3_pmu = { .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), .max_hw_events = 2, + .num_pcrs = 1, + .num_pic_regs = 1, }; /* Niagara1 is very limited. The upper PIC is hard-locked to count @@ -451,6 +456,8 @@ static const struct sparc_pmu niagara1_pmu = { .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), .max_hw_events = 2, + .num_pcrs = 1, + .num_pic_regs = 1, }; static const struct perf_event_map niagara2_perfmon_event_map[] = { @@ -586,6 +593,8 @@ static const struct sparc_pmu niagara2_pmu = { .flags = (SPARC_PMU_ALL_EXCLUDES_SAME | SPARC_PMU_HAS_CONFLICTS), .max_hw_events = 2, + .num_pcrs = 1, + .num_pic_regs = 1, }; static const struct sparc_pmu *sparc_pmu __read_mostly; @@ -615,12 +624,12 @@ static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_ { u64 val, mask = mask_for_index(idx); - val = cpuc->pcr; + val = cpuc->pcr[0]; val &= ~mask; val |= hwc->config; - cpuc->pcr = val; + cpuc->pcr[0] = val; - pcr_ops->write_pcr(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr[0]); } static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx) @@ -629,12 +638,12 @@ static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw u64 nop = nop_for_index(idx); u64 val; - val = cpuc->pcr; + val = cpuc->pcr[0]; val &= ~mask; val |= nop; - cpuc->pcr = val; + cpuc->pcr[0] = val; - pcr_ops->write_pcr(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr[0]); } static u64 sparc_perf_event_update(struct perf_event *event, @@ -751,7 +760,7 @@ static void sparc_pmu_enable(struct pmu *pmu) cpuc->enabled = 1; barrier(); - pcr = cpuc->pcr; + pcr = cpuc->pcr[0]; if (!cpuc->n_events) { pcr = 0; } else { @@ -761,16 +770,16 @@ static void sparc_pmu_enable(struct pmu *pmu) * configuration, so just fetch the settings from the * first entry. */ - cpuc->pcr = pcr | cpuc->event[0]->hw.config_base; + cpuc->pcr[0] = pcr | cpuc->event[0]->hw.config_base; } - pcr_ops->write_pcr(0, cpuc->pcr); + pcr_ops->write_pcr(0, cpuc->pcr[0]); } static void sparc_pmu_disable(struct pmu *pmu) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - u64 val; + int i; if (!cpuc->enabled) return; @@ -778,12 +787,14 @@ static void sparc_pmu_disable(struct pmu *pmu) cpuc->enabled = 0; cpuc->n_added = 0; - val = cpuc->pcr; - val &= ~(sparc_pmu->user_bit | sparc_pmu->priv_bit | - sparc_pmu->hv_bit | sparc_pmu->irq_bit); - cpuc->pcr = val; + for (i = 0; i < sparc_pmu->num_pcrs; i++) { + u64 val = cpuc->pcr[i]; - pcr_ops->write_pcr(0, cpuc->pcr); + val &= ~(sparc_pmu->user_bit | sparc_pmu->priv_bit | + sparc_pmu->hv_bit | sparc_pmu->irq_bit); + cpuc->pcr[i] = val; + pcr_ops->write_pcr(i, cpuc->pcr[i]); + } } static int active_event_index(struct cpu_hw_events *cpuc, @@ -882,9 +893,11 @@ static DEFINE_MUTEX(pmc_grab_mutex); static void perf_stop_nmi_watchdog(void *unused) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int i; stop_nmi_watchdog(NULL); - cpuc->pcr = pcr_ops->read_pcr(0); + for (i = 0; i < sparc_pmu->num_pcrs; i++) + cpuc->pcr[i] = pcr_ops->read_pcr(i); } void perf_event_grab_pmc(void) @@ -1293,8 +1306,7 @@ static struct pmu pmu = { void perf_event_print_debug(void) { unsigned long flags; - u64 pcr, pic; - int cpu; + int cpu, i; if (!sparc_pmu) return; @@ -1303,12 +1315,13 @@ void perf_event_print_debug(void) cpu = smp_processor_id(); - pcr = pcr_ops->read_pcr(0); - pic = pcr_ops->read_pic(0); - pr_info("\n"); - pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n", - cpu, pcr, pic); + for (i = 0; i < sparc_pmu->num_pcrs; i++) + pr_info("CPU#%d: PCR%d[%016llx]\n", + cpu, i, pcr_ops->read_pcr(i)); + for (i = 0; i < sparc_pmu->num_pic_regs; i++) + pr_info("CPU#%d: PIC%d[%016llx]\n", + cpu, i, pcr_ops->read_pic(i)); local_irq_restore(flags); } @@ -1344,8 +1357,9 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, * Do this before we peek at the counters to determine * overflow so we don't lose any events. */ - if (sparc_pmu->irq_bit) - pcr_ops->write_pcr(0, cpuc->pcr); + if (sparc_pmu->irq_bit && + sparc_pmu->num_pcrs == 1) + pcr_ops->write_pcr(0, cpuc->pcr[0]); for (i = 0; i < cpuc->n_events; i++) { struct perf_event *event = cpuc->event[i]; @@ -1353,6 +1367,10 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, struct hw_perf_event *hwc; u64 val; + if (sparc_pmu->irq_bit && + sparc_pmu->num_pcrs > 1) + pcr_ops->write_pcr(idx, cpuc->pcr[idx]); + hwc = &event->hw; val = sparc_perf_event_update(event, hwc, idx); if (val & (1ULL << 31)) -- cgit v1.2.3-18-g5258 From 5ab968413515e17788003c522f7ca40a07fae900 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 03:09:39 -0700 Subject: sparc64: Rework sparc_pmu_enable() so that the side effects are clearer. When cpuc->n_events is zero, we actually don't do anything and we just write the cpuc->pcr[0] value as-is without any modifications. The "pcr = 0;" assignment there was just useless and confusing. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 9be089abb5d..197c79e9206 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -752,7 +752,6 @@ out: static void sparc_pmu_enable(struct pmu *pmu) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); - u64 pcr; if (cpuc->enabled) return; @@ -760,11 +759,8 @@ static void sparc_pmu_enable(struct pmu *pmu) cpuc->enabled = 1; barrier(); - pcr = cpuc->pcr[0]; - if (!cpuc->n_events) { - pcr = 0; - } else { - pcr = maybe_change_configuration(cpuc, pcr); + if (cpuc->n_events) { + u64 pcr = maybe_change_configuration(cpuc, cpuc->pcr[0]); /* We require that all of the events have the same * configuration, so just fetch the settings from the -- cgit v1.2.3-18-g5258 From b4f061a4b8cbf947de4fa816a1cfc53960da218e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 03:14:01 -0700 Subject: sparc64: Make sparc_pmu_{enable,disable}_event() multi-pcr aware. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 197c79e9206..c3ad63775ff 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -623,27 +623,35 @@ static u64 nop_for_index(int idx) static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx) { u64 val, mask = mask_for_index(idx); + int pcr_index = 0; - val = cpuc->pcr[0]; + if (sparc_pmu->num_pcrs > 1) + pcr_index = idx; + + val = cpuc->pcr[pcr_index]; val &= ~mask; val |= hwc->config; - cpuc->pcr[0] = val; + cpuc->pcr[pcr_index] = val; - pcr_ops->write_pcr(0, cpuc->pcr[0]); + pcr_ops->write_pcr(pcr_index, cpuc->pcr[pcr_index]); } static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx) { u64 mask = mask_for_index(idx); u64 nop = nop_for_index(idx); + int pcr_index = 0; u64 val; - val = cpuc->pcr[0]; + if (sparc_pmu->num_pcrs > 1) + pcr_index = idx; + + val = cpuc->pcr[pcr_index]; val &= ~mask; val |= nop; - cpuc->pcr[0] = val; + cpuc->pcr[pcr_index] = val; - pcr_ops->write_pcr(0, cpuc->pcr[0]); + pcr_ops->write_pcr(pcr_index, cpuc->pcr[pcr_index]); } static u64 sparc_perf_event_update(struct perf_event *event, -- cgit v1.2.3-18-g5258 From 7a37a0b8f8df0872932cf8373b21b5e14a92a794 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 03:29:05 -0700 Subject: sparc64: Support perf event encoding for multi-PCR PMUs. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 98 ++++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 23 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index c3ad63775ff..64415922a28 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -710,18 +710,10 @@ static int sparc_perf_event_set_period(struct perf_event *event, return ret; } -/* If performance event entries have been added, move existing - * events around (if necessary) and then assign new entries to - * counters. - */ -static u64 maybe_change_configuration(struct cpu_hw_events *cpuc, u64 pcr) +static void read_in_all_counters(struct cpu_hw_events *cpuc) { int i; - if (!cpuc->n_added) - goto out; - - /* Read in the counters which are moving. */ for (i = 0; i < cpuc->n_events; i++) { struct perf_event *cp = cpuc->event[i]; @@ -732,6 +724,20 @@ static u64 maybe_change_configuration(struct cpu_hw_events *cpuc, u64 pcr) cpuc->current_idx[i] = PIC_NO_INDEX; } } +} + +/* On this PMU all PICs are programmed using a single PCR. Calculate + * the combined control register value. + * + * For such chips we require that all of the events have the same + * configuration, so just fetch the settings from the first entry. + */ +static void calculate_single_pcr(struct cpu_hw_events *cpuc) +{ + int i; + + if (!cpuc->n_added) + goto out; /* Assign to counters all unassigned events. */ for (i = 0; i < cpuc->n_events; i++) { @@ -747,19 +753,71 @@ static u64 maybe_change_configuration(struct cpu_hw_events *cpuc, u64 pcr) cpuc->current_idx[i] = idx; enc = perf_event_get_enc(cpuc->events[i]); - pcr &= ~mask_for_index(idx); + cpuc->pcr[0] &= ~mask_for_index(idx); if (hwc->state & PERF_HES_STOPPED) - pcr |= nop_for_index(idx); + cpuc->pcr[0] |= nop_for_index(idx); else - pcr |= event_encoding(enc, idx); + cpuc->pcr[0] |= event_encoding(enc, idx); } out: - return pcr; + cpuc->pcr[0] |= cpuc->event[0]->hw.config_base; +} + +/* On this PMU each PIC has it's own PCR control register. */ +static void calculate_multiple_pcrs(struct cpu_hw_events *cpuc) +{ + int i; + + if (!cpuc->n_added) + goto out; + + for (i = 0; i < cpuc->n_events; i++) { + struct perf_event *cp = cpuc->event[i]; + struct hw_perf_event *hwc = &cp->hw; + int idx = hwc->idx; + u64 enc; + + if (cpuc->current_idx[i] != PIC_NO_INDEX) + continue; + + sparc_perf_event_set_period(cp, hwc, idx); + cpuc->current_idx[i] = idx; + + enc = perf_event_get_enc(cpuc->events[i]); + cpuc->pcr[idx] &= ~mask_for_index(idx); + if (hwc->state & PERF_HES_STOPPED) + cpuc->pcr[idx] |= nop_for_index(idx); + else + cpuc->pcr[idx] |= event_encoding(enc, idx); + } +out: + for (i = 0; i < cpuc->n_events; i++) { + struct perf_event *cp = cpuc->event[i]; + int idx = cp->hw.idx; + + cpuc->pcr[idx] |= cp->hw.config_base; + } +} + +/* If performance event entries have been added, move existing events + * around (if necessary) and then assign new entries to counters. + */ +static void update_pcrs_for_enable(struct cpu_hw_events *cpuc) +{ + if (cpuc->n_added) + read_in_all_counters(cpuc); + + if (sparc_pmu->num_pcrs == 1) { + calculate_single_pcr(cpuc); + } else { + calculate_multiple_pcrs(cpuc); + } } static void sparc_pmu_enable(struct pmu *pmu) { struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int i; if (cpuc->enabled) return; @@ -767,17 +825,11 @@ static void sparc_pmu_enable(struct pmu *pmu) cpuc->enabled = 1; barrier(); - if (cpuc->n_events) { - u64 pcr = maybe_change_configuration(cpuc, cpuc->pcr[0]); - - /* We require that all of the events have the same - * configuration, so just fetch the settings from the - * first entry. - */ - cpuc->pcr[0] = pcr | cpuc->event[0]->hw.config_base; - } + if (cpuc->n_events) + update_pcrs_for_enable(cpuc); - pcr_ops->write_pcr(0, cpuc->pcr[0]); + for (i = 0; i < sparc_pmu->num_pcrs; i++) + pcr_ops->write_pcr(i, cpuc->pcr[i]); } static void sparc_pmu_disable(struct pmu *pmu) -- cgit v1.2.3-18-g5258 From 035ea28dde1802ad4cc570976da34f8b7c2ed515 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 17 Aug 2012 23:06:09 -0700 Subject: sparc64: Add SPARC-T4 perf event support. Signed-off-by: David S. Miller --- arch/sparc/kernel/perf_event.c | 189 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 64415922a28..18853705282 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c @@ -53,8 +53,8 @@ * normal code. */ -#define MAX_HWEVENTS 2 -#define MAX_