diff options
Diffstat (limited to 'arch/ia64/kernel')
-rw-r--r-- | arch/ia64/kernel/fsys.S | 1 | ||||
-rw-r--r-- | arch/ia64/kernel/jprobes.S | 27 | ||||
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 57 | ||||
-rw-r--r-- | arch/ia64/kernel/mca_asm.S | 2 | ||||
-rw-r--r-- | arch/ia64/kernel/perfmon.c | 2 | ||||
-rw-r--r-- | arch/ia64/kernel/perfmon_montecito.h | 269 | ||||
-rw-r--r-- | arch/ia64/kernel/salinfo.c | 170 | ||||
-rw-r--r-- | arch/ia64/kernel/traps.c | 26 |
8 files changed, 495 insertions, 59 deletions
diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S index 2ddbac6..ce42391 100644 --- a/arch/ia64/kernel/fsys.S +++ b/arch/ia64/kernel/fsys.S @@ -903,5 +903,6 @@ fsyscall_table: data8 0 data8 0 data8 0 + data8 0 // 1280 .org fsyscall_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/ia64/kernel/jprobes.S b/arch/ia64/kernel/jprobes.S index 2323377..5cd6226f 100644 --- a/arch/ia64/kernel/jprobes.S +++ b/arch/ia64/kernel/jprobes.S @@ -60,3 +60,30 @@ END(jprobe_break) GLOBAL_ENTRY(jprobe_inst_return) br.call.sptk.many b0=jprobe_break END(jprobe_inst_return) + +GLOBAL_ENTRY(invalidate_stacked_regs) + movl r16=invalidate_restore_cfm + ;; + mov b6=r16 + ;; + br.ret.sptk.many b6 + ;; +invalidate_restore_cfm: + mov r16=ar.rsc + ;; + mov ar.rsc=r0 + ;; + loadrs + ;; + mov ar.rsc=r16 + ;; + br.cond.sptk.many rp +END(invalidate_stacked_regs) + +GLOBAL_ENTRY(flush_register_stack) + // flush dirty regs to backing store (must be first in insn group) + flushrs + ;; + br.ret.sptk.many rp +END(flush_register_stack) + diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index 346fedf..50ae8c7 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -766,11 +766,56 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, return ret; } +struct param_bsp_cfm { + unsigned long ip; + unsigned long *bsp; + unsigned long cfm; +}; + +static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg) +{ + unsigned long ip; + struct param_bsp_cfm *lp = arg; + + do { + unw_get_ip(info, &ip); + if (ip == 0) + break; + if (ip == lp->ip) { + unw_get_bsp(info, (unsigned long*)&lp->bsp); + unw_get_cfm(info, (unsigned long*)&lp->cfm); + return; + } + } while (unw_unwind(info) >= 0); + lp->bsp = 0; + lp->cfm = 0; + return; +} + int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) { struct jprobe *jp = container_of(p, struct jprobe, kp); unsigned long addr = ((struct fnptr *)(jp->entry))->ip; struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + struct param_bsp_cfm pa; + int bytes; + + /* + * Callee owns the argument space and could overwrite it, eg + * tail call optimization. So to be absolutely safe + * we save the argument space before transfering the control + * to instrumented jprobe function which runs in + * the process context + */ + pa.ip = regs->cr_iip; + unw_init_running(ia64_get_bsp_cfm, &pa); + bytes = (char *)ia64_rse_skip_regs(pa.bsp, pa.cfm & 0x3f) + - (char *)pa.bsp; + memcpy( kcb->jprobes_saved_stacked_regs, + pa.bsp, + bytes ); + kcb->bsp = pa.bsp; + kcb->cfm = pa.cfm; /* save architectural state */ kcb->jprobe_saved_regs = *regs; @@ -792,8 +837,20 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) { struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + int bytes; + /* restoring architectural state */ *regs = kcb->jprobe_saved_regs; + + /* restoring the original argument space */ + flush_register_stack(); + bytes = (char *)ia64_rse_skip_regs(kcb->bsp, kcb->cfm & 0x3f) + - (char *)kcb->bsp; + memcpy( kcb->bsp, + kcb->jprobes_saved_stacked_regs, + bytes ); + invalidate_stacked_regs(); + preempt_enable_no_resched(); return 1; } diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S index db32fc1..403a80a 100644 --- a/arch/ia64/kernel/mca_asm.S +++ b/arch/ia64/kernel/mca_asm.S @@ -847,7 +847,7 @@ ia64_state_restore: ;; mov cr.iim=temp3 mov cr.iha=temp4 - dep r22=0,r22,62,2 // pal_min_state, physical, uncached + dep r22=0,r22,62,1 // pal_min_state, physical, uncached mov IA64_KR(CURRENT)=r21 ld8 r8=[temp1] // os_status ld8 r10=[temp2] // context diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index bd87cb6..2ea4b39 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -628,9 +628,11 @@ static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, #include "perfmon_itanium.h" #include "perfmon_mckinley.h" +#include "perfmon_montecito.h" #include "perfmon_generic.h" static pmu_config_t *pmu_confs[]={ + &pmu_conf_mont, &pmu_conf_mck, &pmu_conf_ita, &pmu_conf_gen, /* must be last */ diff --git a/arch/ia64/kernel/perfmon_montecito.h b/arch/ia64/kernel/perfmon_montecito.h new file mode 100644 index 0000000..cd06ac6 --- /dev/null +++ b/arch/ia64/kernel/perfmon_montecito.h @@ -0,0 +1,269 @@ +/* + * This file contains the Montecito PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. + * Contributed by Stephane Eranian <eranian@hpl.hp.com> + */ +static int pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); + +#define RDEP_MONT_ETB (RDEP(38)|RDEP(39)|RDEP(48)|RDEP(49)|RDEP(50)|RDEP(51)|RDEP(52)|RDEP(53)|RDEP(54)|\ + RDEP(55)|RDEP(56)|RDEP(57)|RDEP(58)|RDEP(59)|RDEP(60)|RDEP(61)|RDEP(62)|RDEP(63)) +#define RDEP_MONT_DEAR (RDEP(32)|RDEP(33)|RDEP(36)) +#define RDEP_MONT_IEAR (RDEP(34)|RDEP(35)) + +static pfm_reg_desc_t pfm_mont_pmc_desc[PMU_MAX_PMCS]={ +/* pmc0 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(4),0, 0, 0}, {0,0, 0, 0}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(5),0, 0, 0}, {0,0, 0, 0}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(6),0, 0, 0}, {0,0, 0, 0}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(7),0, 0, 0}, {0,0, 0, 0}}, +/* pmc8 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(8),0, 0, 0}, {0,0, 0, 0}}, +/* pmc9 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(9),0, 0, 0}, {0,0, 0, 0}}, +/* pmc10 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(10),0, 0, 0}, {0,0, 0, 0}}, +/* pmc11 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(11),0, 0, 0}, {0,0, 0, 0}}, +/* pmc12 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(12),0, 0, 0}, {0,0, 0, 0}}, +/* pmc13 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(13),0, 0, 0}, {0,0, 0, 0}}, +/* pmc14 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(14),0, 0, 0}, {0,0, 0, 0}}, +/* pmc15 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(15),0, 0, 0}, {0,0, 0, 0}}, +/* pmc16 */ { PFM_REG_NOTIMPL, }, +/* pmc17 */ { PFM_REG_NOTIMPL, }, +/* pmc18 */ { PFM_REG_NOTIMPL, }, +/* pmc19 */ { PFM_REG_NOTIMPL, }, +/* pmc20 */ { PFM_REG_NOTIMPL, }, +/* pmc21 */ { PFM_REG_NOTIMPL, }, +/* pmc22 */ { PFM_REG_NOTIMPL, }, +/* pmc23 */ { PFM_REG_NOTIMPL, }, +/* pmc24 */ { PFM_REG_NOTIMPL, }, +/* pmc25 */ { PFM_REG_NOTIMPL, }, +/* pmc26 */ { PFM_REG_NOTIMPL, }, +/* pmc27 */ { PFM_REG_NOTIMPL, }, +/* pmc28 */ { PFM_REG_NOTIMPL, }, +/* pmc29 */ { PFM_REG_NOTIMPL, }, +/* pmc30 */ { PFM_REG_NOTIMPL, }, +/* pmc31 */ { PFM_REG_NOTIMPL, }, +/* pmc32 */ { PFM_REG_CONFIG, 0, 0x30f01ffffffffff, 0x30f01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc33 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc34 */ { PFM_REG_CONFIG, 0, 0xf01ffffffffff, 0xf01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc35 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc36 */ { PFM_REG_CONFIG, 0, 0xfffffff0, 0xf, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc37 */ { PFM_REG_MONITOR, 4, 0x0, 0x3fff, NULL, pfm_mont_pmc_check, {RDEP_MONT_IEAR, 0, 0, 0}, {0, 0, 0, 0}}, +/* pmc38 */ { PFM_REG_CONFIG, 0, 0xdb6, 0x2492, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc39 */ { PFM_REG_MONITOR, 6, 0x0, 0xffcf, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}}, +/* pmc40 */ { PFM_REG_MONITOR, 6, 0x2000000, 0xf01cf, NULL, pfm_mont_pmc_check, {RDEP_MONT_DEAR,0, 0, 0}, {0,0, 0, 0}}, +/* pmc41 */ { PFM_REG_CONFIG, 0, 0x00002078fefefefe, 0x1e00018181818, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc42 */ { PFM_REG_MONITOR, 6, 0x0, 0x7ff4f, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}}, + { PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pfm_mont_pmd_desc[PMU_MAX_PMDS]={ +/* pmd0 */ { PFM_REG_NOTIMPL, }, +/* pmd1 */ { PFM_REG_NOTIMPL, }, +/* pmd2 */ { PFM_REG_NOTIMPL, }, +/* pmd3 */ { PFM_REG_NOTIMPL, }, +/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(4),0, 0, 0}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(5),0, 0, 0}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(6),0, 0, 0}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(7),0, 0, 0}}, +/* pmd8 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(8),0, 0, 0}}, +/* pmd9 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(9),0, 0, 0}}, +/* pmd10 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(10),0, 0, 0}}, +/* pmd11 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(11),0, 0, 0}}, +/* pmd12 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(12),0, 0, 0}}, +/* pmd13 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(13),0, 0, 0}}, +/* pmd14 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(14),0, 0, 0}}, +/* pmd15 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(15),0, 0, 0}}, +/* pmd16 */ { PFM_REG_NOTIMPL, }, +/* pmd17 */ { PFM_REG_NOTIMPL, }, +/* pmd18 */ { PFM_REG_NOTIMPL, }, +/* pmd19 */ { PFM_REG_NOTIMPL, }, +/* pmd20 */ { PFM_REG_NOTIMPL, }, +/* pmd21 */ { PFM_REG_NOTIMPL, }, +/* pmd22 */ { PFM_REG_NOTIMPL, }, +/* pmd23 */ { PFM_REG_NOTIMPL, }, +/* pmd24 */ { PFM_REG_NOTIMPL, }, +/* pmd25 */ { PFM_REG_NOTIMPL, }, +/* pmd26 */ { PFM_REG_NOTIMPL, }, +/* pmd27 */ { PFM_REG_NOTIMPL, }, +/* pmd28 */ { PFM_REG_NOTIMPL, }, +/* pmd29 */ { PFM_REG_NOTIMPL, }, +/* pmd30 */ { PFM_REG_NOTIMPL, }, +/* pmd31 */ { PFM_REG_NOTIMPL, }, +/* pmd32 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(33)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd33 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd34 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(35),0, 0, 0}, {RDEP(37),0, 0, 0}}, +/* pmd35 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(34),0, 0, 0}, {RDEP(37),0, 0, 0}}, +/* pmd36 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(33),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd37 */ { PFM_REG_NOTIMPL, }, +/* pmd38 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd39 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd40 */ { PFM_REG_NOTIMPL, }, +/* pmd41 */ { PFM_REG_NOTIMPL, }, +/* pmd42 */ { PFM_REG_NOTIMPL, }, +/* pmd43 */ { PFM_REG_NOTIMPL, }, +/* pmd44 */ { PFM_REG_NOTIMPL, }, +/* pmd45 */ { PFM_REG_NOTIMPL, }, +/* pmd46 */ { PFM_REG_NOTIMPL, }, +/* pmd47 */ { PFM_REG_NOTIMPL, }, +/* pmd48 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd49 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd50 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd51 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd52 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd53 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd54 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd55 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd56 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd57 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd58 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd59 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd60 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd61 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd62 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd63 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, + { PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +/* + * PMC reserved fields must have their power-up values preserved + */ +static int +pfm_mont_reserved(unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + unsigned long tmp1, tmp2, ival = *val; + + /* remove reserved areas from user value */ + tmp1 = ival & PMC_RSVD_MASK(cnum); + + /* get reserved fields values */ + tmp2 = PMC_DFL_VAL(cnum) & ~PMC_RSVD_MASK(cnum); + + *val = tmp1 | tmp2; + + DPRINT(("pmc[%d]=0x%lx, mask=0x%lx, reset=0x%lx, val=0x%lx\n", + cnum, ival, PMC_RSVD_MASK(cnum), PMC_DFL_VAL(cnum), *val)); + return 0; +} + +/* + * task can be NULL if the context is unloaded + */ +static int +pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + int ret = 0; + unsigned long val32 = 0, val38 = 0, val41 = 0; + unsigned long tmpval; + int check_case1 = 0; + int is_loaded; + + /* first preserve the reserved fields */ + pfm_mont_reserved(cnum, val, regs); + + tmpval = *val; + + /* sanity check */ + if (ctx == NULL) return -EINVAL; + + is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED; + + /* + * we must clear the debug registers if pmc41 has a value which enable + * memory pipeline event constraints. In this case we need to clear the + * the debug registers if they have not yet been accessed. This is required + * to avoid picking stale state. + * PMC41 is "active" if: + * one of the pmc41.cfg_dtagXX field is different from 0x3 + * AND + * at the corresponding pmc41.en_dbrpXX is set. + * AND + * ctx_fl_using_dbreg == 0 (i.e., dbr not yet used) + */ + DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, tmpval, ctx->ctx_fl_using_dbreg, is_loaded)); + + if (cnum == 41 && is_loaded + && (tmpval & 0x1e00000000000) && (tmpval & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc41 settings, clearing dbr\n", cnum, tmpval)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers if: + * AND + */ + ret = pfm_write_ibr_dbr(PFM_DATA_RR, ctx, NULL, 0, regs); + if (ret) return ret; + } + /* + * we must clear the (instruction) debug registers if: + * pmc38.ig_ibrpX is 0 (enabled) + * AND + * ctx_fl_using_dbreg == 0 (i.e., dbr not yet used) + */ + if (cnum == 38 && is_loaded && ((tmpval & 0x492UL) != 0x492UL) && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc38=0x%lx has active pmc38 settings, clearing ibr\n", tmpval)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(PFM_CODE_RR, ctx, NULL, 0, regs); + if (ret) return ret; + + } + switch(cnum) { + case 32: val32 = *val; + val38 = ctx->ctx_pmcs[38]; + val41 = ctx->ctx_pmcs[41]; + check_case1 = 1; + break; + case 38: val38 = *val; + val32 = ctx->ctx_pmcs[32]; + val41 = ctx->ctx_pmcs[41]; + check_case1 = 1; + break; + case 41: val41 = *val; + val32 = ctx->ctx_pmcs[32]; + val38 = ctx->ctx_pmcs[38]; + check_case1 = 1; + break; + } + /* check illegal configuration which can produce inconsistencies in tagging + * i-side events in L1D and L2 caches + */ + if (check_case1) { + ret = (((val41 >> 45) & 0xf) == 0 && ((val32>>57) & 0x1) == 0) + && ((((val38>>1) & 0x3) == 0x2 || ((val38>>1) & 0x3) == 0) + || (((val38>>4) & 0x3) == 0x2 || ((val38>>4) & 0x3) == 0)); + if (ret) { + DPRINT(("invalid config pmc38=0x%lx pmc41=0x%lx pmc32=0x%lx\n", val38, val41, val32)); + return -EINVAL; + } + } + *val = tmpval; + return 0; +} + +/* + * impl_pmcs, impl_pmds are computed at runtime to minimize errors! + */ +static pmu_config_t pmu_conf_mont={ + .pmu_name = "Montecito", + .pmu_family = 0x20, + .flags = PFM_PMU_IRQ_RESEND, + .ovfl_val = (1UL << 47) - 1, + .pmd_desc = pfm_mont_pmd_desc, + .pmc_desc = pfm_mont_pmc_desc, + .num_ibrs = 8, + .num_dbrs = 8, + .use_rr_dbregs = 1 /* debug register are use for range retrictions */ +}; diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c index a87a162..9d5a823 100644 --- a/arch/ia64/kernel/salinfo.c +++ b/arch/ia64/kernel/salinfo.c @@ -3,7 +3,7 @@ * * Creates entries in /proc/sal for various system features. * - * Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (c) 2003, 2006 Silicon Graphics, Inc. All rights reserved. * Copyright (c) 2003 Hewlett-Packard Co * Bjorn Helgaas <bjorn.helgaas@hp.com> * @@ -27,9 +27,17 @@ * mca.c may not pass a buffer, a NULL buffer just indicates that a new * record is available in SAL. * Replace some NR_CPUS by cpus_online, for hotplug cpu. + * + * Jan 5 2006 kaos@sgi.com + * Handle hotplug cpus coming online. + * Handle hotplug cpus going offline while they still have outstanding records. + * Use the cpu_* macros consistently. + * Replace the counting semaphore with a mutex and a test if the cpumask is non-empty. + * Modify the locking to make the test for "work to do" an atomic operation. */ #include <linux/capability.h> +#include <linux/cpu.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/module.h> @@ -132,8 +140,8 @@ enum salinfo_state { }; struct salinfo_data { - volatile cpumask_t cpu_event; /* which cpus have outstanding events */ - struct semaphore sem; /* count of cpus with outstanding events (bits set in cpu_event) */ + cpumask_t cpu_event; /* which cpus have outstanding events */ + struct semaphore mutex; u8 *log_buffer; u64 log_size; u8 *oemdata; /* decoded oem data */ @@ -174,6 +182,21 @@ struct salinfo_platform_oemdata_parms { int ret; }; +/* Kick the mutex that tells user space that there is work to do. Instead of + * trying to track the state of the mutex across multiple cpus, in user + * context, interrupt context, non-maskable interrupt context and hotplug cpu, + * it is far easier just to grab the mutex if it is free then release it. + * + * This routine must be called with data_saved_lock held, to make the down/up + * operation atomic. + */ +static void +salinfo_work_to_do(struct salinfo_data *data) +{ + down_trylock(&data->mutex); + up(&data->mutex); +} + static void salinfo_platform_oemdata_cpu(void *context) { @@ -212,9 +235,9 @@ salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe) BUG_ON(type >= ARRAY_SIZE(salinfo_log_name)); + if (irqsafe) + spin_lock_irqsave(&data_saved_lock, flags); if (buffer) { - if (irqsafe) - spin_lock_irqsave(&data_saved_lock, flags); for (i = 0, data_saved = data->data_saved; i < saved_size; ++i, ++data_saved) { if (!data_saved->buffer) break; @@ -232,13 +255,11 @@ salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe) data_saved->size = size; data_saved->buffer = buffer; } - if (irqsafe) - spin_unlock_irqrestore(&data_saved_lock, flags); } - - if (!test_and_set_bit(smp_processor_id(), &data->cpu_event)) { - if (irqsafe) - up(&data->sem); + cpu_set(smp_processor_id(), data->cpu_event); + if (irqsafe) { + salinfo_work_to_do(data); + spin_unlock_irqrestore(&data_saved_lock, flags); } } @@ -249,20 +270,17 @@ static struct timer_list salinfo_timer; static void salinfo_timeout_check(struct salinfo_data *data) { - int i; + unsigned long flags; if (!data->open) return; - for_each_online_cpu(i) { - if (test_bit(i, &data->cpu_event)) { - /* double up() is not a problem, user space will see no - * records for the additional "events". - */ - up(&data->sem); - } + if (!cpus_empty(data->cpu_event)) { + spin_lock_irqsave(&data_saved_lock, flags); + salinfo_work_to_do(data); + spin_unlock_irqrestore(&data_saved_lock, flags); } } -static void +static void salinfo_timeout (unsigned long arg) { salinfo_timeout_check(salinfo_data + SAL_INFO_TYPE_MCA); @@ -290,16 +308,20 @@ salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t int i, n, cpu = -1; retry: - if (down_trylock(&data->sem)) { + if (cpus_empty(data->cpu_event) && down_trylock(&data->mutex)) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; - if (down_interruptible(&data->sem)) + if (down_interruptible(&data->mutex)) return -EINTR; } n = data->cpu_check; for (i = 0; i < NR_CPUS; i++) { - if (test_bit(n, &data->cpu_event) && cpu_online(n)) { + if (cpu_isset(n, data->cpu_event)) { + if (!cpu_online(n)) { + cpu_clear(n, data->cpu_event); + continue; + } cpu = n; break; } @@ -310,9 +332,6 @@ retry: if (cpu == -1) goto retry; - /* events are sticky until the user says "clear" */ - up(&data->sem); - /* for next read, start checking at next CPU */ data->cpu_check = cpu; if (++data->cpu_check == NR_CPUS) @@ -381,10 +400,8 @@ salinfo_log_release(struct inode *inode, struct file *file) static void call_on_cpu(int cpu, void (*fn)(void *), void *arg) { - cpumask_t save_cpus_allowed, new_cpus_allowed; - memcpy(&save_cpus_allowed, ¤t->cpus_allowed, sizeof(save_cpus_allowed)); - memset(&new_cpus_allowed, 0, sizeof(new_cpus_allowed)); - set_bit(cpu, &new_cpus_allowed); + cpumask_t save_cpus_allowed = current->cpus_allowed; + cpumask_t new_cpus_allowed = cpumask_of_cpu(cpu); set_cpus_allowed(current, new_cpus_allowed); (*fn)(arg); set_cpus_allowed(current, save_cpus_allowed); @@ -433,10 +450,10 @@ retry: if (!data->saved_num) call_on_cpu(cpu, salinfo_log_read_cpu, data); if (!data->log_size) { - data->state = STATE_NO_DATA; - clear_bit(cpu, &data->cpu_event); + data->state = STATE_NO_DATA; + cpu_clear(cpu, data->cpu_event); } else { - data->state = STATE_LOG_RECORD; + data->state = STATE_LOG_RECORD; } } @@ -473,27 +490,31 @@ static int salinfo_log_clear(struct salinfo_data *data, int cpu) { sal_log_record_header_t *rh; + unsigned long flags; + spin_lock_irqsave(&data_saved_lock, flags); data->state = STATE_NO_DATA; - if (!test_bit(cpu, &data->cpu_event)) + if (!cpu_isset(cpu, data->cpu_event)) { + spin_unlock_irqrestore(&data_saved_lock, flags); return 0; - down(&data->sem); - clear_bit(cpu, &data->cpu_event); + } + cpu_clear(cpu, data->cpu_event); if (data->saved_num) { - unsigned long flags; - spin_lock_irqsave(&data_saved_lock, flags); - shift1_data_saved(data, data->saved_num - 1 ); + shift1_data_saved(data, data->saved_num - 1); data->saved_num = 0; - spin_unlock_irqrestore(&data_saved_lock, flags); } + spin_unlock_irqrestore(&data_saved_lock, flags); rh = (sal_log_record_header_t *)(data->log_buffer); /* Corrected errors have already been cleared from SAL */ if (rh->severity != sal_log_severity_corrected) call_on_cpu(cpu, salinfo_log_clear_cpu, data); /* clearing a record may make a new record visible */ salinfo_log_new_read(cpu, data); - if (data->state == STATE_LOG_RECORD && - !test_and_set_bit(cpu, &data->cpu_event)) - up(&data->sem); + if (data->state == STATE_LOG_RECORD) { + spin_lock_irqsave(&data_saved_lock, flags); + cpu_set(cpu, data->cpu_event); + salinfo_work_to_do(data); + spin_unlock_irqrestore(&data_saved_lock, flags); + } return 0; } @@ -550,6 +571,53 @@ static struct file_operations salinfo_data_fops = { .write = salinfo_log_write, }; +#ifdef CONFIG_HOTPLUG_CPU +static int __devinit +salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) +{ + unsigned int i, cpu = (unsigned long)hcpu; + unsigned long flags; + struct salinfo_data *data; + switch (action) { + case CPU_ONLINE: + spin_lock_irqsave(&data_saved_lock, flags); + for (i = 0, data = salinfo_data; + i < ARRAY_SIZE(salinfo_data); + ++i, ++data) { + cpu_set(cpu, data->cpu_event); + salinfo_work_to_do(data); + } + spin_unlock_irqrestore(&data_saved_lock, flags); + break; + case CPU_DEAD: + spin_lock_irqsave(&data_saved_lock, flags); + for (i = 0, data = salinfo_data; + i < ARRAY_SIZE(salinfo_data); + ++i, ++data) { + struct salinfo_data_saved *data_saved; + int j; + for (j = ARRAY_SIZE(data->data_saved) - 1, data_saved = data->data_saved + j; + j >= 0; + --j, --data_saved) { + if (data_saved->buffer && data_saved->cpu == cpu) { + shift1_data_saved(data, j); + } + } + cpu_clear(cpu, data->cpu_event); + } + spin_unlock_irqrestore(&data_saved_lock, flags); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block salinfo_cpu_notifier = +{ + .notifier_call = salinfo_cpu_callback, + .priority = 0, +}; +#endif /* CONFIG_HOTPLUG_CPU */ + static int __init salinfo_init(void) { @@ -557,7 +625,7 @@ salinfo_init(void) struct proc_dir_entry **sdir = salinfo_proc_entries; /* keeps track of every entry */ struct proc_dir_entry *dir, *entry; struct salinfo_data *data; - int i, j, online; + int i, j; salinfo_dir = proc_mkdir("sal", NULL); if (!salinfo_dir) @@ -572,7 +640,7 @@ salinfo_init(void) for (i = 0; i < ARRAY_SIZE(salinfo_log_name); i++) { data = salinfo_data + i; data->type = i; - sema_init(&data->sem, 0); + init_MUTEX(&data->mutex); dir = proc_mkdir(salinfo_log_name[i], salinfo_dir); if (!dir) continue; @@ -592,12 +660,8 @@ salinfo_init(void) *sdir++ = entry; /* we missed any events before now */ - online = 0; - for_each_online_cpu(j) { - set_bit(j, &data->cpu_event); - ++online; - } - sema_init(&data->sem, online); + for_each_online_cpu(j) + cpu_set(j, data->cpu_event); *sdir++ = dir; } @@ -609,6 +673,10 @@ salinfo_init(void) salinfo_timer.function = &salinfo_timeout; add_timer(&salinfo_timer); +#ifdef CONFIG_HOTPLUG_CPU + register_cpu_notifier(&salinfo_cpu_notifier); +#endif + return 0; } diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index d3e0ecb..5539190 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -530,12 +530,15 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, if (fsys_mode(current, ®s)) { extern char __kernel_syscall_via_break[]; /* - * Got a trap in fsys-mode: Taken Branch Trap and Single Step trap - * need special handling; Debug trap is not supposed to happen. + * Got a trap in fsys-mode: Taken Branch Trap + * and Single Step trap need special handling; + * Debug trap is ignored (we disable it here + * and re-enable it in the lower-privilege trap). */ if (unlikely(vector == 29)) { - die("Got debug trap in fsys-mode---not supposed to happen!", - ®s, 0); + set_thread_flag(TIF_DB_DISABLED); + ia64_psr(®s)->db = 0; + ia64_psr(®s)->lp = 1; return; } /* re-do the system call via break 0x100000: */ @@ -589,10 +592,19 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, case 34: if (isr & 0x2) { /* Lower-Privilege Transfer Trap */ + + /* If we disabled debug traps during an fsyscall, + * re-enable them here. + */ + if (test_thread_flag(TIF_DB_DISABLED)) { + clear_thread_flag(TIF_DB_DISABLED); + ia64_psr(®s)->db = 1; + } + /* - * Just clear PSR.lp and then return immediately: all the - * interesting work (e.g., signal delivery is done in the kernel - * exit path). + * Just clear PSR.lp and then return immediately: + * all the interesting work (e.g., signal delivery) + * is done in the kernel exit path. */ ia64_psr(®s)->lp = 0; return; |