Commit 3f9ff4c5 authored by Thomas Richter's avatar Thomas Richter Committed by Alexander Gordeev

s390/pai_ext: Enable per-task counting event

The PMU for PAI NNPA counters enforces the following restriction:

 - No per-task context for PAI NNPA counters.

This restriction is removed. One or more per-task/system-wide counting
events can now be active at the same time while one system wide
sampling event is active.
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 14e37684
...@@ -115,22 +115,36 @@ static void paiext_free(struct paiext_mapptr *mp) ...@@ -115,22 +115,36 @@ static void paiext_free(struct paiext_mapptr *mp)
} }
/* Release the PMU if event is the last perf event */ /* Release the PMU if event is the last perf event */
static void paiext_event_destroy(struct perf_event *event) static void paiext_event_destroy_cpu(struct perf_event *event, int cpu)
{ {
struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, event->cpu); struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, cpu);
struct paiext_map *cpump = mp->mapptr; struct paiext_map *cpump = mp->mapptr;
mutex_lock(&paiext_reserve_mutex); mutex_lock(&paiext_reserve_mutex);
if (event->attr.sample_period) if (event->attr.sample_period)
cpump->mode &= ~PAI_MODE_SAMPLING; cpump->mode &= ~PAI_MODE_SAMPLING;
free_page(PAI_SAVE_AREA(event));
if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */ if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */
paiext_free(mp); paiext_free(mp);
paiext_root_free(); paiext_root_free();
mutex_unlock(&paiext_reserve_mutex); mutex_unlock(&paiext_reserve_mutex);
debug_sprintf_event(paiext_dbg, 4, "%s cpu %d mapptr %p\n", __func__, }
event->cpu, mp->mapptr);
static void paiext_event_destroy(struct perf_event *event)
{
int cpu;
free_page(PAI_SAVE_AREA(event));
if (event->cpu == -1) {
struct cpumask *mask = PAI_CPU_MASK(event);
for_each_cpu(cpu, mask)
paiext_event_destroy_cpu(event, cpu);
kfree(mask);
} else {
paiext_event_destroy_cpu(event, event->cpu);
}
debug_sprintf_event(paiext_dbg, 4, "%s cpu %d\n", __func__,
event->cpu);
} }
/* Used to avoid races in checking concurrent access of counting and /* Used to avoid races in checking concurrent access of counting and
...@@ -147,19 +161,18 @@ static void paiext_event_destroy(struct perf_event *event) ...@@ -147,19 +161,18 @@ static void paiext_event_destroy(struct perf_event *event)
* *
* Allocate the memory for the event. * Allocate the memory for the event.
*/ */
static int paiext_alloc(struct perf_event_attr *a, struct perf_event *event) static int paiext_alloc_cpu(struct perf_event_attr *a, int cpu)
{ {
struct paiext_mapptr *mp; struct paiext_mapptr *mp;
struct paiext_map *cpump; struct paiext_map *cpump;
int rc; int rc;
mutex_lock(&paiext_reserve_mutex); mutex_lock(&paiext_reserve_mutex);
rc = paiext_root_alloc(); rc = paiext_root_alloc();
if (rc) if (rc)
goto unlock; goto unlock;
mp = per_cpu_ptr(paiext_root.mapptr, event->cpu); mp = per_cpu_ptr(paiext_root.mapptr, cpu);
cpump = mp->mapptr; cpump = mp->mapptr;
if (!cpump) { /* Paiext_map allocated? */ if (!cpump) { /* Paiext_map allocated? */
rc = -ENOMEM; rc = -ENOMEM;
...@@ -217,6 +230,40 @@ static int paiext_alloc(struct perf_event_attr *a, struct perf_event *event) ...@@ -217,6 +230,40 @@ static int paiext_alloc(struct perf_event_attr *a, struct perf_event *event)
return rc; return rc;
} }
static int paiext_alloc(struct perf_event *event)
{
struct cpumask *maskptr;
int cpu, rc = -ENOMEM;
maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
if (!maskptr)
goto out;
for_each_online_cpu(cpu) {
rc = paiext_alloc_cpu(&event->attr, cpu);
if (rc) {
for_each_cpu(cpu, maskptr)
paiext_event_destroy_cpu(event, cpu);
kfree(maskptr);
goto out;
}
cpumask_set_cpu(cpu, maskptr);
}
/*
* 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:
debug_sprintf_event(paiext_dbg, 5, "%s cpu %u rc %d\n", __func__,
cpu, rc);
return rc;
}
/* The PAI extension 1 control block supports up to 128 entries. Return /* The PAI extension 1 control block supports up to 128 entries. Return
* the index within PAIE1_CB given the event number. Also validate event * the index within PAIE1_CB given the event number. Also validate event
* number. * number.
...@@ -246,8 +293,9 @@ static int paiext_event_init(struct perf_event *event) ...@@ -246,8 +293,9 @@ static int paiext_event_init(struct perf_event *event)
rc = paiext_event_valid(event); rc = paiext_event_valid(event);
if (rc) if (rc)
return rc; return rc;
/* Allow only CPU wide operation, no process context for now. */ /* Allow only CPU wide operation for sampling */
if ((event->attach_state & PERF_ATTACH_TASK) || event->cpu == -1) if (a->sample_period &&
((event->attach_state & PERF_ATTACH_TASK) || event->cpu == -1))
return -ENOENT; return -ENOENT;
/* Allow only event NNPA_ALL for sampling. */ /* Allow only event NNPA_ALL for sampling. */
if (a->sample_period && a->config != PAI_NNPA_BASE) if (a->sample_period && a->config != PAI_NNPA_BASE)
...@@ -262,7 +310,10 @@ static int paiext_event_init(struct perf_event *event) ...@@ -262,7 +310,10 @@ static int paiext_event_init(struct perf_event *event)
return -ENOMEM; return -ENOMEM;
} }
rc = paiext_alloc(a, event); if (event->cpu >= 0)
rc = paiext_alloc_cpu(a, event->cpu);
else
rc = paiext_alloc(event);
if (rc) { if (rc) {
free_page(PAI_SAVE_AREA(event)); free_page(PAI_SAVE_AREA(event));
return rc; return rc;
...@@ -352,8 +403,6 @@ static int paiext_add(struct perf_event *event, int flags) ...@@ -352,8 +403,6 @@ static int paiext_add(struct perf_event *event, int flags)
pcb->acc = virt_to_phys(cpump->area) | 0x1; pcb->acc = virt_to_phys(cpump->area) | 0x1;
/* Enable CPU instruction lookup for PAIE1 control block */ /* Enable CPU instruction lookup for PAIE1 control block */
local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT); local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT);
debug_sprintf_event(paiext_dbg, 4, "%s 1508 %llx acc %llx\n",
__func__, S390_lowcore.aicd, pcb->acc);
} }
if (flags & PERF_EF_START) if (flags & PERF_EF_START)
paiext_start(event, PERF_EF_RELOAD); paiext_start(event, PERF_EF_RELOAD);
...@@ -387,8 +436,6 @@ static void paiext_del(struct perf_event *event, int flags) ...@@ -387,8 +436,6 @@ static void paiext_del(struct perf_event *event, int flags)
local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT); local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT);
pcb->acc = 0; pcb->acc = 0;
S390_lowcore.aicd = 0; S390_lowcore.aicd = 0;
debug_sprintf_event(paiext_dbg, 4, "%s 1508 %llx acc %llx\n",
__func__, S390_lowcore.aicd, pcb->acc);
} }
} }
...@@ -544,7 +591,7 @@ static const struct attribute_group *paiext_attr_groups[] = { ...@@ -544,7 +591,7 @@ static const struct attribute_group *paiext_attr_groups[] = {
/* Performance monitoring unit for mapped counters */ /* Performance monitoring unit for mapped counters */
static struct pmu paiext = { static struct pmu paiext = {
.task_ctx_nr = perf_invalid_context, .task_ctx_nr = perf_hw_context,
.event_init = paiext_event_init, .event_init = paiext_event_init,
.add = paiext_add, .add = paiext_add,
.del = paiext_del, .del = paiext_del,
......
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