/* Create the PAI extension 1 control block area. *ThePAIextensioncontrolblock1ispointedtobylowcore *address0x1508foreachCPU.Thiscontrolblockis512bytesinsize *andrequiresa512byteboundaryalignment.
*/ struct paiext_cb { /* PAI extension 1 control block */
u64 header; /* Not used */
u64 reserved1;
u64 acc; /* Addr to analytics counter control block */
u8 reserved2[488];
} __packed;
struct paiext_map { unsignedlong *area; /* Area for CPU to store counters */ struct pai_userdata *save; /* Area to store non-zero counters */ unsignedint active_events; /* # of PAI Extension users */
refcount_t refcnt; struct perf_event *event; /* Perf event for sampling */ struct paiext_cb *paiext_cb; /* PAI extension control block area */ struct list_head syswide_list; /* List system-wide sampling events */
};
staticstruct paiext_root { /* Anchor to per CPU data */
refcount_t refcnt; /* Overall active events */ struct paiext_mapptr __percpu *mapptr;
} paiext_root;
/* Free per CPU data when the last event is removed. */ staticvoid paiext_root_free(void)
{ if (refcount_dec_and_test(&paiext_root.refcnt)) {
free_percpu(paiext_root.mapptr);
paiext_root.mapptr = NULL;
}
debug_sprintf_event(paiext_dbg, 5, "%s root.refcount %d\n", __func__,
refcount_read(&paiext_root.refcnt));
}
/* On initialization of first event also allocate per CPU data dynamically. *Startwithanarrayofpointers,thearraysizeisthemaximumnumberof *CPUspossible,whichmightbelargerthanthenumberofCPUscurrently *online.
*/ staticint paiext_root_alloc(void)
{ if (!refcount_inc_not_zero(&paiext_root.refcnt)) { /* The memory is already zeroed. */
paiext_root.mapptr = alloc_percpu(struct paiext_mapptr); if (!paiext_root.mapptr) { /* Returning without refcnt adjustment is ok. The *errorcodeishandledbypaiext_alloc()which *decrementsrefcntwhenaneventcannotbe *created.
*/ return -ENOMEM;
}
refcount_set(&paiext_root.refcnt, 1);
} return0;
}
/* Protects against concurrent increment of sampler and counter member *incrementsatthesametimeandprohibitsconcurrentexecutionof *countingandsamplingevents. *Ensuresthatanalyticscounterblockisdeallocatedonlywhenthe *samplingandcountingonthatcpuiszero. *Fordetailsseepaiext_alloc().
*/ static DEFINE_MUTEX(paiext_reserve_mutex);
/* Release the PMU if event is the last perf event */ staticvoid paiext_event_destroy_cpu(struct perf_event *event, int cpu)
{ struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, cpu); struct paiext_map *cpump = mp->mapptr;
mutex_lock(&paiext_reserve_mutex); if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */
paiext_free(mp);
paiext_root_free();
mutex_unlock(&paiext_reserve_mutex);
}
staticvoid paiext_event_destroy(struct perf_event *event)
{ int cpu;
undo: if (rc) { /* Error in allocation of event, decrement anchor. Since *theeventinnotcreated,itsdestroy()functionisnever *invoked.Adjustthereferencecounterfortheanchor.
*/
paiext_root_free();
}
unlock:
mutex_unlock(&paiext_reserve_mutex); /* If rc is non-zero, no increment of counter/sampler was done. */ return rc;
}
/* The PAI extension 1 control block supports up to 128 entries. Return *theindexwithinPAIE1_CBgiventheeventnumber.Alsovalidateevent *number.
*/ staticint paiext_event_valid(struct perf_event *event)
{
u64 cfg = event->attr.config;
/* Might be called on different CPU than the one the event is intended for. */ staticint paiext_event_init(struct perf_event *event)
{ struct perf_event_attr *a = &event->attr; int rc;
/* PMU pai_ext registered as PERF_TYPE_RAW, check event type */ if (a->type != PERF_TYPE_RAW && event->pmu->type != a->type) return -ENOENT; /* PAI extension event must be valid and in supported range */
rc = paiext_event_valid(event); if (rc) return rc; /* Allow only event NNPA_ALL for sampling. */ if (a->sample_period && a->config != PAI_NNPA_BASE) return -EINVAL; /* Prohibit exclude_user event selection */ if (a->exclude_user) return -EINVAL; /* Get a page to store last counter values for sampling */ if (a->sample_period) {
PAI_SAVE_AREA(event) = get_zeroed_page(GFP_KERNEL); if (!PAI_SAVE_AREA(event)) return -ENOMEM;
}
if (a->sample_period) {
a->sample_period = 1;
a->freq = 0; /* Register for paicrypt_sched_task() to be called */
event->attach_state |= PERF_ATTACH_SCHED_CB; /* Add raw data which are the memory mapped counters */
a->sample_type |= PERF_SAMPLE_RAW; /* Turn off inheritance */
a->inherit = 0;
}
return0;
}
static u64 paiext_getctr(unsignedlong *area, int nr)
{ return area[nr];
}
/* Read the counter values. Return value from location in buffer. For event *NNPA_ALLsumupallevents.
*/ static u64 paiext_getdata(struct perf_event *event)
{ struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr); struct paiext_map *cpump = mp->mapptr;
u64 sum = 0; int i;
if (event->attr.config != PAI_NNPA_BASE) return paiext_getctr(cpump->area,
event->attr.config - PAI_NNPA_BASE);
for (i = 1; i <= paiext_cnt; i++)
sum += paiext_getctr(cpump->area, i);
paiext_stop(event, PERF_EF_UPDATE); if (--cpump->active_events == 0) { /* Disable CPU instruction lookup for PAIE1 control block */
local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT);
pcb->acc = 0;
get_lowcore()->aicd = 0;
}
}
/* Create raw data and save it in buffer. Returns number of bytes copied. *Savesonlypositivecounterentriesoftheform *2bytes:Numberofcounter *8bytes:Valueofcounter
*/ static size_t paiext_copy(struct pai_userdata *userdata, unsignedlong *area, unsignedlong *area_old)
{ int i, outidx = 0;
for (i = 1; i <= paiext_cnt; i++) {
u64 val = paiext_getctr(area, i);
u64 val_old = paiext_getctr(area_old, i);
if (val >= val_old)
val -= val_old; else
val = (~0ULL - val_old) + val + 1; if (val) {
userdata[outidx].num = i;
userdata[outidx].value = val;
outidx++;
}
} return outidx * sizeof(*userdata);
}
/* Write sample when one or more counters values are nonzero. * *Note:Thefunctionpaiext_sched_task()andpaiext_push_sample()arenot *invokedafterfunctionpaiext_del()hasbeencalledbecauseoffunction *perf_sched_cb_dec(). *Thefunctionpaiext_sched_task()andpaiext_push_sample()areonly *calledwhensamplingisactive.Functionperf_sched_cb_inc() *hasbeeninvokedtoinstallfunctionpaiext_sched_task()ascallback *torunatcontextswitchtime(seepaiext_add()). * *Thiscausesfunctionperf_event_context_sched_out()and *perf_event_context_sched_in()tocheckwhetherthePMUhasinstalledan *sched_task()callback.Thatcallbackisnotactiveafterpaiext_del() *returnsandhasdeletedtheeventonthatCPU.
*/ staticint paiext_push_sample(size_t rawsize, struct paiext_map *cpump, struct perf_event *event)
{ struct perf_sample_data data; struct perf_raw_record raw; struct pt_regs regs; int overflow;
overflow = perf_event_overflow(event, &data, ®s);
perf_event_update_userpage(event); /* Save NNPA lowcore area after read in event */
memcpy((void *)PAI_SAVE_AREA(event), cpump->area,
PAIE1_CTRBLOCK_SZ); return overflow;
}
/* Check if there is data to be saved on schedule out of a task. */ staticvoid paiext_have_sample(struct perf_event *event, struct paiext_map *cpump)
{
size_t rawsize;
if (!event) return;
rawsize = paiext_copy(cpump->save, cpump->area,
(unsignedlong *)PAI_SAVE_AREA(event)); if (rawsize) /* Incremented counters */
paiext_push_sample(rawsize, cpump, event);
}
/* Check if there is data to be saved on schedule out of a task. */ staticvoid paiext_have_samples(void)
{ struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr); struct paiext_map *cpump = mp->mapptr; struct perf_event *event;
/* Called on schedule-in and schedule-out. No access to event structure, *butforsamplingonlyeventNNPA_ALLisallowed.
*/ staticvoid paiext_sched_task(struct perf_event_pmu_context *pmu_ctx, struct task_struct *task, bool sched_in)
{ /* We started with a clean page on event installation. So read out *resultsonschedule_outandifpagewasdirty,saveoldvalues.
*/ if (!sched_in)
paiext_have_samples();
}
/* Attribute definitions for pai extension1 interface. As with other CPU *MeasurementFacilities,thereisoneattributepermappedcounter. *Thenumberofmappedcountersmayvarypermachinegeneration.Use *theQUERYPROCESSORACTIVITYCOUNTERINFORMATION(QPACI)instruction *todeterminethenumberofmappedcounters.Theinstructionsreturns *apositivenumber,whichisthehighestnumberofsupportedcounters. *Allcounterslessthanthisnumberarealsosupported,thereareno *holes.Areturnednumberofzeromeansnosupportformappedcounters. * *Theidentificationofthecounterisauniquenumber.Thechosenrange *is0x1800+offsetinmappedkernelpage. *AllCPUMeasurementFacilitycountersidentifiersmustbeuniqueand *thenumbersfrom0to496arealreadyusedfortheCPUMeasurement *Counterfacility.Number0x1000to0x103eareusedforPAIcryptography *counters. *Numbers0xb0000,0xbc000and0xbd000arealready *usedfortheCPUMeasurementSamplingfacility.
*/
PMU_FORMAT_ATTR(event, "config:0-63");
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.