mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
s390/cpum_cf: introduce static CPU counter facility information
The CPU measurement facility counter information instruction qctri() retrieves information about the available counter sets. The information varies between machine generations, but is constant when running on a particular machine. For example the CPU measurement facility counter first and second version numbers determine the amount of counters in a counter set. This information never changes. The counter sets are identical for all CPUs in the system. It does not matter which CPU performs the instruction. Authorization control of the CPU Measurement facility can only be changed in the activation profile while the LPAR is not running. Retrieve the CPU measurement counter information at device driver initialization time and use its constant values. Function validate_ctr_version() verifies if a user provided CPU Measurement counter facility counter is valid and defined. It now uses the newly introduced static CPU counter facility information. To avoid repeated recalculation of the counter set sizes (numbers of counters per set), which never changes on a running machine, calculate the counter set size once at device driver initialization and store the result in an array. Functions cpum_cf_make_setsize() and cpum_cf_read_setsize() are introduced. Finally remove cpu_cf_events::info member and use the static CPU counter facility information instead. Signed-off-by: Thomas Richter <tmricht@linux.ibm.com> Acked-by: Heiko Carstens <hca@linux.ibm.com> Acked-by: Sumanth Korikkar <sumanthk@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
3b42877cd5
commit
46c4d945ea
1 changed files with 65 additions and 67 deletions
|
@ -76,7 +76,6 @@ static inline int ctr_stcctm(enum cpumf_ctr_set set, u64 range, u64 *dest)
|
|||
}
|
||||
|
||||
struct cpu_cf_events {
|
||||
struct cpumf_ctr_info info;
|
||||
atomic_t ctr_set[CPUMF_CTR_SET_MAX];
|
||||
u64 state; /* For perf_event_open SVC */
|
||||
u64 dev_state; /* For /dev/hwctr */
|
||||
|
@ -95,6 +94,15 @@ static DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events);
|
|||
static unsigned int cfdiag_cpu_speed; /* CPU speed for CF_DIAG trailer */
|
||||
static debug_info_t *cf_dbg;
|
||||
|
||||
/*
|
||||
* The CPU Measurement query counter information instruction contains
|
||||
* information which varies per machine generation, but is constant and
|
||||
* does not change when running on a particular machine, such as counter
|
||||
* first and second version number. This is needed to determine the size
|
||||
* of counter sets. Extract this information at device driver initialization.
|
||||
*/
|
||||
static struct cpumf_ctr_info cpumf_ctr_info;
|
||||
|
||||
#define CF_DIAG_CTRSET_DEF 0xfeef /* Counter set header mark */
|
||||
/* interval in seconds */
|
||||
|
||||
|
@ -167,11 +175,10 @@ struct cf_trailer_entry { /* CPU-M CF_DIAG trailer (64 byte) */
|
|||
/* Create the trailer data at the end of a page. */
|
||||
static void cfdiag_trailer(struct cf_trailer_entry *te)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
|
||||
struct cpuid cpuid;
|
||||
|
||||
te->cfvn = cpuhw->info.cfvn; /* Counter version numbers */
|
||||
te->csvn = cpuhw->info.csvn;
|
||||
te->cfvn = cpumf_ctr_info.cfvn; /* Counter version numbers */
|
||||
te->csvn = cpumf_ctr_info.csvn;
|
||||
|
||||
get_cpu_id(&cpuid); /* Machine type */
|
||||
te->mach_type = cpuid.machine;
|
||||
|
@ -184,50 +191,60 @@ static void cfdiag_trailer(struct cf_trailer_entry *te)
|
|||
}
|
||||
|
||||
/*
|
||||
* Return the maximum possible counter set size (in number of 8 byte counters)
|
||||
* depending on type and model number.
|
||||
* The number of counters per counter set varies between machine generations,
|
||||
* but is constant when running on a particular machine generation.
|
||||
* Determine each counter set size at device driver initialization and
|
||||
* retrieve it later.
|
||||
*/
|
||||
static size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset,
|
||||
struct cpumf_ctr_info *info)
|
||||
static size_t cpumf_ctr_setsizes[CPUMF_CTR_SET_MAX];
|
||||
static void cpum_cf_make_setsize(enum cpumf_ctr_set ctrset)
|
||||
{
|
||||
size_t ctrset_size = 0;
|
||||
|
||||
switch (ctrset) {
|
||||
case CPUMF_CTR_SET_BASIC:
|
||||
if (info->cfvn >= 1)
|
||||
if (cpumf_ctr_info.cfvn >= 1)
|
||||
ctrset_size = 6;
|
||||
break;
|
||||
case CPUMF_CTR_SET_USER:
|
||||
if (info->cfvn == 1)
|
||||
if (cpumf_ctr_info.cfvn == 1)
|
||||
ctrset_size = 6;
|
||||
else if (info->cfvn >= 3)
|
||||
else if (cpumf_ctr_info.cfvn >= 3)
|
||||
ctrset_size = 2;
|
||||
break;
|
||||
case CPUMF_CTR_SET_CRYPTO:
|
||||
if (info->csvn >= 1 && info->csvn <= 5)
|
||||
if (cpumf_ctr_info.csvn >= 1 && cpumf_ctr_info.csvn <= 5)
|
||||
ctrset_size = 16;
|
||||
else if (info->csvn == 6 || info->csvn == 7)
|
||||
else if (cpumf_ctr_info.csvn == 6 || cpumf_ctr_info.csvn == 7)
|
||||
ctrset_size = 20;
|
||||
break;
|
||||
case CPUMF_CTR_SET_EXT:
|
||||
if (info->csvn == 1)
|
||||
if (cpumf_ctr_info.csvn == 1)
|
||||
ctrset_size = 32;
|
||||
else if (info->csvn == 2)
|
||||
else if (cpumf_ctr_info.csvn == 2)
|
||||
ctrset_size = 48;
|
||||
else if (info->csvn >= 3 && info->csvn <= 5)
|
||||
else if (cpumf_ctr_info.csvn >= 3 && cpumf_ctr_info.csvn <= 5)
|
||||
ctrset_size = 128;
|
||||
else if (info->csvn == 6 || info->csvn == 7)
|
||||
else if (cpumf_ctr_info.csvn == 6 || cpumf_ctr_info.csvn == 7)
|
||||
ctrset_size = 160;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MT_DIAG:
|
||||
if (info->csvn > 3)
|
||||
if (cpumf_ctr_info.csvn > 3)
|
||||
ctrset_size = 48;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MAX:
|
||||
break;
|
||||
}
|
||||
cpumf_ctr_setsizes[ctrset] = ctrset_size;
|
||||
}
|
||||
|
||||
return ctrset_size;
|
||||
/*
|
||||
* Return the maximum possible counter set size (in number of 8 byte counters)
|
||||
* depending on type and model number.
|
||||
*/
|
||||
static size_t cpum_cf_read_setsize(enum cpumf_ctr_set ctrset)
|
||||
{
|
||||
return cpumf_ctr_setsizes[ctrset];
|
||||
}
|
||||
|
||||
/* Read a counter set. The counter set number determines the counter set and
|
||||
|
@ -248,14 +265,13 @@ static size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset,
|
|||
static size_t cfdiag_getctrset(struct cf_ctrset_entry *ctrdata, int ctrset,
|
||||
size_t room, bool error_ok)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw = this_cpu_ptr(&cpu_cf_events);
|
||||
size_t ctrset_size, need = 0;
|
||||
int rc = 3; /* Assume write failure */
|
||||
|
||||
ctrdata->def = CF_DIAG_CTRSET_DEF;
|
||||
ctrdata->set = ctrset;
|
||||
ctrdata->res1 = 0;
|
||||
ctrset_size = cpum_cf_ctrset_size(ctrset, &cpuhw->info);
|
||||
ctrset_size = cpum_cf_read_setsize(ctrset);
|
||||
|
||||
if (ctrset_size) { /* Save data */
|
||||
need = ctrset_size * sizeof(u64) + sizeof(*ctrdata);
|
||||
|
@ -269,10 +285,6 @@ static size_t cfdiag_getctrset(struct cf_ctrset_entry *ctrdata, int ctrset,
|
|||
need = 0;
|
||||
}
|
||||
|
||||
debug_sprintf_event(cf_dbg, 3,
|
||||
"%s ctrset %d ctrset_size %zu cfvn %d csvn %d"
|
||||
" need %zd rc %d\n", __func__, ctrset, ctrset_size,
|
||||
cpuhw->info.cfvn, cpuhw->info.csvn, need, rc);
|
||||
return need;
|
||||
}
|
||||
|
||||
|
@ -380,37 +392,34 @@ static enum cpumf_ctr_set get_counter_set(u64 event)
|
|||
static int validate_ctr_version(const struct hw_perf_event *hwc,
|
||||
enum cpumf_ctr_set set)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw;
|
||||
int err = 0;
|
||||
u16 mtdiag_ctl;
|
||||
|
||||
cpuhw = &get_cpu_var(cpu_cf_events);
|
||||
int err = 0;
|
||||
|
||||
/* check required version for counter sets */
|
||||
switch (set) {
|
||||
case CPUMF_CTR_SET_BASIC:
|
||||
case CPUMF_CTR_SET_USER:
|
||||
if (cpuhw->info.cfvn < 1)
|
||||
if (cpumf_ctr_info.cfvn < 1)
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_CRYPTO:
|
||||
if ((cpuhw->info.csvn >= 1 && cpuhw->info.csvn <= 5 &&
|
||||
if ((cpumf_ctr_info.csvn >= 1 && cpumf_ctr_info.csvn <= 5 &&
|
||||
hwc->config > 79) ||
|
||||
(cpuhw->info.csvn >= 6 && hwc->config > 83))
|
||||
(cpumf_ctr_info.csvn >= 6 && hwc->config > 83))
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_EXT:
|
||||
if (cpuhw->info.csvn < 1)
|
||||
if (cpumf_ctr_info.csvn < 1)
|
||||
err = -EOPNOTSUPP;
|
||||
if ((cpuhw->info.csvn == 1 && hwc->config > 159) ||
|
||||
(cpuhw->info.csvn == 2 && hwc->config > 175) ||
|
||||
(cpuhw->info.csvn >= 3 && cpuhw->info.csvn <= 5
|
||||
if ((cpumf_ctr_info.csvn == 1 && hwc->config > 159) ||
|
||||
(cpumf_ctr_info.csvn == 2 && hwc->config > 175) ||
|
||||
(cpumf_ctr_info.csvn >= 3 && cpumf_ctr_info.csvn <= 5
|
||||
&& hwc->config > 255) ||
|
||||
(cpuhw->info.csvn >= 6 && hwc->config > 287))
|
||||
(cpumf_ctr_info.csvn >= 6 && hwc->config > 287))
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MT_DIAG:
|
||||
if (cpuhw->info.csvn <= 3)
|
||||
if (cpumf_ctr_info.csvn <= 3)
|
||||
err = -EOPNOTSUPP;
|
||||
/*
|
||||
* MT-diagnostic counters are read-only. The counter set
|
||||
|
@ -425,35 +434,30 @@ static int validate_ctr_version(const struct hw_perf_event *hwc,
|
|||
* counter set is enabled and active.
|
||||
*/
|
||||
mtdiag_ctl = cpumf_ctr_ctl[CPUMF_CTR_SET_MT_DIAG];
|
||||
if (!((cpuhw->info.auth_ctl & mtdiag_ctl) &&
|
||||
(cpuhw->info.enable_ctl & mtdiag_ctl) &&
|
||||
(cpuhw->info.act_ctl & mtdiag_ctl)))
|
||||
if (!((cpumf_ctr_info.auth_ctl & mtdiag_ctl) &&
|
||||
(cpumf_ctr_info.enable_ctl & mtdiag_ctl) &&
|
||||
(cpumf_ctr_info.act_ctl & mtdiag_ctl)))
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
case CPUMF_CTR_SET_MAX:
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
put_cpu_var(cpu_cf_events);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int validate_ctr_auth(const struct hw_perf_event *hwc)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw;
|
||||
int err = 0;
|
||||
|
||||
cpuhw = &get_cpu_var(cpu_cf_events);
|
||||
int err = -ENOENT;
|
||||
|
||||
/* Check authorization for cpu counter sets.
|
||||
* If the particular CPU counter set is not authorized,
|
||||
* return with -ENOENT in order to fall back to other
|
||||
* PMUs that might suffice the event request.
|
||||
*/
|
||||
if (!(hwc->config_base & cpuhw->info.auth_ctl))
|
||||
err = -ENOENT;
|
||||
if ((hwc->config_base & cpumf_ctr_info.auth_ctl))
|
||||
err = 0;
|
||||
|
||||
put_cpu_var(cpu_cf_events);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -509,8 +513,6 @@ static void cpum_cf_setup_cpu(void *flags)
|
|||
|
||||
switch ((unsigned long)flags) {
|
||||
case PMC_INIT:
|
||||
memset(&cpuhw->info, 0, sizeof(cpuhw->info));
|
||||
qctri(&cpuhw->info);
|
||||
cpuhw->flags |= PMU_F_RESERVED;
|
||||
break;
|
||||
|
||||
|
@ -977,7 +979,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
|
|||
|
||||
/* counter authorization change alert */
|
||||
if (alert & CPU_MF_INT_CF_CACA)
|
||||
qctri(&cpuhw->info);
|
||||
qctri(&cpumf_ctr_info);
|
||||
|
||||
/* loss of counter data alert */
|
||||
if (alert & CPU_MF_INT_CF_LCDA)
|
||||
|
@ -994,9 +996,14 @@ static int __init cpumf_pmu_init(void)
|
|||
{
|
||||
int rc;
|
||||
|
||||
if (!cpum_cf_avail())
|
||||
/* Extract counter measurement facility information */
|
||||
if (!cpum_cf_avail() || qctri(&cpumf_ctr_info))
|
||||
return -ENODEV;
|
||||
|
||||
/* Determine and store counter set sizes for later reference */
|
||||
for (rc = CPUMF_CTR_SET_BASIC; rc < CPUMF_CTR_SET_MAX; ++rc)
|
||||
cpum_cf_make_setsize(rc);
|
||||
|
||||
/*
|
||||
* Clear bit 15 of cr0 to unauthorize problem-state to
|
||||
* extract measurement counters
|
||||
|
@ -1263,21 +1270,19 @@ static int cfset_all_start(struct cfset_request *req)
|
|||
*/
|
||||
static size_t cfset_needspace(unsigned int sets)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw = get_cpu_ptr(&cpu_cf_events);
|
||||
size_t bytes = 0;
|
||||
int i;
|
||||
|
||||
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
|
||||
if (!(sets & cpumf_ctr_ctl[i]))
|
||||
continue;
|
||||
bytes += cpum_cf_ctrset_size(i, &cpuhw->info) * sizeof(u64) +
|
||||
bytes += cpum_cf_read_setsize(i) * sizeof(u64) +
|
||||
sizeof(((struct s390_ctrset_setdata *)0)->set) +
|
||||
sizeof(((struct s390_ctrset_setdata *)0)->no_cnts);
|
||||
}
|
||||
bytes = sizeof(((struct s390_ctrset_read *)0)->no_cpus) + nr_cpu_ids *
|
||||
(bytes + sizeof(((struct s390_ctrset_cpudata *)0)->cpu_nr) +
|
||||
sizeof(((struct s390_ctrset_cpudata *)0)->no_sets));
|
||||
put_cpu_ptr(&cpu_cf_events);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
@ -1351,7 +1356,7 @@ static void cfset_cpu_read(void *parm)
|
|||
|
||||
if (!(p->sets & cpumf_ctr_ctl[set]))
|
||||
continue; /* Counter set not in list */
|
||||
set_size = cpum_cf_ctrset_size(set, &cpuhw->info);
|
||||
set_size = cpum_cf_read_setsize(set);
|
||||
space = sizeof(cpuhw->data) - cpuhw->used;
|
||||
space = cfset_cpuset_read(sp, set, set_size, space);
|
||||
if (space) {
|
||||
|
@ -1562,16 +1567,13 @@ static void cfdiag_read(struct perf_event *event)
|
|||
|
||||
static int get_authctrsets(void)
|
||||
{
|
||||
struct cpu_cf_events *cpuhw;
|
||||
unsigned long auth = 0;
|
||||
enum cpumf_ctr_set i;
|
||||
|
||||
cpuhw = &get_cpu_var(cpu_cf_events);
|
||||
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
|
||||
if (cpuhw->info.auth_ctl & cpumf_ctr_ctl[i])
|
||||
if (cpumf_ctr_info.auth_ctl & cpumf_ctr_ctl[i])
|
||||
auth |= cpumf_ctr_ctl[i];
|
||||
}
|
||||
put_cpu_var(cpu_cf_events);
|
||||
return auth;
|
||||
}
|
||||
|
||||
|
@ -1709,7 +1711,7 @@ static size_t cfdiag_maxsize(struct cpumf_ctr_info *info)
|
|||
enum cpumf_ctr_set i;
|
||||
|
||||
for (i = CPUMF_CTR_SET_BASIC; i < CPUMF_CTR_SET_MAX; ++i) {
|
||||
size_t size = cpum_cf_ctrset_size(i, info);
|
||||
size_t size = cpum_cf_read_setsize(i);
|
||||
|
||||
if (size)
|
||||
max_size += size * sizeof(u64) +
|
||||
|
@ -1743,16 +1745,12 @@ static void cfdiag_get_cpu_speed(void)
|
|||
|
||||
static int cfset_init(void)
|
||||
{
|
||||
struct cpumf_ctr_info info;
|
||||
size_t need;
|
||||
int rc;
|
||||
|
||||
if (qctri(&info))
|
||||
return -ENODEV;
|
||||
|
||||
cfdiag_get_cpu_speed();
|
||||
/* Make sure the counter set data fits into predefined buffer. */
|
||||
need = cfdiag_maxsize(&info);
|
||||
need = cfdiag_maxsize(&cpumf_ctr_info);
|
||||
if (need > sizeof(((struct cpu_cf_events *)0)->start)) {
|
||||
pr_err("Insufficient memory for PMU(cpum_cf_diag) need=%zu\n",
|
||||
need);
|
||||
|
|
Loading…
Reference in a new issue