Commit 9f66572f authored by Thomas Richter's avatar Thomas Richter Committed by Alexander Gordeev

s390/pai_crypto: Enable per-task and system-wide sampling event

The PMU for PAI crypto counters enforces the following restrictions:

      - No per-task context for PAI crypto sampling event CRYPTO_ALL
      - No multiple system-wide PAI crypto sampling event CRYPTO_ALL

Both restrictions are removed. One or more per-task sampling events
are supported. Also one or more system-wide sampling events are
supported.

Example for per-task context of sampling event CRYPTO_ALL:
       # perf record -e pai_crypto/CRYPTO_ALL/ -- true
Example for system-wide context of sampling event CRYPTO_ALL:
       # perf record -e pai_crypto/CRYPTO_ALL/ -a -- sleep 4
Acked-by: default avatarSumanth Korikkar <sumanthk@linux.ibm.com>
Signed-off-by: default avatarThomas Richter <tmricht@linux.ibm.com>
Signed-off-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
parent 92ea6868
...@@ -24,6 +24,8 @@ static debug_info_t *cfm_dbg; ...@@ -24,6 +24,8 @@ static debug_info_t *cfm_dbg;
static unsigned int paicrypt_cnt; /* Size of the mapped counter sets */ static unsigned int paicrypt_cnt; /* Size of the mapped counter sets */
/* extracted with QPACI instruction */ /* extracted with QPACI instruction */
#define PAI_SWLIST(x) (&(x)->hw.tp_list)
DEFINE_STATIC_KEY_FALSE(pai_key); DEFINE_STATIC_KEY_FALSE(pai_key);
struct pai_userdata { struct pai_userdata {
...@@ -36,8 +38,8 @@ struct paicrypt_map { ...@@ -36,8 +38,8 @@ struct paicrypt_map {
struct pai_userdata *save; /* Page to store no-zero counters */ struct pai_userdata *save; /* Page to store no-zero counters */
unsigned int active_events; /* # of PAI crypto users */ unsigned int active_events; /* # of PAI crypto users */
refcount_t refcnt; /* Reference count mapped buffers */ refcount_t refcnt; /* Reference count mapped buffers */
enum paievt_mode mode; /* Type of event */
struct perf_event *event; /* Perf event for sampling */ struct perf_event *event; /* Perf event for sampling */
struct list_head syswide_list; /* List system-wide sampling events */
}; };
struct paicrypt_mapptr { struct paicrypt_mapptr {
...@@ -90,13 +92,10 @@ static void paicrypt_event_destroy_cpu(struct perf_event *event, int cpu) ...@@ -90,13 +92,10 @@ static void paicrypt_event_destroy_cpu(struct perf_event *event, int cpu)
struct paicrypt_map *cpump = mp->mapptr; struct paicrypt_map *cpump = mp->mapptr;
mutex_lock(&pai_reserve_mutex); mutex_lock(&pai_reserve_mutex);
debug_sprintf_event(cfm_dbg, 5, "%s event %#llx cpu %d users %d" debug_sprintf_event(cfm_dbg, 5, "%s event %#llx cpu %d users %d "
" mode %d refcnt %u\n", __func__, "refcnt %u\n", __func__, event->attr.config,
event->attr.config, event->cpu, event->cpu, cpump->active_events,
cpump->active_events, cpump->mode,
refcount_read(&cpump->refcnt)); refcount_read(&cpump->refcnt));
if (event->attr.sample_period)
cpump->mode &= ~PAI_MODE_SAMPLING;
if (refcount_dec_and_test(&cpump->refcnt)) { if (refcount_dec_and_test(&cpump->refcnt)) {
debug_sprintf_event(cfm_dbg, 4, "%s page %#lx save %p\n", debug_sprintf_event(cfm_dbg, 4, "%s page %#lx save %p\n",
__func__, (unsigned long)cpump->page, __func__, (unsigned long)cpump->page,
...@@ -172,12 +171,7 @@ static u64 paicrypt_getall(struct perf_event *event) ...@@ -172,12 +171,7 @@ static u64 paicrypt_getall(struct perf_event *event)
return sum; return sum;
} }
/* Used to avoid races in checking concurrent access of counting and /* Check concurrent access of counting and sampling for crypto events.
* sampling for crypto events
*
* Only one instance of event pai_crypto/CRYPTO_ALL/ for sampling is
* allowed. Several counting events are allowed in parallel.
*
* This function is called in process context and it is save to block. * This function is called in process context and it is save to block.
* When the event initialization functions fails, no other call back will * When the event initialization functions fails, no other call back will
* be invoked. * be invoked.
...@@ -186,7 +180,6 @@ static u64 paicrypt_getall(struct perf_event *event) ...@@ -186,7 +180,6 @@ static u64 paicrypt_getall(struct perf_event *event)
*/ */
static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu) static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu)
{ {
struct perf_event_attr *a = &event->attr;
struct paicrypt_map *cpump = NULL; struct paicrypt_map *cpump = NULL;
struct paicrypt_mapptr *mp; struct paicrypt_mapptr *mp;
int rc; int rc;
...@@ -207,25 +200,9 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu) ...@@ -207,25 +200,9 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu)
rc = -ENOMEM; rc = -ENOMEM;
goto free_root; goto free_root;
} }
INIT_LIST_HEAD(&cpump->syswide_list);
} }
if (a->sample_period) { /* Sampling requested */
if (cpump->mode & PAI_MODE_SAMPLING)
rc = -EBUSY; /* ... and sampling active */
else
cpump->mode |= PAI_MODE_SAMPLING;
}
/*
* This error case triggers when there is a conflict:
* Either sampling requested and counting already active, or visa
* versa. Therefore the struct paicrypto_map for this CPU is
* needed or the error could not have occurred. Only adjust root
* node refcount.
*/
if (rc)
goto free_root;
/* Allocate memory for counter page and counter extraction. /* Allocate memory for counter page and counter extraction.
* Only the first counting event has to allocate a page. * Only the first counting event has to allocate a page.
*/ */
...@@ -250,14 +227,14 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu) ...@@ -250,14 +227,14 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu)
rc = 0; rc = 0;
refcount_set(&cpump->refcnt, 1); refcount_set(&cpump->refcnt, 1);
mp->mapptr = cpump; mp->mapptr = cpump;
debug_sprintf_event(cfm_dbg, 5, "%s sample_period %#llx users %d" debug_sprintf_event(cfm_dbg, 5, "%s users %d refcnt %u page %#lx "
" mode %d refcnt %u page %#lx save %p rc %d\n", "save %p rc %d\n", __func__, cpump->active_events,
__func__, a->sample_period, cpump->active_events, refcount_read(&cpump->refcnt),
cpump->mode, refcount_read(&cpump->refcnt),
(unsigned long)cpump->page, cpump->save, rc); (unsigned long)cpump->page, cpump->save, rc);
goto unlock; goto unlock;
free_paicrypt_map: free_paicrypt_map:
/* Undo memory allocation */
kfree(cpump); kfree(cpump);
mp->mapptr = NULL; mp->mapptr = NULL;
free_root: free_root:
...@@ -315,11 +292,7 @@ static int paicrypt_event_init(struct perf_event *event) ...@@ -315,11 +292,7 @@ static int paicrypt_event_init(struct perf_event *event)
if (a->config < PAI_CRYPTO_BASE || if (a->config < PAI_CRYPTO_BASE ||
a->config > PAI_CRYPTO_BASE + paicrypt_cnt) a->config > PAI_CRYPTO_BASE + paicrypt_cnt)
return -EINVAL; return -EINVAL;
/* Allow only CPU wide operation for sampling */ /* Allow only CRYPTO_ALL for sampling */
if (a->sample_period &&
((event->attach_state & PERF_ATTACH_TASK) || event->cpu == -1))
return -ENOENT;
/* Allow only CRYPTO_ALL for sampling. */
if (a->sample_period && a->config != PAI_CRYPTO_BASE) if (a->sample_period && a->config != PAI_CRYPTO_BASE)
return -EINVAL; return -EINVAL;
/* Get a page to store last counter values for sampling */ /* Get a page to store last counter values for sampling */
...@@ -382,9 +355,14 @@ static void paicrypt_start(struct perf_event *event, int flags) ...@@ -382,9 +355,14 @@ static void paicrypt_start(struct perf_event *event, int flags)
sum = paicrypt_getall(event); /* Get current value */ sum = paicrypt_getall(event); /* Get current value */
local64_set(&event->hw.prev_count, sum); local64_set(&event->hw.prev_count, sum);
} else { /* Sampling */ } else { /* Sampling */
cpump->event = event;
memcpy((void *)PAI_SAVE_AREA(event), cpump->page, PAGE_SIZE); memcpy((void *)PAI_SAVE_AREA(event), cpump->page, PAGE_SIZE);
perf_sched_cb_inc(event->pmu); /* Enable context switch callback for system-wide sampling */
if (!(event->attach_state & PERF_ATTACH_TASK)) {
list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
perf_sched_cb_inc(event->pmu);
} else {
cpump->event = event;
}
} }
} }
...@@ -405,6 +383,7 @@ static int paicrypt_add(struct perf_event *event, int flags) ...@@ -405,6 +383,7 @@ static int paicrypt_add(struct perf_event *event, int flags)
return 0; return 0;
} }
static void paicrypt_have_sample(struct perf_event *, struct paicrypt_map *);
static void paicrypt_stop(struct perf_event *event, int flags) static void paicrypt_stop(struct perf_event *event, int flags)
{ {
struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr); struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
...@@ -413,8 +392,13 @@ static void paicrypt_stop(struct perf_event *event, int flags) ...@@ -413,8 +392,13 @@ static void paicrypt_stop(struct perf_event *event, int flags)
if (!event->attr.sample_period) { /* Counting */ if (!event->attr.sample_period) { /* Counting */
paicrypt_read(event); paicrypt_read(event);
} else { /* Sampling */ } else { /* Sampling */
perf_sched_cb_dec(event->pmu); if (!(event->attach_state & PERF_ATTACH_TASK)) {
cpump->event = NULL; perf_sched_cb_dec(event->pmu);
list_del(PAI_SWLIST(event));
} else {
paicrypt_have_sample(event, cpump);
cpump->event = NULL;
}
} }
event->hw.state = PERF_HES_STOPPED; event->hw.state = PERF_HES_STOPPED;
} }
...@@ -507,23 +491,30 @@ static int paicrypt_push_sample(size_t rawsize, struct paicrypt_map *cpump, ...@@ -507,23 +491,30 @@ static int paicrypt_push_sample(size_t rawsize, struct paicrypt_map *cpump,
} }
/* Check if there is data to be saved on schedule out of a task. */ /* Check if there is data to be saved on schedule out of a task. */
static int paicrypt_have_sample(void) static void paicrypt_have_sample(struct perf_event *event,
struct paicrypt_map *cpump)
{ {
struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
struct paicrypt_map *cpump = mp->mapptr;
struct perf_event *event = cpump->event;
size_t rawsize; size_t rawsize;
int rc = 0;
if (!event) /* No event active */ if (!event) /* No event active */
return 0; return;
rawsize = paicrypt_copy(cpump->save, cpump->page, rawsize = paicrypt_copy(cpump->save, cpump->page,
(unsigned long *)PAI_SAVE_AREA(event), (unsigned long *)PAI_SAVE_AREA(event),
cpump->event->attr.exclude_user, event->attr.exclude_user,
cpump->event->attr.exclude_kernel); event->attr.exclude_kernel);
if (rawsize) /* No incremented counters */ if (rawsize) /* No incremented counters */
rc = paicrypt_push_sample(rawsize, cpump, event); paicrypt_push_sample(rawsize, cpump, event);
return rc; }
/* Check if there is data to be saved on schedule out of a task. */
static void paicrypt_have_samples(void)
{
struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
struct paicrypt_map *cpump = mp->mapptr;
struct perf_event *event;
list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
paicrypt_have_sample(event, cpump);
} }
/* Called on schedule-in and schedule-out. No access to event structure, /* Called on schedule-in and schedule-out. No access to event structure,
...@@ -535,7 +526,7 @@ static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sch ...@@ -535,7 +526,7 @@ static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sch
* results on schedule_out and if page was dirty, save old values. * results on schedule_out and if page was dirty, save old values.
*/ */
if (!sched_in) if (!sched_in)
paicrypt_have_sample(); paicrypt_have_samples();
} }
/* Attribute definitions for paicrypt interface. As with other CPU /* Attribute definitions for paicrypt interface. As with other CPU
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment