diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/dtrace/dcpc.c | 90 | ||||
-rw-r--r-- | usr/src/uts/common/sys/cpc_impl.h | 22 | ||||
-rw-r--r-- | usr/src/uts/intel/ia32/os/cpc_subr.c | 4 |
3 files changed, 87 insertions, 29 deletions
diff --git a/usr/src/uts/common/dtrace/dcpc.c b/usr/src/uts/common/dtrace/dcpc.c index c410e65eaa..d4ca1d308b 100644 --- a/usr/src/uts/common/dtrace/dcpc.c +++ b/usr/src/uts/common/dtrace/dcpc.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -496,6 +496,7 @@ dcpc_program_cpu_event(cpu_t *c) if (octx != NULL) { kcpc_set_t *oset = octx->kc_set; kmem_free(oset->ks_data, oset->ks_nreqs * sizeof (uint64_t)); + kcpc_free_configs(oset); kcpc_free_set(oset); kcpc_ctx_free(octx); } @@ -507,6 +508,7 @@ err: * We failed to configure this request up so free things up and * get out. */ + kcpc_free_configs(set); kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); kcpc_free_set(set); kcpc_ctx_free(ctx); @@ -537,30 +539,37 @@ dcpc_disable_cpu(cpu_t *c) set = ctx->kc_set; kcpc_free_configs(set); - kmem_free(set->ks_data, set->ks_nreqs * sizeof (uint64_t)); kcpc_free_set(set); kcpc_ctx_free(ctx); } /* - * Stop overflow interrupts being actively processed so that per-CPU - * configuration state can be changed safely and correctly. Each CPU has a - * dcpc interrupt state byte which is transitioned from DCPC_INTR_FREE (the - * "free" state) to DCPC_INTR_CONFIG (the "configuration in process" state) - * before any configuration state is changed on any CPUs. The hardware overflow - * handler, kcpc_hw_overflow_intr(), will only process an interrupt when a - * configuration is not in process (i.e. the state is marked as free). During - * interrupt processing the state is set to DCPC_INTR_PROCESSING by the - * overflow handler. + * The dcpc_*_interrupts() routines are responsible for manipulating the + * per-CPU dcpc interrupt state byte. The purpose of the state byte is to + * synchronize processing of hardware overflow interrupts wth configuration + * changes made to the CPU performance counter subsystem by the dcpc provider. + * + * The dcpc provider claims ownership of the overflow interrupt mechanism + * by transitioning the state byte from DCPC_INTR_INACTIVE (indicating the + * dcpc provider is not in use) to DCPC_INTR_FREE (the dcpc provider owns the + * overflow mechanism and interrupts may be processed). Before modifying + * a CPUs configuration state the state byte is transitioned from + * DCPC_INTR_FREE to DCPC_INTR_CONFIG ("configuration in process" state). + * The hardware overflow handler, kcpc_hw_overflow_intr(), will only process + * an interrupt when a configuration is not in process (i.e. the state is + * marked as free). During interrupt processing the state is set to + * DCPC_INTR_PROCESSING by the overflow handler. When the last dcpc based + * enabling is removed, the state byte is set to DCPC_INTR_INACTIVE to indicate + * the dcpc provider is no longer interested in overflow interrupts. */ static void dcpc_block_interrupts(void) { - cpu_t *c; + cpu_t *c = cpu_list; uint8_t *state; - c = cpu_list; + ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE); do { state = &cpu_core[c->cpu_id].cpuc_dcpc_intr_state; @@ -581,6 +590,27 @@ dcpc_release_interrupts(void) { cpu_t *c = cpu_list; + ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE); + + do { + cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE; + membar_producer(); + } while ((c = c->cpu_next) != cpu_list); +} + +/* + * Transition all CPUs dcpc interrupt state from DCPC_INTR_INACTIVE to + * to DCPC_INTR_FREE. This indicates that the dcpc provider is now + * responsible for handling all overflow interrupt activity. Should only be + * called before enabling the first dcpc based probe. + */ +static void +dcpc_claim_interrupts(void) +{ + cpu_t *c = cpu_list; + + ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state == DCPC_INTR_INACTIVE); + do { cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_FREE; membar_producer(); @@ -588,6 +618,24 @@ dcpc_release_interrupts(void) } /* + * Set all CPUs dcpc interrupt state to DCPC_INTR_INACTIVE to indicate that + * the dcpc provider is no longer processing overflow interrupts. Only called + * during removal of the last dcpc based enabling. + */ +static void +dcpc_surrender_interrupts(void) +{ + cpu_t *c = cpu_list; + + ASSERT(cpu_core[c->cpu_id].cpuc_dcpc_intr_state != DCPC_INTR_INACTIVE); + + do { + cpu_core[c->cpu_id].cpuc_dcpc_intr_state = DCPC_INTR_INACTIVE; + membar_producer(); + } while ((c = c->cpu_next) != cpu_list); +} + +/* * dcpc_program_event() can be called owing to a new enabling or if a multi * overflow platform has disabled a request but needs to program the requests * that are still valid. @@ -751,10 +799,13 @@ dcpc_enable(void *arg, dtrace_id_t id, void *parg) dcpc_enablings < cpc_ncounters)) { /* * Before attempting to program the first enabling we need to - * invalidate any lwp-based contexts. + * invalidate any lwp-based contexts and lay claim to the + * overflow interrupt mechanism. */ - if (dcpc_enablings == 0) + if (dcpc_enablings == 0) { kcpc_invalidate_all(); + dcpc_claim_interrupts(); + } if (dcpc_program_event(pp) == 0) { dcpc_enablings++; @@ -784,6 +835,13 @@ dcpc_enable(void *arg, dtrace_id_t id, void *parg) } while ((c = c->cpu_next) != cpu_list); } + /* + * Give up any claim to the overflow interrupt mechanism if no + * dcpc based enablings exist. + */ + if (dcpc_enablings == 0) + dcpc_surrender_interrupts(); + dtrace_cpc_in_use--; dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; pp->dcpc_actv_req_idx = pp->dcpc_picno = -1; @@ -850,7 +908,7 @@ dcpc_disable(void *arg, dtrace_id_t id, void *parg) } while ((c = c->cpu_next) != cpu_list); dcpc_actv_reqs[pp->dcpc_actv_req_idx] = NULL; - dcpc_release_interrupts(); + dcpc_surrender_interrupts(); } else { /* * This platform can support multiple overflow events and diff --git a/usr/src/uts/common/sys/cpc_impl.h b/usr/src/uts/common/sys/cpc_impl.h index ae89c90508..bc89006ea9 100644 --- a/usr/src/uts/common/sys/cpc_impl.h +++ b/usr/src/uts/common/sys/cpc_impl.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -189,17 +189,17 @@ typedef struct __cpc_args32 { * a platform supports. */ -/* No configuration events or overflow interrupts are currently in process. */ -#define DCPC_INTR_FREE 0 - -/* An overflow interrupt is currently being processed. */ -#define DCPC_INTR_PROCESSING 1 - -/* The cpc subsystem is currently being configured by the dcpc provider. */ -#define DCPC_INTR_CONFIG 2 +enum dcpc_intr_state { + DCPC_INTR_INACTIVE, /* The dcpc provider is currently not in use */ + DCPC_INTR_FREE, /* No config events or ovf ints in progress */ + DCPC_INTR_PROCESSING, /* An overflow interrupt is being processed */ + DCPC_INTR_CONFIG /* cpc subsystem being configured by dcpc */ +}; -#define DCPC_UMASK 1 /* The platform supports a "umask" attribute. */ -#define DCPC_EMASK 2 /* The platform supports an "emask" attribute. */ +enum dcpc_mask_attr { + DCPC_UMASK = 0x1, /* The platform supports a "umask" attribute */ + DCPC_EMASK = 0x2 /* The platform supports an "emask" attribute */ +}; #ifdef __sparc extern uint64_t ultra_gettick(void); diff --git a/usr/src/uts/intel/ia32/os/cpc_subr.c b/usr/src/uts/intel/ia32/os/cpc_subr.c index 1e3049a399..df7d5cda88 100644 --- a/usr/src/uts/intel/ia32/os/cpc_subr.c +++ b/usr/src/uts/intel/ia32/os/cpc_subr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -89,7 +89,7 @@ kcpc_cpu_setup(cpu_setup_t what, int cpuid, void *arg) * If any CPU-bound contexts exist, we don't need to invalidate * anything, as no per-LWP contexts can coexist. */ - if (kcpc_cpuctx) + if (kcpc_cpuctx || dtrace_cpc_in_use) return (0); /* |