Commit 56033c44 authored by Paul Mackerras's avatar Paul Mackerras Committed by Greg Kroah-Hartman

powerpc/perf_counters: Reduce stack usage of power_check_constraints

commit e51ee31e upstream.

Michael Ellerman reported stack-frame size warnings being produced
for power_check_constraints(), which uses an 8*8 array of u64 and
two 8*8 arrays of unsigned long, which are currently allocated on the
stack, along with some other smaller variables.  These arrays come
to 1.5kB on 64-bit or 1kB on 32-bit, which is a bit too much for the
stack.

This fixes the problem by putting these arrays in the existing
per-cpu cpu_hw_counters struct.  This is OK because two of the call
sites have interrupts disabled already; for the third call site we
use get_cpu_var, which disables preemption, so we know we won't
get a context switch while we're in power_check_constraints().
Note that power_check_constraints() can be called during context
switch but is not called from interrupts.
Reported-by: default avatarMichael Ellerman <michael@ellerman.id.au>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 9db85a34
...@@ -32,6 +32,9 @@ struct cpu_hw_counters { ...@@ -32,6 +32,9 @@ struct cpu_hw_counters {
unsigned long mmcr[3]; unsigned long mmcr[3];
struct perf_counter *limited_counter[MAX_LIMITED_HWCOUNTERS]; struct perf_counter *limited_counter[MAX_LIMITED_HWCOUNTERS];
u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS]; u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS];
u64 alternatives[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
unsigned long amasks[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
unsigned long avalues[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
}; };
DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters); DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters);
...@@ -245,13 +248,11 @@ static void write_pmc(int idx, unsigned long val) ...@@ -245,13 +248,11 @@ static void write_pmc(int idx, unsigned long val)
* and see if any combination of alternative codes is feasible. * and see if any combination of alternative codes is feasible.
* The feasible set is returned in event[]. * The feasible set is returned in event[].
*/ */
static int power_check_constraints(u64 event[], unsigned int cflags[], static int power_check_constraints(struct cpu_hw_counters *cpuhw,
u64 event[], unsigned int cflags[],
int n_ev) int n_ev)
{ {
unsigned long mask, value, nv; unsigned long mask, value, nv;
u64 alternatives[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
unsigned long amasks[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
unsigned long avalues[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
unsigned long smasks[MAX_HWCOUNTERS], svalues[MAX_HWCOUNTERS]; unsigned long smasks[MAX_HWCOUNTERS], svalues[MAX_HWCOUNTERS];
int n_alt[MAX_HWCOUNTERS], choice[MAX_HWCOUNTERS]; int n_alt[MAX_HWCOUNTERS], choice[MAX_HWCOUNTERS];
int i, j; int i, j;
...@@ -266,21 +267,23 @@ static int power_check_constraints(u64 event[], unsigned int cflags[], ...@@ -266,21 +267,23 @@ static int power_check_constraints(u64 event[], unsigned int cflags[],
if ((cflags[i] & PPMU_LIMITED_PMC_REQD) if ((cflags[i] & PPMU_LIMITED_PMC_REQD)
&& !ppmu->limited_pmc_event(event[i])) { && !ppmu->limited_pmc_event(event[i])) {
ppmu->get_alternatives(event[i], cflags[i], ppmu->get_alternatives(event[i], cflags[i],
alternatives[i]); cpuhw->alternatives[i]);
event[i] = alternatives[i][0]; event[i] = cpuhw->alternatives[i][0];
} }
if (ppmu->get_constraint(event[i], &amasks[i][0], if (ppmu->get_constraint(event[i], &cpuhw->amasks[i][0],
&avalues[i][0])) &cpuhw->avalues[i][0]))
return -1; return -1;
} }
value = mask = 0; value = mask = 0;
for (i = 0; i < n_ev; ++i) { for (i = 0; i < n_ev; ++i) {
nv = (value | avalues[i][0]) + (value & avalues[i][0] & addf); nv = (value | cpuhw->avalues[i][0]) +
(value & cpuhw->avalues[i][0] & addf);
if ((((nv + tadd) ^ value) & mask) != 0 || if ((((nv + tadd) ^ value) & mask) != 0 ||
(((nv + tadd) ^ avalues[i][0]) & amasks[i][0]) != 0) (((nv + tadd) ^ cpuhw->avalues[i][0]) &
cpuhw->amasks[i][0]) != 0)
break; break;
value = nv; value = nv;
mask |= amasks[i][0]; mask |= cpuhw->amasks[i][0];
} }
if (i == n_ev) if (i == n_ev)
return 0; /* all OK */ return 0; /* all OK */
...@@ -291,10 +294,11 @@ static int power_check_constraints(u64 event[], unsigned int cflags[], ...@@ -291,10 +294,11 @@ static int power_check_constraints(u64 event[], unsigned int cflags[],
for (i = 0; i < n_ev; ++i) { for (i = 0; i < n_ev; ++i) {
choice[i] = 0; choice[i] = 0;
n_alt[i] = ppmu->get_alternatives(event[i], cflags[i], n_alt[i] = ppmu->get_alternatives(event[i], cflags[i],
alternatives[i]); cpuhw->alternatives[i]);
for (j = 1; j < n_alt[i]; ++j) for (j = 1; j < n_alt[i]; ++j)
ppmu->get_constraint(alternatives[i][j], ppmu->get_constraint(cpuhw->alternatives[i][j],
&amasks[i][j], &avalues[i][j]); &cpuhw->amasks[i][j],
&cpuhw->avalues[i][j]);
} }
/* enumerate all possibilities and see if any will work */ /* enumerate all possibilities and see if any will work */
...@@ -313,11 +317,11 @@ static int power_check_constraints(u64 event[], unsigned int cflags[], ...@@ -313,11 +317,11 @@ static int power_check_constraints(u64 event[], unsigned int cflags[],
* where k > j, will satisfy the constraints. * where k > j, will satisfy the constraints.
*/ */
while (++j < n_alt[i]) { while (++j < n_alt[i]) {
nv = (value | avalues[i][j]) + nv = (value | cpuhw->avalues[i][j]) +
(value & avalues[i][j] & addf); (value & cpuhw->avalues[i][j] & addf);
if ((((nv + tadd) ^ value) & mask) == 0 && if ((((nv + tadd) ^ value) & mask) == 0 &&
(((nv + tadd) ^ avalues[i][j]) (((nv + tadd) ^ cpuhw->avalues[i][j])
& amasks[i][j]) == 0) & cpuhw->amasks[i][j]) == 0)
break; break;
} }
if (j >= n_alt[i]) { if (j >= n_alt[i]) {
...@@ -339,7 +343,7 @@ static int power_check_constraints(u64 event[], unsigned int cflags[], ...@@ -339,7 +343,7 @@ static int power_check_constraints(u64 event[], unsigned int cflags[],
svalues[i] = value; svalues[i] = value;
smasks[i] = mask; smasks[i] = mask;
value = nv; value = nv;
mask |= amasks[i][j]; mask |= cpuhw->amasks[i][j];
++i; ++i;
j = -1; j = -1;
} }
...@@ -347,7 +351,7 @@ static int power_check_constraints(u64 event[], unsigned int cflags[], ...@@ -347,7 +351,7 @@ static int power_check_constraints(u64 event[], unsigned int cflags[],
/* OK, we have a feasible combination, tell the caller the solution */ /* OK, we have a feasible combination, tell the caller the solution */
for (i = 0; i < n_ev; ++i) for (i = 0; i < n_ev; ++i)
event[i] = alternatives[i][choice[i]]; event[i] = cpuhw->alternatives[i][choice[i]];
return 0; return 0;
} }
...@@ -752,7 +756,7 @@ int hw_perf_group_sched_in(struct perf_counter *group_leader, ...@@ -752,7 +756,7 @@ int hw_perf_group_sched_in(struct perf_counter *group_leader,
return -EAGAIN; return -EAGAIN;
if (check_excludes(cpuhw->counter, cpuhw->flags, n0, n)) if (check_excludes(cpuhw->counter, cpuhw->flags, n0, n))
return -EAGAIN; return -EAGAIN;
i = power_check_constraints(cpuhw->events, cpuhw->flags, n + n0); i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n + n0);
if (i < 0) if (i < 0)
return -EAGAIN; return -EAGAIN;
cpuhw->n_counters = n0 + n; cpuhw->n_counters = n0 + n;
...@@ -807,7 +811,7 @@ static int power_pmu_enable(struct perf_counter *counter) ...@@ -807,7 +811,7 @@ static int power_pmu_enable(struct perf_counter *counter)
cpuhw->flags[n0] = counter->hw.counter_base; cpuhw->flags[n0] = counter->hw.counter_base;
if (check_excludes(cpuhw->counter, cpuhw->flags, n0, 1)) if (check_excludes(cpuhw->counter, cpuhw->flags, n0, 1))
goto out; goto out;
if (power_check_constraints(cpuhw->events, cpuhw->flags, n0 + 1)) if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1))
goto out; goto out;
counter->hw.config = cpuhw->events[n0]; counter->hw.config = cpuhw->events[n0];
...@@ -1012,6 +1016,7 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter) ...@@ -1012,6 +1016,7 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
unsigned int cflags[MAX_HWCOUNTERS]; unsigned int cflags[MAX_HWCOUNTERS];
int n; int n;
int err; int err;
struct cpu_hw_counters *cpuhw;
if (!ppmu) if (!ppmu)
return ERR_PTR(-ENXIO); return ERR_PTR(-ENXIO);
...@@ -1090,7 +1095,11 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter) ...@@ -1090,7 +1095,11 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
cflags[n] = flags; cflags[n] = flags;
if (check_excludes(ctrs, cflags, n, 1)) if (check_excludes(ctrs, cflags, n, 1))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (power_check_constraints(events, cflags, n + 1))
cpuhw = &get_cpu_var(cpu_hw_counters);
err = power_check_constraints(cpuhw, events, cflags, n + 1);
put_cpu_var(cpu_hw_counters);
if (err)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
counter->hw.config = events[n]; counter->hw.config = events[n];
......
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