/* Create the PAI extension 1 control block area. * The PAI extension control block 1 is pointed to by lowcore * address 0x1508 for each CPU. This control block is 512 bytes in size * and requires a 512 byte boundary alignment.
*/ 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. * 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 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 * error code is handled by paiext_alloc() which * decrements refcnt when an event can not be * created.
*/ return -ENOMEM;
}
refcount_set(&paiext_root.refcnt, 1);
} return 0;
}
/* Protects against concurrent increment of sampler and counter member * increments at the same time and prohibits concurrent execution of * counting and sampling events. * Ensures that analytics counter block is deallocated only when the * sampling and counting on that cpu is zero. * For details see paiext_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;
/* Used to avoid races in checking concurrent access of counting and * sampling for pai_extension events. * * Only one instance of event pai_ext/NNPA_ALL/ for sampling is * allowed and when this event is running, no counting event is allowed. * Several counting events are allowed in parallel, but no sampling event * is allowed while one (or more) counting events are running. * * This function is called in process context and it is safe to block. * When the event initialization functions fails, no other call back will * be invoked. * * Allocate the memory for the event.
*/ staticint paiext_alloc_cpu(struct perf_event *event, int cpu)
{ struct paiext_mapptr *mp; struct paiext_map *cpump; int rc;
mutex_lock(&paiext_reserve_mutex);
rc = paiext_root_alloc(); if (rc) goto unlock;
/* Allocate memory for counter area and counter extraction. * These are * - a 512 byte block and requires 512 byte boundary alignment. * - a 1KB byte block and requires 1KB boundary alignment. * Only the first counting event has to allocate the area. * * Note: This works with commit 59bb47985c1d by default. * Backporting this to kernels without this commit might * need adjustment.
*/
mp->mapptr = cpump;
cpump->area = kzalloc(PAIE1_CTRBLOCK_SZ, GFP_KERNEL);
cpump->paiext_cb = kzalloc(PAIE1_CB_SZ, GFP_KERNEL);
cpump->save = kvmalloc_array(paiext_cnt + 1, sizeof(struct pai_userdata),
GFP_KERNEL); if (!cpump->save || !cpump->area || !cpump->paiext_cb) {
paiext_free(mp); goto undo;
}
INIT_LIST_HEAD(&cpump->syswide_list);
refcount_set(&cpump->refcnt, 1);
rc = 0;
} else {
refcount_inc(&cpump->refcnt);
}
undo: if (rc) { /* Error in allocation of event, decrement anchor. Since * the event in not created, its destroy() function is never * invoked. Adjust the reference counter for the anchor.
*/
paiext_root_free();
}
unlock:
mutex_unlock(&paiext_reserve_mutex); /* If rc is non-zero, no increment of counter/sampler was done. */ return rc;
}
/* * On error all cpumask are freed and all events have been destroyed. * Save of which CPUs data structures have been allocated for. * Release them in paicrypt_event_destroy call back function * for this event.
*/
PAI_CPU_MASK(event) = maskptr;
rc = 0;
out: return rc;
}
/* The PAI extension 1 control block supports up to 128 entries. Return * the index within PAIE1_CB given the event number. Also validate event * 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;
}
return 0;
}
static u64 paiext_getctr(unsignedlong *area, int nr)
{ return area[nr];
}
/* Read the counter values. Return value from location in buffer. For event * NNPA_ALL sum up all events.
*/ 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. * Saves only positive counter entries of the form * 2 bytes: Number of counter * 8 bytes: Value of counter
*/ 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: The function paiext_sched_task() and paiext_push_sample() are not * invoked after function paiext_del() has been called because of function * perf_sched_cb_dec(). * The function paiext_sched_task() and paiext_push_sample() are only * called when sampling is active. Function perf_sched_cb_inc() * has been invoked to install function paiext_sched_task() as call back * to run at context switch time (see paiext_add()). * * This causes function perf_event_context_sched_out() and * perf_event_context_sched_in() to check whether the PMU has installed an * sched_task() callback. That callback is not active after paiext_del() * returns and has deleted the event on that CPU.
*/ 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, * but for sampling only event NNPA_ALL is allowed.
*/ 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 * results on schedule_out and if page was dirty, save old values.
*/ if (!sched_in)
paiext_have_samples();
}
/* Attribute definitions for pai extension1 interface. As with other CPU * Measurement Facilities, there is one attribute per mapped counter. * The number of mapped counters may vary per machine generation. Use * the QUERY PROCESSOR ACTIVITY COUNTER INFORMATION (QPACI) instruction * to determine the number of mapped counters. The instructions returns * a positive number, which is the highest number of supported counters. * All counters less than this number are also supported, there are no * holes. A returned number of zero means no support for mapped counters. * * The identification of the counter is a unique number. The chosen range * is 0x1800 + offset in mapped kernel page. * All CPU Measurement Facility counters identifiers must be unique and * the numbers from 0 to 496 are already used for the CPU Measurement * Counter facility. Number 0x1000 to 0x103e are used for PAI cryptography * counters. * Numbers 0xb0000, 0xbc000 and 0xbd000 are already * used for the CPU Measurement Sampling facility.
*/
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.