staticinlineint ctr_stcctm(enum cpumf_ctr_set set, u64 range, u64 *dest)
{ switch (set) { case CPUMF_CTR_SET_BASIC: return stcctm(BASIC, range, dest); case CPUMF_CTR_SET_USER: return stcctm(PROBLEM_STATE, range, dest); case CPUMF_CTR_SET_CRYPTO: return stcctm(CRYPTO_ACTIVITY, range, dest); case CPUMF_CTR_SET_EXT: return stcctm(EXTENDED, range, dest); case CPUMF_CTR_SET_MT_DIAG: return stcctm(MT_DIAG_CLEARING, range, dest); case CPUMF_CTR_SET_MAX: return 3;
} return 3;
}
struct cpu_cf_events {
refcount_t refcnt; /* Reference count */
atomic_t ctr_set[CPUMF_CTR_SET_MAX];
u64 state; /* For perf_event_open SVC */
u64 dev_state; /* For /dev/hwctr */ unsignedint flags;
size_t used; /* Bytes used in data */
size_t usedss; /* Bytes used in start/stop */ unsignedchar start[PAGE_SIZE]; /* Counter set at event add */ unsignedchar stop[PAGE_SIZE]; /* Counter set at event delete */ unsignedchar data[PAGE_SIZE]; /* Counter set at /dev/hwctr */ unsignedint sets; /* # Counter set saved in memory */
};
staticunsignedint cfdiag_cpu_speed; /* CPU speed for CF_DIAG trailer */ static debug_info_t *cf_dbg;
/* * The CPU Measurement query counter information instruction contains * information which varies per machine generation, but is constant and * does not change when running on a particular machine, such as counter * first and second version number. This is needed to determine the size * of counter sets. Extract this information at device driver initialization.
*/ staticstruct cpumf_ctr_info cpumf_ctr_info;
staticstruct cpu_cf_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */ struct cpu_cf_ptr __percpu *cfptr;
} cpu_cf_root;
/* * Serialize event initialization and event removal. Both are called from * user space in task context with perf_event_open() and close() * system calls. * * This mutex serializes functions cpum_cf_alloc_cpu() called at event * initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu() * called at event removal via call back function hw_perf_event_destroy() * when the event is deleted. They are serialized to enforce correct * bookkeeping of pointer and reference counts anchored by * struct cpu_cf_root and the access to cpu_cf_root::refcnt and the * per CPU pointers stored in cpu_cf_root::cfptr.
*/ static DEFINE_MUTEX(pmc_reserve_mutex);
/* * Get pointer to per-cpu structure. * * Function get_cpu_cfhw() is called from * - cfset_copy_all(): This function is protected by cpus_read_lock(), so * CPU hot plug remove can not happen. Event removal requires a close() * first. * * Function this_cpu_cfhw() is called from perf common code functions: * - pmu_{en|dis}able(), pmu_{add|del}()and pmu_{start|stop}(): * All functions execute with interrupts disabled on that particular CPU. * - cfset_ioctl_{on|off}, cfset_cpu_read(): see comment cfset_copy_all(). * * Therefore it is safe to access the CPU specific pointer to the event.
*/ staticstruct cpu_cf_events *get_cpu_cfhw(int cpu)
{ struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr;
if (p) { struct cpu_cf_ptr *q = per_cpu_ptr(p, cpu);
/* Disable counter sets on dedicated CPU */ staticvoid cpum_cf_reset_cpu(void *flags)
{
lcctl(0);
}
/* Free per CPU data when the last event is removed. */ staticvoid cpum_cf_free_root(void)
{ if (!refcount_dec_and_test(&cpu_cf_root.refcnt)) return;
free_percpu(cpu_cf_root.cfptr);
cpu_cf_root.cfptr = NULL;
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n",
__func__, refcount_read(&cpu_cf_root.refcnt),
!cpu_cf_root.cfptr);
}
/* * On initialization of first event also allocate per CPU data dynamically. * Start with an array of pointers, the array size is the maximum number of * CPUs possible, which might be larger than the number of CPUs currently * online.
*/ staticint cpum_cf_alloc_root(void)
{ int rc = 0;
if (refcount_inc_not_zero(&cpu_cf_root.refcnt)) return rc;
/* The memory is already zeroed. */
cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr); if (cpu_cf_root.cfptr) {
refcount_set(&cpu_cf_root.refcnt, 1);
on_each_cpu(cpum_cf_reset_cpu, NULL, 1);
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
} else {
rc = -ENOMEM;
}
return rc;
}
/* Free CPU counter data structure for a PMU */ staticvoid cpum_cf_free_cpu(int cpu)
{ struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p;
mutex_lock(&pmc_reserve_mutex); /* * When invoked via CPU hotplug handler, there might be no events * installed or that particular CPU might not have an * event installed. This anchor pointer can be NULL!
*/ if (!cpu_cf_root.cfptr) goto out;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf; /* * Might be zero when called from CPU hotplug handler and no event * installed on that CPU, but on different CPUs.
*/ if (!cpuhw) goto out;
/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */ staticint cpum_cf_alloc_cpu(int cpu)
{ struct cpu_cf_events *cpuhw; struct cpu_cf_ptr *p; int rc;
mutex_lock(&pmc_reserve_mutex);
rc = cpum_cf_alloc_root(); if (rc) goto unlock;
p = per_cpu_ptr(cpu_cf_root.cfptr, cpu);
cpuhw = p->cpucf;
if (!cpuhw) {
cpuhw = kzalloc(sizeof(*cpuhw), GFP_KERNEL); if (cpuhw) {
p->cpucf = cpuhw;
refcount_set(&cpuhw->refcnt, 1);
} else {
rc = -ENOMEM;
}
} else {
refcount_inc(&cpuhw->refcnt);
} if (rc) { /* * Error in allocation of event, decrement anchor. Since * cpu_cf_event in not created, its destroy() function is not * invoked. Adjust the reference counter for the anchor.
*/
cpum_cf_free_root();
}
unlock:
mutex_unlock(&pmc_reserve_mutex); return rc;
}
/* * Create/delete per CPU data structures for /dev/hwctr interface and events * created by perf_event_open(). * If cpu is -1, track task on all available CPUs. This requires * allocation of hardware data structures for all CPUs. This setup handles * perf_event_open() with task context and /dev/hwctr interface. * If cpu is non-zero install event on this CPU only. This setup handles * perf_event_open() with CPU context.
*/ staticint cpum_cf_alloc(int cpu)
{
cpumask_var_t mask; int rc;
#define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */ /* interval in seconds */
/* Counter sets are stored as data stream in a page sized memory buffer and * exported to user space via raw data attached to the event sample data. * Each counter set starts with an eight byte header consisting of: * - a two byte eye catcher (0xfeef) * - a one byte counter set number * - a two byte counter set size (indicates the number of counters in this set) * - a three byte reserved value (must be zero) to make the header the same * size as a counter value. * All counter values are eight byte in size. * * All counter sets are followed by a 64 byte trailer. * The trailer consists of a: * - flag field indicating valid fields when corresponding bit set * - the counter facility first and second version number * - the CPU speed if nonzero * - the time stamp the counter sets have been collected * - the time of day (TOD) base value * - the machine type. * * The counter sets are saved when the process is prepared to be executed on a * CPU and saved again when the process is going to be removed from a CPU. * The difference of both counter sets are calculated and stored in the event * sample data area.
*/ struct cf_ctrset_entry { /* CPU-M CF counter set entry (8 byte) */ unsignedint def:16; /* 0-15 Data Entry Format */ unsignedint set:16; /* 16-31 Counter set identifier */ unsignedint ctr:16; /* 32-47 Number of stored counters */ unsignedint res1:16; /* 48-63 Reserved */
};
struct cf_trailer_entry { /* CPU-M CF_DIAG trailer (64 byte) */ /* 0 - 7 */ union { struct { unsignedint clock_base:1; /* TOD clock base set */ unsignedint speed:1; /* CPU speed set */ /* Measurement alerts */ unsignedint mtda:1; /* Loss of MT ctr. data alert */ unsignedint caca:1; /* Counter auth. change alert */ unsignedint lcda:1; /* Loss of counter data alert */
}; unsignedlong flags; /* 0-63 All indicators */
}; /* 8 - 15 */ unsignedint cfvn:16; /* 64-79 Ctr First Version */ unsignedint csvn:16; /* 80-95 Ctr Second Version */ unsignedint cpu_speed:32; /* 96-127 CPU speed */ /* 16 - 23 */ unsignedlong timestamp; /* 128-191 Timestamp (TOD) */ /* 24 - 55 */ union { struct { unsignedlong progusage1; unsignedlong progusage2; unsignedlong progusage3; unsignedlong tod_base;
}; unsignedlong progusage[4];
}; /* 56 - 63 */ unsignedint mach_type:16; /* Machine type */ unsignedint res1:16; /* Reserved */ unsignedint res2:32; /* Reserved */
};
/* Create the trailer data at the end of a page. */ staticvoid cfdiag_trailer(struct cf_trailer_entry *te)
{ struct cpuid cpuid;
get_cpu_id(&cpuid); /* Machine type */
te->mach_type = cpuid.machine;
te->cpu_speed = cfdiag_cpu_speed; if (te->cpu_speed)
te->speed = 1;
te->clock_base = 1; /* Save clock base */
te->tod_base = tod_clock_base.tod;
te->timestamp = get_tod_clock_fast();
}
/* * The number of counters per counter set varies between machine generations, * but is constant when running on a particular machine generation. * Determine each counter set size at device driver initialization and * retrieve it later.
*/ static size_t cpumf_ctr_setsizes[CPUMF_CTR_SET_MAX]; staticvoid cpum_cf_make_setsize(enum cpumf_ctr_set ctrset)
{
size_t ctrset_size = 0;
switch (ctrset) { case CPUMF_CTR_SET_BASIC: if (cpumf_ctr_info.cfvn >= 1)
ctrset_size = 6; break; case CPUMF_CTR_SET_USER: if (cpumf_ctr_info.cfvn == 1)
ctrset_size = 6; elseif (cpumf_ctr_info.cfvn >= 3)
ctrset_size = 2; break; case CPUMF_CTR_SET_CRYPTO: if (cpumf_ctr_info.csvn >= 1 && cpumf_ctr_info.csvn <= 5)
ctrset_size = 16; elseif (cpumf_ctr_info.csvn >= 6)
ctrset_size = 20; break; case CPUMF_CTR_SET_EXT: if (cpumf_ctr_info.csvn == 1)
ctrset_size = 32; elseif (cpumf_ctr_info.csvn == 2)
ctrset_size = 48; elseif (cpumf_ctr_info.csvn >= 3 && cpumf_ctr_info.csvn <= 5)
ctrset_size = 128; elseif (cpumf_ctr_info.csvn >= 6 && cpumf_ctr_info.csvn <= 8)
ctrset_size = 160; break; case CPUMF_CTR_SET_MT_DIAG: if (cpumf_ctr_info.csvn > 3)
ctrset_size = 48; break; case CPUMF_CTR_SET_MAX: break;
}
cpumf_ctr_setsizes[ctrset] = ctrset_size;
}
/* * Return the maximum possible counter set size (in number of 8 byte counters) * depending on type and model number.
*/ static size_t cpum_cf_read_setsize(enum cpumf_ctr_set ctrset)
{ return cpumf_ctr_setsizes[ctrset];
}
/* Read a counter set. The counter set number determines the counter set and * the CPUM-CF first and second version number determine the number of * available counters in each counter set. * Each counter set starts with header containing the counter set number and * the number of eight byte counters. * * The functions returns the number of bytes occupied by this counter set * including the header. * If there is no counter in the counter set, this counter set is useless and * zero is returned on this case. * * Note that the counter sets may not be enabled or active and the stcctm * instruction might return error 3. Depending on error_ok value this is ok, * for example when called from cpumf_pmu_start() call back function.
*/ static size_t cfdiag_getctrset(struct cf_ctrset_entry *ctrdata, int ctrset,
size_t room, bool error_ok)
{
size_t ctrset_size, need = 0; int rc = 3; /* Assume write failure */
/* Read out all counter sets and save them in the provided data buffer. * The last 64 byte host an artificial trailer entry.
*/ static size_t cfdiag_getctr(void *data, size_t sz, unsignedlong auth, bool error_ok)
{ struct cf_trailer_entry *trailer;
size_t offset = 0, done; int i;
memset(data, 0, sz);
sz -= sizeof(*trailer); /* Always room for trailer */ for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) { struct cf_ctrset_entry *ctrdata = data + offset;
if (!(auth & cpumf_ctr_ctl[i])) continue; /* Counter set not authorized */
/* Calculate the difference for each counter in a counter set. */ staticvoid cfdiag_diffctrset(u64 *pstart, u64 *pstop, int counters)
{ for (; --counters >= 0; ++pstart, ++pstop) if (*pstop >= *pstart)
*pstop -= *pstart; else
*pstop = *pstart - *pstop + 1;
}
/* Scan the counter sets and calculate the difference of each counter * in each set. The result is the increment of each counter during the * period the counter set has been activated. * * Return true on success.
*/ staticint cfdiag_diffctr(struct cpu_cf_events *cpuhw, unsignedlong auth)
{ struct cf_trailer_entry *trailer_start, *trailer_stop; struct cf_ctrset_entry *ctrstart, *ctrstop;
size_t offset = 0; int i;
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
ctrstart = (struct cf_ctrset_entry *)(cpuhw->start + offset);
ctrstop = (struct cf_ctrset_entry *)(cpuhw->stop + offset);
/* Counter set not authorized */ if (!(auth & cpumf_ctr_ctl[i])) continue; /* Counter set size zero was not saved */ if (!cpum_cf_read_setsize(i)) continue;
if (memcmp(ctrstop, ctrstart, sizeof(*ctrstop))) {
pr_err_once("cpum_cf_diag counter set compare error " "in set %i\n", ctrstart->set); return 0;
} if (ctrstart->def == CF_DIAG_CTRSET_DEF) {
cfdiag_diffctrset((u64 *)(ctrstart + 1),
(u64 *)(ctrstop + 1), ctrstart->ctr);
offset += ctrstart->ctr * sizeof(u64) + sizeof(*ctrstart);
}
}
/* Save time_stamp from start of event in stop's trailer */
trailer_start = (struct cf_trailer_entry *)(cpuhw->start + offset);
trailer_stop = (struct cf_trailer_entry *)(cpuhw->stop + offset);
trailer_stop->progusage[0] = trailer_start->timestamp;
return 1;
}
staticenum cpumf_ctr_set get_counter_set(u64 event)
{ int set = CPUMF_CTR_SET_MAX;
if (event < 32)
set = CPUMF_CTR_SET_BASIC; elseif (event < 64)
set = CPUMF_CTR_SET_USER; elseif (event < 128)
set = CPUMF_CTR_SET_CRYPTO; elseif (event < 288)
set = CPUMF_CTR_SET_EXT; elseif (event >= 448 && event < 496)
set = CPUMF_CTR_SET_MT_DIAG;
/* check required version for counter sets */ switch (set) { case CPUMF_CTR_SET_BASIC: case CPUMF_CTR_SET_USER: if (cpumf_ctr_info.cfvn < 1)
err = -EOPNOTSUPP; break; case CPUMF_CTR_SET_CRYPTO: if ((cpumf_ctr_info.csvn >= 1 && cpumf_ctr_info.csvn <= 5 &&
config > 79) || (cpumf_ctr_info.csvn >= 6 && config > 83))
err = -EOPNOTSUPP; break; case CPUMF_CTR_SET_EXT: if (cpumf_ctr_info.csvn < 1)
err = -EOPNOTSUPP; if ((cpumf_ctr_info.csvn == 1 && config > 159) ||
(cpumf_ctr_info.csvn == 2 && config > 175) ||
(cpumf_ctr_info.csvn >= 3 && cpumf_ctr_info.csvn <= 5 &&
config > 255) ||
(cpumf_ctr_info.csvn >= 6 && config > 287))
err = -EOPNOTSUPP; break; case CPUMF_CTR_SET_MT_DIAG: if (cpumf_ctr_info.csvn <= 3)
err = -EOPNOTSUPP; /* * MT-diagnostic counters are read-only. The counter set * is automatically enabled and activated on all CPUs with * multithreading (SMT). Deactivation of multithreading * also disables the counter set. State changes are ignored * by lcctl(). Because Linux controls SMT enablement through * a kernel parameter only, the counter set is either disabled * or enabled and active. * * Thus, the counters can only be used if SMT is on and the * counter set is enabled and active.
*/
mtdiag_ctl = cpumf_ctr_ctl[CPUMF_CTR_SET_MT_DIAG]; if (!((cpumf_ctr_info.auth_ctl & mtdiag_ctl) &&
(cpumf_ctr_info.enable_ctl & mtdiag_ctl) &&
(cpumf_ctr_info.act_ctl & mtdiag_ctl)))
err = -EOPNOTSUPP; break; case CPUMF_CTR_SET_MAX:
err = -EOPNOTSUPP;
}
return err;
}
/* * Change the CPUMF state to active. * Enable and activate the CPU-counter sets according * to the per-cpu control state.
*/ staticvoid cpumf_pmu_enable(struct pmu *pmu)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); int err;
if (!cpuhw || (cpuhw->flags & PMU_F_ENABLED)) return;
err = lcctl(cpuhw->state | cpuhw->dev_state); if (err)
pr_err("Enabling the performance measuring unit failed with rc=%x\n", err); else
cpuhw->flags |= PMU_F_ENABLED;
}
/* * Change the CPUMF state to inactive. * Disable and enable (inactive) the CPU-counter sets according * to the per-cpu control state.
*/ staticvoid cpumf_pmu_disable(struct pmu *pmu)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw();
u64 inactive; int err;
if (!cpuhw || !(cpuhw->flags & PMU_F_ENABLED)) return;
inactive = cpuhw->state & ~((1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1);
inactive |= cpuhw->dev_state;
err = lcctl(inactive); if (err)
pr_err("Disabling the performance measuring unit failed with rc=%x\n", err); else
cpuhw->flags &= ~PMU_F_ENABLED;
}
/* Release the PMU if event is the last perf event */ staticvoid hw_perf_event_destroy(struct perf_event *event)
{
cpum_cf_free(event->cpu);
}
switch (type) { case PERF_TYPE_RAW: /* Raw events are used to access counters directly,
* hence do not permit excludes */ if (attr->exclude_kernel || attr->exclude_user ||
attr->exclude_hv) return -EOPNOTSUPP;
ev = attr->config; break;
case PERF_TYPE_HARDWARE:
ev = attr->config; if (!attr->exclude_user && attr->exclude_kernel) { /* * Count user space (problem-state) only * Handle events 32 and 33 as 0:u and 1:u
*/ if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_user)) return -EOPNOTSUPP;
ev = cpumf_generic_events_user[ev];
}
} elseif (!attr->exclude_kernel && attr->exclude_user) { /* No support for kernel space counters only */ return -EOPNOTSUPP;
} else { /* Count user and kernel space, incl. events 32 + 33 */ if (!is_userspace_event(ev)) { if (ev >= ARRAY_SIZE(cpumf_generic_events_basic)) return -EOPNOTSUPP;
ev = cpumf_generic_events_basic[ev];
}
} break;
default: return -ENOENT;
}
if (ev == -1) return -ENOENT;
if (ev > PERF_CPUM_CF_MAX_CTR) return -ENOENT;
/* Obtain the counter set to which the specified counter belongs */
set = get_counter_set(ev); switch (set) { case CPUMF_CTR_SET_BASIC: case CPUMF_CTR_SET_USER: case CPUMF_CTR_SET_CRYPTO: case CPUMF_CTR_SET_EXT: case CPUMF_CTR_SET_MT_DIAG: /* * Use the hardware perf event structure to store the * counter number in the 'config' member and the counter * set number in the 'config_base' as bit mask. * It is later used to enable/disable the counter(s).
*/
hwc->config = ev;
hwc->config_base = cpumf_ctr_ctl[set]; break; case CPUMF_CTR_SET_MAX: /* The counter could not be associated to a counter set */ return -EINVAL;
}
/* Initialize for using the CPU-measurement counter facility */ if (cpum_cf_alloc(event->cpu)) return -ENOMEM;
event->destroy = hw_perf_event_destroy;
/* * Finally, validate version and authorization of the counter set. * If the particular CPU counter set is not authorized, * return with -ENOENT in order to fall back to other * PMUs that might suffice the event request.
*/ if (!(hwc->config_base & cpumf_ctr_info.auth_ctl)) return -ENOENT; return validate_ctr_version(hwc->config, set);
}
/* Events CPU_CYCLES and INSTRUCTIONS can be submitted with two different * attribute::type values: * - PERF_TYPE_HARDWARE: * - pmu->type: * Handle both type of invocations identical. They address the same hardware. * The result is different when event modifiers exclude_kernel and/or * exclude_user are also set.
*/ staticint cpumf_pmu_event_type(struct perf_event *event)
{
u64 ev = event->attr.config;
if (cpumf_generic_events_basic[PERF_COUNT_HW_CPU_CYCLES] == ev ||
cpumf_generic_events_basic[PERF_COUNT_HW_INSTRUCTIONS] == ev ||
cpumf_generic_events_user[PERF_COUNT_HW_CPU_CYCLES] == ev ||
cpumf_generic_events_user[PERF_COUNT_HW_INSTRUCTIONS] == ev) return PERF_TYPE_HARDWARE; return PERF_TYPE_RAW;
}
staticint cpumf_pmu_event_init(struct perf_event *event)
{ unsignedint type = event->attr.type; int err = -ENOENT;
if (is_sampling_event(event)) /* No sampling support */ return err; if (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_RAW)
err = __hw_perf_event_init(event, type); elseif (event->pmu->type == type) /* Registered as unknown PMU */
err = __hw_perf_event_init(event, cpumf_pmu_event_type(event));
return err;
}
staticint hw_perf_event_reset(struct perf_event *event)
{
u64 prev, new; int err;
prev = local64_read(&event->hw.prev_count); do {
err = ecctr(event->hw.config, &new); if (err) { if (err != 3) break; /* The counter is not (yet) available. This * might happen if the counter set to which * this counter belongs is in the disabled * state.
*/ new = 0;
}
} while (!local64_try_cmpxchg(&event->hw.prev_count, &prev, new));
staticvoid cpumf_pmu_read(struct perf_event *event)
{ if (event->hw.state & PERF_HES_STOPPED) return;
hw_perf_event_update(event);
}
staticvoid cpumf_pmu_start(struct perf_event *event, int flags)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); struct hw_perf_event *hwc = &event->hw; int i;
if (!(hwc->state & PERF_HES_STOPPED)) return;
hwc->state = 0;
/* (Re-)enable and activate the counter set */
ctr_set_enable(&cpuhw->state, hwc->config_base);
ctr_set_start(&cpuhw->state, hwc->config_base);
/* The counter set to which this counter belongs can be already active. * Because all counters in a set are active, the event->hw.prev_count * needs to be synchronized. At this point, the counter set can be in * the inactive or disabled state.
*/ if (hwc->config == PERF_EVENT_CPUM_CF_DIAG) {
cpuhw->usedss = cfdiag_getctr(cpuhw->start, sizeof(cpuhw->start),
hwc->config_base, true);
} else {
hw_perf_event_reset(event);
}
/* Increment refcount for counter sets */ for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) if ((hwc->config_base & cpumf_ctr_ctl[i]))
atomic_inc(&cpuhw->ctr_set[i]);
}
/* Create perf event sample with the counter sets as raw data. The sample * is then pushed to the event subsystem and the function checks for * possible event overflows. If an event overflow occurs, the PMU is * stopped. * * Return non-zero if an event overflow occurred.
*/ staticint cfdiag_push_sample(struct perf_event *event, struct cpu_cf_events *cpuhw)
{ struct perf_sample_data data; struct perf_raw_record raw; struct pt_regs regs; int overflow;
staticvoid cpumf_pmu_stop(struct perf_event *event, int flags)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); struct hw_perf_event *hwc = &event->hw; int i;
if (!(hwc->state & PERF_HES_STOPPED)) { /* Decrement reference count for this counter set and if this * is the last used counter in the set, clear activation * control and set the counter set state to inactive.
*/ for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) { if (!(hwc->config_base & cpumf_ctr_ctl[i])) continue; if (!atomic_dec_return(&cpuhw->ctr_set[i]))
ctr_set_stop(&cpuhw->state, cpumf_ctr_ctl[i]);
}
hwc->state |= PERF_HES_STOPPED;
}
if (flags & PERF_EF_START)
cpumf_pmu_start(event, PERF_EF_RELOAD);
return 0;
}
staticvoid cpumf_pmu_del(struct perf_event *event, int flags)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); int i;
cpumf_pmu_stop(event, PERF_EF_UPDATE);
/* Check if any counter in the counter set is still used. If not used, * change the counter set to the disabled state. This also clears the * content of all counters in the set. * * When a new perf event has been added but not yet started, this can * clear enable control and resets all counters in a set. Therefore, * cpumf_pmu_start() always has to re-enable a counter set.
*/ for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) if (!atomic_read(&cpuhw->ctr_set[i]))
ctr_set_disable(&cpuhw->state, cpumf_ctr_ctl[i]);
}
staticstruct cfset_session { /* CPUs and counter set bit mask */ struct list_head head; /* Head of list of active processes */
} cfset_session = {
.head = LIST_HEAD_INIT(cfset_session.head)
};
static refcount_t cfset_opencnt = REFCOUNT_INIT(0); /* Access count */ /* * Synchronize access to device /dev/hwc. This mutex protects against * concurrent access to functions cfset_open() and cfset_release(). * Same for CPU hotplug add and remove events triggering * cpum_cf_online_cpu() and cpum_cf_offline_cpu(). * It also serializes concurrent device ioctl access from multiple * processes accessing /dev/hwc. * * The mutex protects concurrent access to the /dev/hwctr session management * struct cfset_session and reference counting variable cfset_opencnt.
*/ static DEFINE_MUTEX(cfset_ctrset_mutex);
/* * CPU hotplug handles only /dev/hwctr device. * For perf_event_open() the CPU hotplug handling is done on kernel common * code: * - CPU add: Nothing is done since a file descriptor can not be created * and returned to the user. * - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and * pmu_delete(). The event itself is removed when the file descriptor is * closed.
*/ staticint cfset_online_cpu(unsignedint cpu);
staticint cpum_cf_online_cpu(unsignedint cpu)
{ int rc = 0;
/* * Ignore notification for perf_event_open(). * Handle only /dev/hwctr device sessions.
*/
mutex_lock(&cfset_ctrset_mutex); if (refcount_read(&cfset_opencnt)) {
rc = cpum_cf_alloc_cpu(cpu); if (!rc)
cfset_online_cpu(cpu);
}
mutex_unlock(&cfset_ctrset_mutex); return rc;
}
staticint cfset_offline_cpu(unsignedint cpu);
staticint cpum_cf_offline_cpu(unsignedint cpu)
{ /* * During task exit processing of grouped perf events triggered by CPU * hotplug processing, pmu_disable() is called as part of perf context * removal process. Therefore do not trigger event removal now for * perf_event_open() created events. Perf common code triggers event * destruction when the event file descriptor is closed. * * Handle only /dev/hwctr device sessions.
*/
mutex_lock(&cfset_ctrset_mutex); if (refcount_read(&cfset_opencnt)) {
cfset_offline_cpu(cpu);
cpum_cf_free_cpu(cpu);
}
mutex_unlock(&cfset_ctrset_mutex); return 0;
}
/* Return true if store counter set multiple instruction is available */ staticinlineint stccm_avail(void)
{ return test_facility(142);
}
/* CPU-measurement alerts for the counter facility */ staticvoid cpumf_measurement_alert(struct ext_code ext_code, unsignedint alert, unsignedlong unused)
{ struct cpu_cf_events *cpuhw;
if (!(alert & CPU_MF_INT_CF_MASK)) return;
inc_irq_stat(IRQEXT_CMC);
/* * Measurement alerts are shared and might happen when the PMU * is not reserved. Ignore these alerts in this case.
*/
cpuhw = this_cpu_cfhw(); if (!cpuhw) return;
/* Support for the CPU Measurement Facility counter set extraction using * device /dev/hwctr. This allows user space programs to extract complete * counter set via normal file operations.
*/
struct cfset_call_on_cpu_parm { /* Parm struct for smp_call_on_cpu */ unsignedint sets; /* Counter set bit mask */
atomic_t cpus_ack; /* # CPUs successfully executed func */
};
struct cfset_request { /* CPUs and counter set bit mask */ unsignedlong ctrset; /* Bit mask of counter set to read */
cpumask_t mask; /* CPU mask to read from */ struct list_head node; /* Chain to cfset_session.head */
};
/* Remove current request from global bookkeeping. Maintain a counter set bit * mask on a per CPU basis. * Done in process context under mutex protection.
*/ staticvoid cfset_session_del(struct cfset_request *p)
{
list_del(&p->node);
}
/* Add current request to global bookkeeping. Maintain a counter set bit mask * on a per CPU basis. * Done in process context under mutex protection.
*/ staticvoid cfset_session_add(struct cfset_request *p)
{
list_add(&p->node, &cfset_session.head);
}
/* The /dev/hwctr device access uses PMU_F_IN_USE to mark the device access * path is currently used. * The cpu_cf_events::dev_state is used to denote counter sets in use by this * interface. It is always or'ed in. If this interface is not active, its * value is zero and no additional counter sets will be included. * * The cpu_cf_events::state is used by the perf_event_open SVC and remains * unchanged. * * perf_pmu_enable() and perf_pmu_enable() and its call backs * cpumf_pmu_enable() and cpumf_pmu_disable() are called by the * performance measurement subsystem to enable per process * CPU Measurement counter facility. * The XXX_enable() and XXX_disable functions are used to turn off * x86 performance monitoring interrupt (PMI) during scheduling. * s390 uses these calls to temporarily stop and resume the active CPU * counters sets during scheduling. * * We do allow concurrent access of perf_event_open() SVC and /dev/hwctr * device access. The perf_event_open() SVC interface makes a lot of effort * to only run the counters while the calling process is actively scheduled * to run. * When /dev/hwctr interface is also used at the same time, the counter sets * will keep running, even when the process is scheduled off a CPU. * However this is not a problem and does not lead to wrong counter values * for the perf_event_open() SVC. The current counter value will be recorded * during schedule-in. At schedule-out time the current counter value is * extracted again and the delta is calculated and added to the event.
*/ /* Stop all counter sets via ioctl interface */ staticvoid cfset_ioctl_off(void *parm)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); struct cfset_call_on_cpu_parm *p = parm; int rc;
/* Check if any counter set used by /dev/hwctr */ for (rc = CPUMF_CTR_SET_BASIC; rc < CPUMF_CTR_SET_MAX; ++rc) if ((p->sets & cpumf_ctr_ctl[rc])) { if (!atomic_dec_return(&cpuhw->ctr_set[rc])) {
ctr_set_disable(&cpuhw->dev_state,
cpumf_ctr_ctl[rc]);
ctr_set_stop(&cpuhw->dev_state,
cpumf_ctr_ctl[rc]);
}
} /* Keep perf_event_open counter sets */
rc = lcctl(cpuhw->dev_state | cpuhw->state); if (rc)
pr_err("Counter set stop %#llx of /dev/%s failed rc=%i\n",
cpuhw->state, S390_HWCTR_DEVICE, rc); if (!cpuhw->dev_state)
cpuhw->flags &= ~PMU_F_IN_USE;
}
/* Start counter sets on particular CPU */ staticvoid cfset_ioctl_on(void *parm)
{ struct cpu_cf_events *cpuhw = this_cpu_cfhw(); struct cfset_call_on_cpu_parm *p = parm; int rc;
cpuhw->flags |= PMU_F_IN_USE;
ctr_set_enable(&cpuhw->dev_state, p->sets);
ctr_set_start(&cpuhw->dev_state, p->sets); for (rc = CPUMF_CTR_SET_BASIC; rc < CPUMF_CTR_SET_MAX; ++rc) if ((p->sets & cpumf_ctr_ctl[rc]))
atomic_inc(&cpuhw->ctr_set[rc]);
rc = lcctl(cpuhw->dev_state | cpuhw->state); /* Start counter sets */ if (!rc)
atomic_inc(&p->cpus_ack); else
pr_err("Counter set start %#llx of /dev/%s failed rc=%i\n",
cpuhw->dev_state | cpuhw->state, S390_HWCTR_DEVICE, rc);
}
cpuhw->dev_state = 0;
rc = lcctl(cpuhw->state); /* Keep perf_event_open counter sets */ if (rc)
pr_err("Counter set release %#llx of /dev/%s failed rc=%i\n",
cpuhw->state, S390_HWCTR_DEVICE, rc);
}
/* This modifies the process CPU mask to adopt it to the currently online * CPUs. Offline CPUs can not be addresses. This call terminates the access * and is usually followed by close() or a new iotcl(..., START, ...) which * creates a new request structure.
*/ staticvoid cfset_all_stop(struct cfset_request *req)
{ struct cfset_call_on_cpu_parm p = {
.sets = req->ctrset,
};
/* Release function is also called when application gets terminated without * doing a proper ioctl(..., S390_HWCTR_STOP, ...) command.
*/ staticint cfset_release(struct inode *inode, struct file *file)
{
mutex_lock(&cfset_ctrset_mutex); /* Open followed by close/exit has no private_data */ if (file->private_data) {
cfset_all_stop(file->private_data);
cfset_session_del(file->private_data);
kfree(file->private_data);
file->private_data = NULL;
} if (refcount_dec_and_test(&cfset_opencnt)) { /* Last close */
on_each_cpu(cfset_release_cpu, NULL, 1);
cpum_cf_free(-1);
}
mutex_unlock(&cfset_ctrset_mutex); return 0;
}
/* * Open via /dev/hwctr device. Allocate all per CPU resources on the first * open of the device. The last close releases all per CPU resources. * Parallel perf_event_open system calls also use per CPU resources. * These invocations are handled via reference counting on the per CPU data * structures.
*/ staticint cfset_open(struct inode *inode, struct file *file)
{ int rc = 0;
if (!perfmon_capable()) return -EPERM;
file->private_data = NULL;
mutex_lock(&cfset_ctrset_mutex); if (!refcount_inc_not_zero(&cfset_opencnt)) { /* First open */
rc = cpum_cf_alloc(-1); if (!rc) {
cfset_session_init();
refcount_set(&cfset_opencnt, 1);
}
}
mutex_unlock(&cfset_ctrset_mutex);
/* Return the maximum required space for all possible CPUs in case one * CPU will be onlined during the START, READ, STOP cycles. * To find out the size of the counter sets, any one CPU will do. They * all have the same counter sets.
*/ static size_t cfset_needspace(unsignedint sets)
{
size_t bytes = 0; int i;
/* No data saved yet */
cpuhw->used = 0;
cpuhw->sets = 0;
memset(cpuhw->data, 0, sizeof(cpuhw->data));
/* Scan the counter sets */ for (set = CPUMF_CTR_SET_BASIC; set < CPUMF_CTR_SET_MAX; ++set) { struct s390_ctrset_setdata *sp = (void *)cpuhw->data +
cpuhw->used;
if (!(p->sets & cpumf_ctr_ctl[set])) continue; /* Counter set not in list */
set_size = cpum_cf_read_setsize(set);
space = sizeof(cpuhw->data) - cpuhw->used;
space = cfset_cpuset_read(sp, set, set_size, space); if (space) {
cpuhw->used += space;
cpuhw->sets += 1;
}
}
}
if (file->private_data) return -EBUSY;
ustart = (struct s390_ctrset_start __user *)arg; if (copy_from_user(&start, ustart, sizeof(start))) return -EFAULT; if (start.version != S390_HWCTR_START_VERSION) return -EINVAL; if (start.counter_sets & ~(cpumf_ctr_ctl[CPUMF_CTR_SET_BASIC] |
cpumf_ctr_ctl[CPUMF_CTR_SET_USER] |
cpumf_ctr_ctl[CPUMF_CTR_SET_CRYPTO] |
cpumf_ctr_ctl[CPUMF_CTR_SET_EXT] |
cpumf_ctr_ctl[CPUMF_CTR_SET_MT_DIAG])) return -EINVAL; /* Invalid counter set */ if (!start.counter_sets) return -EINVAL; /* No counter set at all? */
preq = kzalloc(sizeof(*preq), GFP_KERNEL); if (!preq) return -ENOMEM;
cpumask_clear(&preq->mask);
len = min_t(u64, start.cpumask_len, cpumask_size());
umask = (void __user *)start.cpumask; if (copy_from_user(&preq->mask, umask, len)) {
kfree(preq); return -EFAULT;
} if (cpumask_empty(&preq->mask)) {
kfree(preq); return -EINVAL;
}
need = cfset_needspace(start.counter_sets); if (put_user(need, &ustart->data_bytes)) {
kfree(preq); return -EFAULT;
}
preq->ctrset = start.counter_sets;
ret = cfset_all_start(preq); if (!ret) {
cfset_session_add(preq);
file->private_data = preq;
} else {
kfree(preq);
} return ret;
}
/* Entry point to the /dev/hwctr device interface. * The ioctl system call supports three subcommands: * S390_HWCTR_START: Start the specified counter sets on a CPU list. The * counter set keeps running until explicitly stopped. Returns the number * of bytes needed to store the counter values. If another S390_HWCTR_START * ioctl subcommand is called without a previous S390_HWCTR_STOP stop * command on the same file descriptor, -EBUSY is returned. * S390_HWCTR_READ: Read the counter set values from specified CPU list given * with the S390_HWCTR_START command. * S390_HWCTR_STOP: Stops the counter sets on the CPU list given with the * previous S390_HWCTR_START subcommand.
*/ staticlong cfset_ioctl(struct file *file, unsignedint cmd, unsignedlong arg)
{ int ret;
cpus_read_lock();
mutex_lock(&cfset_ctrset_mutex); switch (cmd) { case S390_HWCTR_START:
ret = cfset_ioctl_start(arg, file); break; case S390_HWCTR_STOP:
ret = cfset_ioctl_stop(file); break; case S390_HWCTR_READ:
ret = cfset_ioctl_read(arg, file->private_data); break; default:
ret = -ENOTTY; break;
}
mutex_unlock(&cfset_ctrset_mutex);
cpus_read_unlock(); return ret;
}
/* Hotplug add of a CPU. Scan through all active processes and add * that CPU to the list of CPUs supplied with ioctl(..., START, ...).
*/ staticint cfset_online_cpu(unsignedint cpu)
{ struct cfset_call_on_cpu_parm p; struct cfset_request *rp;
/* Hotplug remove of a CPU. Scan through all active processes and clear * that CPU from the list of CPUs supplied with ioctl(..., START, ...). * Adjust reference counts.
*/ staticint cfset_offline_cpu(unsignedint cpu)
{ struct cfset_call_on_cpu_parm p; struct cfset_request *rp;
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) { if (cpumf_ctr_info.auth_ctl & cpumf_ctr_ctl[i])
auth |= cpumf_ctr_ctl[i];
} return auth;
}
/* Setup the event. Test for authorized counter sets and only include counter * sets which are authorized at the time of the setup. Including unauthorized * counter sets result in specification exception (and panic).
*/ staticint cfdiag_event_init2(struct perf_event *event)
{ struct perf_event_attr *attr = &event->attr; int err = 0;
/* Set sample_period to indicate sampling */
event->hw.config = attr->config;
event->hw.sample_period = attr->sample_period;
local64_set(&event->hw.period_left, event->hw.sample_period);
local64_set(&event->count, 0);
event->hw.last_period = event->hw.sample_period;
/* Add all authorized counter sets to config_base. The * the hardware init function is either called per-cpu or just once * for all CPUS (event->cpu == -1). This depends on the whether * counting is started for all CPUs or on a per workload base where * the perf event moves from one CPU to another CPU. * Checking the authorization on any CPU is fine as the hardware * applies the same authorization settings to all CPUs.
*/
event->hw.config_base = get_authctrsets();
/* No authorized counter sets, nothing to count/sample */ if (!event->hw.config_base)
err = -EINVAL;
if (event->attr.config != PERF_EVENT_CPUM_CF_DIAG ||
event->attr.type != event->pmu->type) goto out;
/* Raw events are used to access counters directly, * hence do not permit excludes. * This event is useless without PERF_SAMPLE_RAW to return counter set * values as raw data.
*/ if (attr->exclude_kernel || attr->exclude_user || attr->exclude_hv ||
!(attr->sample_type & (PERF_SAMPLE_CPU | PERF_SAMPLE_RAW))) {
err = -EOPNOTSUPP; goto out;
}
/* Initialize for using the CPU-measurement counter facility */ if (cpum_cf_alloc(event->cpu)) return -ENOMEM;
event->destroy = hw_perf_event_destroy;
/* Create cf_diag/events/CF_DIAG event sysfs file. This counter is used * to collect the complete counter sets for a scheduled process. Target * are complete counter sets attached as raw data to the artificial event. * This results in complete counter sets available when a process is * scheduled. Contains the delta of every counter while the process was * running.
*/
CPUMF_EVENT_ATTR(CF_DIAG, CF_DIAG, PERF_EVENT_CPUM_CF_DIAG);
/* Performance monitoring unit for event CF_DIAG. Since this event * is also started and stopped via the perf_event_open() system call, use * the same event enable/disable call back functions. They do not * have a pointer to the perf_event structure as first parameter. * * The functions XXX_add, XXX_del, XXX_start and XXX_stop are also common. * Reuse them and distinguish the event (always first parameter) via * 'config' member.
*/ staticstruct pmu cf_diag = {
.task_ctx_nr = perf_sw_context,
.event_init = cfdiag_event_init,
.pmu_enable = cpumf_pmu_enable,
.pmu_disable = cpumf_pmu_disable,
.add = cpumf_pmu_add,
.del = cpumf_pmu_del,
.start = cpumf_pmu_start,
.stop = cpumf_pmu_stop,
.read = cfdiag_read,
.attr_groups = cfdiag_attr_groups
};
/* Calculate memory needed to store all counter sets together with header and * trailer data. This is independent of the counter set authorization which * can vary depending on the configuration.
*/ static size_t cfdiag_maxsize(struct cpumf_ctr_info *info)
{
size_t max_size = sizeof(struct cf_trailer_entry); enum cpumf_ctr_set i;
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
size_t size = cpum_cf_read_setsize(i);
/* Fallback: CPU speed extract static part. Used in case * CPU Measurement Sampling Facility is turned off.
*/
mhz = __ecag(ECAG_CPU_ATTRIBUTE, 0); if (mhz != -1UL)
cfdiag_cpu_speed = mhz & 0xffffffff;
}
staticint cfset_init(void)
{
size_t need; int rc;
cfdiag_get_cpu_speed(); /* Make sure the counter set data fits into predefined buffer. */
need = cfdiag_maxsize(&cpumf_ctr_info); if (need > sizeof(((struct cpu_cf_events *)0)->start)) {
pr_err("Insufficient memory for PMU(cpum_cf_diag) need=%zu\n",
need); return -ENOMEM;
}
rc = misc_register(&cfset_dev); if (rc) {
pr_err("Registration of /dev/%s failed rc=%i\n",
cfset_dev.name, rc); goto out;
}
rc = perf_pmu_register(&cf_diag, "cpum_cf_diag", -1); if (rc) {
misc_deregister(&cfset_dev);
pr_err("Registration of PMU(cpum_cf_diag) failed with rc=%i\n",
rc);
}
out: return rc;
}
device_initcall(cpumf_pmu_init);
Messung V0.5
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.