staticstruct cpu_cf_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */ struct cpu_cf_ptr __percpu *cfptr;
} cpu_cf_root;
/* 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);
}
/* 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;
/* Read out all counter sets and save them in the provided data buffer. *Thelast64bytehostanartificialtrailerentry.
*/ 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 *ineachset.Theresultistheincrementofeachcounterduringthe *periodthecountersethasbeenactivated. * *Returntrueonsuccess.
*/ 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); return0;
} 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;
return1;
}
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;
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) { /* *Countuserspace(problem-state)only *Handleevents32and33as0:uand1: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: /* *Usethehardwareperfeventstructuretostorethe *counternumberinthe'config'memberandthecounter *setnumberinthe'config_base'asbitmask. *Itislaterusedtoenable/disablethecounter(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;
/* Events CPU_CYCLES and INSTRUCTIONS can be submitted with two different *attribute::typevalues: *-PERF_TYPE_HARDWARE: *-pmu->type: *Handlebothtypeofinvocationsidentical.Theyaddressthesamehardware. *Theresultisdifferentwheneventmodifiersexclude_kerneland/or *exclude_userarealsoset.
*/ 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 *mighthappenifthecountersettowhich *thiscounterbelongsisinthedisabled *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. *Becauseallcountersinasetareactive,theevent->hw.prev_count *needstobesynchronized.Atthispoint,thecountersetcanbein *theinactiveordisabledstate.
*/ 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 *isthenpushedtotheeventsubsystemandthefunctionchecksfor *possibleeventoverflows.Ifaneventoverflowoccurs,thePMUis *stopped. * *Returnnon-zeroifaneventoverflowoccurred.
*/ 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 *isthelastusedcounterintheset,clearactivation *controlandsetthecountersetstatetoinactive.
*/ 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);
return0;
}
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, *changethecountersettothedisabledstate.Thisalsoclearsthe *contentofallcountersintheset. * *Whenanewperfeventhasbeenaddedbutnotyetstarted,thiscan *clearenablecontrolandresetsallcountersinaset.Therefore, *cpumf_pmu_start()alwayshastore-enableacounterset.
*/ 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)
};
/* Support for the CPU Measurement Facility counter set extraction using *device/dev/hwctr.Thisallowsuserspaceprogramstoextractcomplete *countersetvianormalfileoperations.
*/
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 *maskonaperCPUbasis. *Doneinprocesscontextundermutexprotection.
*/ staticvoid cfset_session_del(struct cfset_request *p)
{
list_del(&p->node);
}
/* Add current request to global bookkeeping. Maintain a counter set bit mask *onaperCPUbasis. *Doneinprocesscontextundermutexprotection.
*/ 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 *pathiscurrentlyused. *Thecpu_cf_events::dev_stateisusedtodenotecountersetsinusebythis *interface.Itisalwaysor'edin.Ifthisinterfaceisnotactive,its *valueiszeroandnoadditionalcountersetswillbeincluded. * *Thecpu_cf_events::stateisusedbytheperf_event_openSVCandremains *unchanged. * *perf_pmu_enable()andperf_pmu_enable()anditscallbacks *cpumf_pmu_enable()andcpumf_pmu_disable()arecalledbythe *performancemeasurementsubsystemtoenableperprocess *CPUMeasurementcounterfacility. *TheXXX_enable()andXXX_disablefunctionsareusedtoturnoff *x86performancemonitoringinterrupt(PMI)duringscheduling. *s390usesthesecallstotemporarilystopandresumetheactiveCPU *counterssetsduringscheduling. * *Wedoallowconcurrentaccessofperf_event_open()SVCand/dev/hwctr *deviceaccess.Theperf_event_open()SVCinterfacemakesalotofeffort *toonlyrunthecounterswhilethecallingprocessisactivelyscheduled *torun. *When/dev/hwctrinterfaceisalsousedatthesametime,thecountersets *willkeeprunning,evenwhentheprocessisscheduledoffaCPU. *Howeverthisisnotaproblemanddoesnotleadtowrongcountervalues *fortheperf_event_open()SVC.Thecurrentcountervaluewillberecorded *duringschedule-in.Atschedule-outtimethecurrentcountervalueis *extractedagainandthedeltaiscalculatedandaddedtotheevent.
*/ /* 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.OfflineCPUscannotbeaddresses.Thiscallterminatestheaccess *andisusuallyfollowedbyclose()oranewiotcl(...,START,...)which *createsanewrequeststructure.
*/ 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 *doingaproperioctl(...,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); return0;
}
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 *CPUwillbeonlinedduringtheSTART,READ,STOPcycles. *Tofindoutthesizeofthecountersets,anyoneCPUwilldo.They *allhavethesamecountersets.
*/ 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;
}
}
}
/* Hotplug add of a CPU. Scan through all active processes and add *thatCPUtothelistofCPUssuppliedwithioctl(...,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 *thatCPUfromthelistofCPUssuppliedwithioctl(...,START,...). *Adjustreferencecounts.
*/ 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 *setswhichareauthorizedatthetimeofthesetup.Includingunauthorized *countersetsresultinspecificationexception(andpanic).
*/ 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 *thehardwareinitfunctioniseithercalledper-cpuorjustonce *forallCPUS(event->cpu==-1).Thisdependsonthewhether *countingisstartedforallCPUsoronaperworkloadbasewhere *theperfeventmovesfromoneCPUtoanotherCPU. *CheckingtheauthorizationonanyCPUisfineasthehardware *appliesthesameauthorizationsettingstoallCPUs.
*/
event->hw.config_base = get_authctrsets();
/* No authorized counter sets, nothing to count/sample */ if (!event->hw.config_base)
err = -EINVAL;
/* Calculate memory needed to store all counter sets together with header and *trailerdata.Thisisindependentofthecountersetauthorizationwhich *canvarydependingontheconfiguration.
*/ 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 *CPUMeasurementSamplingFacilityisturnedoff.
*/
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 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.52Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
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.