diff --git a/cpacfstats/cpacfstats.1 b/cpacfstats/cpacfstats.1 index b4f458fd..1a99b259 100644 --- a/cpacfstats/cpacfstats.1 +++ b/cpacfstats/cpacfstats.1 @@ -1,6 +1,6 @@ .\" cpacfstats.1 .\" -.\" Copyright IBM Corp. 2015, 2020 +.\" Copyright IBM Corp. 2015, 2022 .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" @@ -32,7 +32,9 @@ cpacfstats \- enable, disable and display CPACF statistical data .RB ] .RB [ \-p | \-\-print .I counter +.RB [ \-n | \-\-nonzero] .RB ] +.RB [ \-j | \-\-json ] . .SH DESCRIPTION The cpacfstats client application interacts with the cpacfstatsd daemon and @@ -44,12 +46,344 @@ All counters are initially disabled and must be switched on to measure CPACF activities of the system. There is a slight performance penalty with CPACF counters enabled. -Note: All cryptographic counters are bound to physical CPUs. If you use -dedicated CPUs for your LPAR, you obtain only your CPACF activities. If -your CPUs are shared with other LPARs, their cryptographic activities might -distort the results. CPACF performance counters are available on LPARs -only. For security reasons only members of the group \fIcpacfstats\fR are -allowed to run the cpacfstats client application. +CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF +counters are only available on LPARs and have to be authorized. If +they are available, the counters +.B des, +.B aes, +.B sha, +.B rng, +and +.B ecc +are made available. These counters can individually be activated, +reset, printed, or deactivated. PAI counters are a lot more detailed. +The user interface only offers the counters +.B pai_user +and +.B pai_kernel +to count CPACF usage in user-space or kernel-space. When printing +these counters, detailed counters are shown. The detailed names are: +.RS +.IP \(bu +KM DES, +.IP \(bu +KM 2key TDES, +.IP \(bu +KM TDES, +.IP \(bu +KM DES protected key, +.IP \(bu +KM 2key TDES protected key, +.IP \(bu +KM TDES protected key, +.IP \(bu +KM AES 128bit, +.IP \(bu +KM AES 192bit, +.IP \(bu +KM AES 256bit, +.IP \(bu +KM AES 128bit protected key, +.IP \(bu +KM AES 192bit protected key, +.IP \(bu +KM AES 256bit protected key, +.IP \(bu +KM AES-XTS 128bit, +.IP \(bu +KM AES-XTS 256bit, +.IP \(bu +KM AES-XTS 128bit protected key, +.IP \(bu +KM AES-XTS 256bit protected key, +.IP \(bu +KMC DES, +.IP \(bu +KMC 2key TDES, +.IP \(bu +KMC TDES, +.IP \(bu +KMC DES protected key, +.IP \(bu +KMC 2key TDES protected key, +.IP \(bu +KMC TDES protected key, +.IP \(bu +KMC AES 128bit, +.IP \(bu +KMC AES 192bit, +.IP \(bu +KMC AES 256bit, +.IP \(bu +KMC AES 128bit protected key, +.IP \(bu +KMC AES 192bit protected key, +.IP \(bu +KMC AES 256bit protected key, +.IP \(bu +KMC PRNG, +.IP \(bu +KMA AES 128bit, +.IP \(bu +KMA AES 192bit, +.IP \(bu +KMA AES 256bit, +.IP \(bu +KMA AES 128bit protected key, +.IP \(bu +KMA AES 192bit protected key, +.IP \(bu +KMA AES 256bit protected key, +.IP \(bu +KMF DES, +.IP \(bu +KMF 2key TDES, +.IP \(bu +KMF TDES, +.IP \(bu +KMF DES protected key, +.IP \(bu +KMF 2key TDES protected key, +.IP \(bu +KMF TDES protected key, +.IP \(bu +KMF AES 128bit, +.IP \(bu +KMF AES 192bit, +.IP \(bu +KMF AES 256bit, +.IP \(bu +KMF AES 128bit protected key, +.IP \(bu +KMF AES 192bit protected key, +.IP \(bu +KMF AES 256bit protected key, +.IP \(bu +KMCTR DES, +.IP \(bu +KMCTR 2key TDES, +.IP \(bu +KMCTR TDES, +.IP \(bu +KMCTR DES protected key, +.IP \(bu +KMCTR 2key TDES protected key, +.IP \(bu +KMCTR TDES protected key, +.IP \(bu +KMCTR AES 128bit, +.IP \(bu +KMCTR AES 192bit, +.IP \(bu +KMCTR AES 256bit, +.IP \(bu +KMCTR AES 128bit protected key, +.IP \(bu +KMCTR AES 192bit protected key, +.IP \(bu +KMCTR AES 256bit protected key, +.IP \(bu +KMO DES, +.IP \(bu +KMO 2key TDES, +.IP \(bu +KMO TDES, +.IP \(bu +KMO DES protected key, +.IP \(bu +KMO 2key TDES protected key, +.IP \(bu +KMO TDES protected key, +.IP \(bu +KMO AES 128bit, +.IP \(bu +KMO AES 192bit, +.IP \(bu +KMO AES 256bit, +.IP \(bu +KMO AES 128bit protected key, +.IP \(bu +KMO AES 192bit protected key, +.IP \(bu +KMO AES 256bit protected key, +.IP \(bu +KIMD SHA1, +.IP \(bu +KIMD SHA256, +.IP \(bu +KIMD SHA512, +.IP \(bu +KIMD SHA3-224, +.IP \(bu +KIMD SHA3-256, +.IP \(bu +KIMD SHA3-384, +.IP \(bu +KIMD SHA3-512, +.IP \(bu +KIMD SHAKE 128, +.IP \(bu +KIMD SHAE 256, +.IP \(bu +KIMD GHASH, +.IP \(bu +KLMD SHA1, +.IP \(bu +KLMD SHA256, +.IP \(bu +KLMD SHA512, +.IP \(bu +KLMD SHA3-224, +.IP \(bu +KLMD SHA3-256, +.IP \(bu +KLMD SHA3-384, +.IP \(bu +KLMD SHA3-512, +.IP \(bu +KLMD SHAKE 128, +.IP \(bu +KLMD SHAKE 256, +.IP \(bu +KMAC DES, +.IP \(bu +KMAC 2key TDES, +.IP \(bu +KMAC TDES, +.IP \(bu +KMAC DES protected key, +.IP \(bu +KMAC 2key TDES protected key, +.IP \(bu +KMAC TDES protected key, +.IP \(bu +KMAC AES 128bit, +.IP \(bu +KMAC AES 192bit, +.IP \(bu +KMAC AES 256bit, +.IP \(bu +KMAC AES 128bit protected key, +.IP \(bu +KMAC AES 192bit protected key, +.IP \(bu +KMAC AES 256bit protected key, +.IP \(bu +PCC Last Block CMAC DES, +.IP \(bu +PCC Last Block CMAC 2key TDES, +.IP \(bu +PCC Last Block CMAC TDES, +.IP \(bu +PCC Last Block CMAC DES protected key, +.IP \(bu +PCC Last Block CMAC 2key TDES protected key, +.IP \(bu +PCC Last Block CMAC TDES protected key, +.IP \(bu +PCC Last Block CMAC AES 128bit, +.IP \(bu +PCC Last Block CMAC AES 192bit, +.IP \(bu +PCC Last Block CMAC AES 256bit, +.IP \(bu +PCC Last Block CMAC AES 128bit protected key, +.IP \(bu +PCC Last Block CMAC AES 192bit protected key, +.IP \(bu +PCC Last Block CMAC AES 256bit protected key, +.IP \(bu +PCC XTS Parameter AES 128bit, +.IP \(bu +PCC XTS Parameter AES 256bit, +.IP \(bu +PCC XTS Parameter AES 128bit protected key, +.IP \(bu +PCC XTS Parameter AES 256bit protected key, +.IP \(bu +PCC Scalar Mult P256, +.IP \(bu +PCC Scalar Mult P384, +.IP \(bu +PCC Scalar Mult P521, +.IP \(bu +PCC Scalar Mult Ed25519, +.IP \(bu +PCC Scalar Mult Ed448, +.IP \(bu +PCC Scalar Mult X25519, +.IP \(bu +PCC Scalar Mult X448, +.IP \(bu +PRNO SHA512 DRNG, +.IP \(bu +PRNO TRNG Query Ratio, +.IP \(bu +PRNO TRNG, +.IP \(bu +KDSA ECDSA Verify P256, +.IP \(bu +KDSA ECDSA Verify P384, +.IP \(bu +KDSA ECDSA Verify P521, +.IP \(bu +KDSA ECDSA Sign P256, +.IP \(bu +KDSA ECDSA Sign P384, +.IP \(bu +KDSA ECDSA Sign P521, +.IP \(bu +KDSA ECDSA Sign P256 protected key, +.IP \(bu +KDSA ECDSA Sign P384 protected key, +.IP \(bu +KDSA ECDSA Sign P521 protected key, +.IP \(bu +KDSA EdDSA Verify Ed25519, +.IP \(bu +KDSA EdDSA Verify Ed448, +.IP \(bu +KDSA EdDSA Sign Ed25519, +.IP \(bu +KDSA EdDSA Sign Ed448, +.IP \(bu +KDSA EdDSA Sign Ed25519 protected key, +.IP \(bu +KDSA EdDSA Sign Ed448 protected key, +.IP \(bu +PCKMO DES, +.IP \(bu +PCKMO 2key TDES, +.IP \(bu +PCMKO TDES, +.IP \(bu +PCKMO AES 128bit, +.IP \(bu +PCKMO AES 192bit, +.IP \(bu +PCMKO AES 256bit, +.IP \(bu +PCKMO ECC P256, +.IP \(bu +PCKMO ECC P384, +.IP \(bu +PCKMO ECC P521, +.IP \(bu +PCKMO ECC Ed25519, +.IP \(bu +PCKMO ECC Ed448, +.IP \(bu +Reserved 1, and +.IP \(bu +Reserved 2. +.RE +Note that the counters starting with PCKMO and Reserved are only +available in the pai_kernel set. + +Note: CPU-MF based CPACF performance counters are available on LPARs +only. PAI counters are available on all hypervisors. For security +reasons only members of the group \fIcpacfstats\fR are allowed to run +the cpacfstats client application. Example usage scenario: .P @@ -101,12 +435,34 @@ argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter argument is omitted, all performance counters are reset to 0. .TP -\fB\-p\fR or \fB\-\-print\fR [counter] +\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter] Display the value of one or all CPACF performance counters. The optional counter argument can be one of: \fBdes\fR, \fBaes\fR, -\fBsha\fR, \fBprng\fR, \fBecc\fR, or \fBall\fR. If the counter -argument is omitted or if there is no argument, all performance -counters are displayed. +\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or +\fBall\fR. If the counter argument is omitted or if there is no +argument, all performance counters are displayed. If the optional +\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI +counters that have a non-zero value are printed. +.TP +\fB\-j\fR or \fB\-\-json\fR +Display all activated counters in JSON format. The JSON contains an +array of counter objects. Each object contains the property +.B counter +specifying either a CPU-MF counter of one of the detailed PAI counter. +Additional properties include +.B error +an error number if the counter could not be read, +.B value +the counter value if the counter could be read, +.B space +for PAI counters to specify +.B user +or +.B kernel +space counter set, and +.B counterid +for PAI counters to specify the PAI counter number as specified in the +Principles of Operation. .TP The default command is --print all. . diff --git a/cpacfstats/cpacfstats.c b/cpacfstats/cpacfstats.c index c063c67d..acc2e18a 100644 --- a/cpacfstats/cpacfstats.c +++ b/cpacfstats/cpacfstats.c @@ -3,7 +3,7 @@ * * cpacfstats client implementation * - * Copyright IBM Corp. 2015, 2020 + * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -39,17 +39,187 @@ static const char *const usage = "\t-d, --disable [counter] Disable one or all counters\n" "\t-r, --reset [counter] Reset one or all counter values\n" "\t-p, --print [counter] Print one or all counter values\n" - "\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc' or 'all'\n"; + "\t-n, --nonzero Print all PAI counters\n" + "\t-j, --json Print all counter values in JSON format\n" + "\tcounter can be: 'aes' 'des' 'rng' 'sha' 'ecc'\n" + "\t 'pai_user' 'pai_kernel' or 'all'\n"; static const char *const counter_str[] = { [DES_FUNCTIONS] = "des", [AES_FUNCTIONS] = "aes", [SHA_FUNCTIONS] = "sha", [PRNG_FUNCTIONS] = "rng", - [ECC_FUNCTIONS] = "ecc", - [ALL_COUNTER] = "all" + [ECC_FUNCTIONS] = "ecc", + [ALL_COUNTER] = "all", + [PAI_USER] = "pai_user", + [PAI_KERNEL] = "pai_kernel" }; +/* Strings for the pai counter details. Note that this is 0-based + * while PoP is 1-based. + */ +static const char *const pai_str[] = { + [ 0] = "KM DES", + [ 1] = "KM 2key TDES", + [ 2] = "KM TDES", + [ 3] = "KM DES protected key", + [ 4] = "KM 2key TDES protected key", + [ 5] = "KM TDES protected key", + [ 6] = "KM AES 128bit", + [ 7] = "KM AES 192bit", + [ 8] = "KM AES 256bit", + [ 9] = "KM AES 128bit protected key", + [ 10] = "KM AES 192bit protected key", + [ 11] = "KM AES 256bit protected key", + [ 12] = "KM AES-XTS 128bit", + [ 13] = "KM AES-XTS 256bit", + [ 14] = "KM AES-XTS 128bit protected key", + [ 15] = "KM AES-XTS 256bit protected key", + [ 16] = "KMC DES", + [ 17] = "KMC 2key TDES", + [ 18] = "KMC TDES", + [ 19] = "KMC DES protected key", + [ 20] = "KMC 2key TDES protected key", + [ 21] = "KMC TDES protected key", + [ 22] = "KMC AES 128bit", + [ 23] = "KMC AES 192bit", + [ 24] = "KMC AES 256bit", + [ 25] = "KMC AES 128bit protected key", + [ 26] = "KMC AES 192bit protected key", + [ 27] = "KMC AES 256bit protected key", + [ 28] = "KMC PRNG", + [ 29] = "KMA AES 128bit", + [ 30] = "KMA AES 192bit", + [ 31] = "KMA AES 256bit", + [ 32] = "KMA AES 128bit protected key", + [ 33] = "KMA AES 192bit protected key", + [ 34] = "KMA AES 256bit protected key", + [ 35] = "KMF DES", + [ 36] = "KMF 2key TDES", + [ 37] = "KMF TDES", + [ 38] = "KMF DES protected key", + [ 39] = "KMF 2key TDES protected key", + [ 40] = "KMF TDES protected key", + [ 41] = "KMF AES 128bit", + [ 42] = "KMF AES 192bit", + [ 43] = "KMF AES 256bit", + [ 44] = "KMF AES 128bit protected key", + [ 45] = "KMF AES 192bit protected key", + [ 46] = "KMF AES 256bit protected key", + [ 47] = "KMCTR DES", + [ 48] = "KMCTR 2key TDES", + [ 49] = "KMCTR TDES", + [ 50] = "KMCTR DES protected key", + [ 51] = "KMCTR 2key TDES protected key", + [ 52] = "KMCTR TDES protected key", + [ 53] = "KMCTR AES 128bit", + [ 54] = "KMCTR AES 192bit", + [ 55] = "KMCTR AES 256bit", + [ 56] = "KMCTR AES 128bit protected key", + [ 57] = "KMCTR AES 192bit protected key", + [ 58] = "KMCTR AES 256bit protected key", + [ 59] = "KMO DES", + [ 60] = "KMO 2key TDES", + [ 61] = "KMO TDES", + [ 62] = "KMO DES protected key", + [ 63] = "KMO 2key TDES protected key", + [ 64] = "KMO TDES protected key", + [ 65] = "KMO AES 128bit", + [ 66] = "KMO AES 192bit", + [ 67] = "KMO AES 256bit", + [ 68] = "KMO AES 128bit protected key", + [ 69] = "KMO AES 192bit protected key", + [ 70] = "KMO AES 256bit protected key", + [ 71] = "KIMD SHA1", + [ 72] = "KIMD SHA256", + [ 73] = "KIMD SHA512", + [ 74] = "KIMD SHA3-224", + [ 75] = "KIMD SHA3-256", + [ 76] = "KIMD SHA3-384", + [ 77] = "KIMD SHA3-512", + [ 78] = "KIMD SHAKE 128", + [ 79] = "KIMD SHAE 256", + [ 80] = "KIMD GHASH", + [ 81] = "KLMD SHA1", + [ 82] = "KLMD SHA256", + [ 83] = "KLMD SHA512", + [ 84] = "KLMD SHA3-224", + [ 85] = "KLMD SHA3-256", + [ 86] = "KLMD SHA3-384", + [ 87] = "KLMD SHA3-512", + [ 88] = "KLMD SHAKE 128", + [ 89] = "KLMD SHAKE 256", + [ 90] = "KMAC DES", + [ 91] = "KMAC 2key TDES", + [ 92] = "KMAC TDES", + [ 93] = "KMAC DES protected key", + [ 94] = "KMAC 2key TDES protected key", + [ 95] = "KMAC TDES protected key", + [ 96] = "KMAC AES 128bit", + [ 97] = "KMAC AES 192bit", + [ 98] = "KMAC AES 256bit", + [ 99] = "KMAC AES 128bit protected key", + [100] = "KMAC AES 192bit protected key", + [101] = "KMAC AES 256bit protected key", + [102] = "PCC Last Block CMAC DES", + [103] = "PCC Last Block CMAC 2key TDES", + [104] = "PCC Last Block CMAC TDES", + [105] = "PCC Last Block CMAC DES protected key", + [106] = "PCC Last Block CMAC 2key TDES protected key", + [107] = "PCC Last Block CMAC TDES protected key", + [108] = "PCC Last Block CMAC AES 128bit", + [109] = "PCC Last Block CMAC AES 192bit", + [110] = "PCC Last Block CMAC AES 256bit", + [111] = "PCC Last Block CMAC AES 128bit protected key", + [112] = "PCC Last Block CMAC AES 192bit protected key", + [113] = "PCC Last Block CMAC AES 256bit protected key", + [114] = "PCC XTS Parameter AES 128bit", + [115] = "PCC XTS Parameter AES 256bit", + [116] = "PCC XTS Parameter AES 128bit protected key", + [117] = "PCC XTS Parameter AES 256bit protected key", + [118] = "PCC Scalar Mult P256", + [119] = "PCC Scalar Mult P384", + [120] = "PCC Scalar Mult P521", + [121] = "PCC Scalar Mult Ed25519", + [122] = "PCC Scalar Mult Ed448", + [123] = "PCC Scalar Mult X25519", + [124] = "PCC Scalar Mult X448", + [125] = "PRNO SHA512 DRNG", + [126] = "PRNO TRNG Query Ratio", + [127] = "PRNO TRNG", + [128] = "KDSA ECDSA Verify P256", + [129] = "KDSA ECDSA Verify P384", + [130] = "KDSA ECDSA Verify P521", + [131] = "KDSA ECDSA Sign P256", + [132] = "KDSA ECDSA Sign P384", + [133] = "KDSA ECDSA Sign P521", + [134] = "KDSA ECDSA Sign P256 protected key", + [135] = "KDSA ECDSA Sign P384 protected key", + [136] = "KDSA ECDSA Sign P521 protected key", + [137] = "KDSA EdDSA Verify Ed25519", + [138] = "KDSA EdDSA Verify Ed448", + [139] = "KDSA EdDSA Sign Ed25519", + [140] = "KDSA EdDSA Sign Ed448", + [141] = "KDSA EdDSA Sign Ed25519 protected key", + [142] = "KDSA EdDSA Sign Ed448 protected key", + [143] = "PCKMO DES", + [144] = "PCKMO 2key TDES", + [145] = "PCMKO TDES", + [146] = "PCKMO AES 128bit", + [147] = "PCKMO AES 192bit", + [148] = "PCMKO AES 256bit", + [149] = "PCKMO ECC P256", + [150] = "PCKMO ECC P384", + [151] = "PCKMO ECC P521", + [152] = "PCKMO ECC Ed25519", + [153] = "PCKMO ECC Ed448", + [154] = "Reserved 1", + [155] = "Reserved 2" +}; + + +static int paiprintnonzero; + static int send_query(int s, enum cmd_e cmd, enum ctr_e ctr) { @@ -91,21 +261,140 @@ static int recv_answer(int s, int *ctr, int *state, uint64_t *value) return rc; } -static void print_virtual_counter_answer(int ctr, int state, uint64_t value) + +static void printjsonsep(void) { - if (ctr == HOTPLUG_DETECTED) { - if (state >= 0 && value > 0) - printf(" hotplug detected\n"); + static const char *jsonsep = ""; + + fputs(jsonsep, stdout); + jsonsep = ","; +} + + +static void json_print_virtual_counter_answer(int s, int ctr, + int state, uint64_t value) +{ + int paictr = 0, paistate = 0, ec; + uint64_t i, paivalue = 0, maxnum; + const char *space; + + switch (ctr) { + case HOTPLUG_DETECTED: + printjsonsep(); + printf("{\"counter\":\"hotplug detected\","); + if (state < 0) + printf("\"error\":%d}", state); + else + printf("\"value\":%d}", !!value); + return; + case PAI_USER: + maxnum = NUM_PAI_USER; + space = "user"; + break; + case PAI_KERNEL: + maxnum = NUM_PAI_KERNEL; + space = "kernel"; + break; + default: + return; + } + /* Here, we have validated the PAI counter retrieved, but not + * yet printed. */ + if (state != ENABLED) + return; + if (value > maxnum) { + eprint("Incompatible versions detected!\n"); + eprint("Expected %"PRIu64" counter space for %s, but got %"PRIu64"\n", + maxnum, space, value); + exit(EXIT_FAILURE); + } + for (i = 0; i < value; ++i) { + ec = recv_answer(s, &paictr, &paistate, &paivalue); + if (ec < 0 || paistate < 0) { + eprint("Error on receiving answer message from daemon\n"); + /* No more data for this virtual event after error. */ + return; + } + if (paictr > NUM_PAI_KERNEL) { + eprint("Pai counter number too big: %d\n", paictr); + } else { + printjsonsep(); + printf("{\"counter\":\"%s\",\"space\":\"%s\",\"counterid\":%d,", + pai_str[paictr], space, paictr + 1); + if (paistate < 0) { + printf("\"error\":%d}", paistate); + /* Protocol does not send furter counters. */ + return; + } + printf("\"value\":%"PRIu64"}", paivalue); + } } } -static void print_answer(int ctr, int state, uint64_t value) + +static void print_virtual_counter_answer(int s, + int ctr, int state, uint64_t value) { - if (ctr > ALL_COUNTER) { - print_virtual_counter_answer(ctr, state, value); + static const char *const states[] = { + [DISABLED] = "disabled", + [ENABLED] = "enabled", + [UNSUPPORTED] = "unsupported" + }; + int paictr = 0, paistate = 0, ec; + uint64_t i, paivalue = 0, maxnum; + const char *ctrstr; + + switch (ctr) { + case HOTPLUG_DETECTED: + if (state >= 0 && value > 0) + printf(" hotplug detected\n"); + return; + case PAI_USER: + maxnum = NUM_PAI_USER; + ctrstr = "pai_user"; + break; + case PAI_KERNEL: + maxnum = NUM_PAI_KERNEL; + ctrstr = "pai_kernel"; + break; + default: return; } - if (state < 0) + /* Here, we have validated the PAI counter retrieved, but not + * yet printed. */ + if (state < 0 || state > UNSUPPORTED) { + printf(" %11s: error state %d\n", ctrstr, state); + /* No details follow if counter in error state. */ + return; + } + printf(" %-11s: %s\n", ctrstr, states[state]); + if (state != ENABLED) + return; + if (value > maxnum) { + eprint("Incompatible versions detected!\n"); + eprint("Expected %"PRIu64" counters for %s, but got %"PRIu64"\n", + maxnum, ctrstr, value); + exit(EXIT_FAILURE); + } + for (i = 0; i < value; ++i) { + ec = recv_answer(s, &paictr, &paistate, &paivalue); + if (ec < 0 || paistate < 0) { + eprint("Error on receiving answer message from daemon\n"); + /* No more data for this virtual event after error. */ + return; + } + if (paictr > NUM_PAI_KERNEL) + eprint("Pai counter number too big: %d\n", paictr); + else if (!paiprintnonzero || paivalue > 0) + printf(" %-45s: %"PRIu64"\n", pai_str[paictr], paivalue); + } +} + +static void print_answer(int s, int ctr, int state, uint64_t value) +{ + if (ctr > ALL_COUNTER) + print_virtual_counter_answer(s, ctr, state, value); + else if (state < 0) printf(" %s counter: error state %d\n", counter_str[ctr], state); else if (state == DISABLED) @@ -117,6 +406,22 @@ static void print_answer(int ctr, int state, uint64_t value) } +static void json_print_answer(int s, int ctr, int state, uint64_t value) +{ + if (ctr > ALL_COUNTER) { + json_print_virtual_counter_answer(s, ctr, state, value); + } else if (state < 0) { + printjsonsep(); + printf("{\"counter\":\"%s\",", counter_str[ctr]); + printf("\"error\":%d}\n", state); + } else if (state == ENABLED) { + printjsonsep(); + printf("{\"counter\":\"%s\",", counter_str[ctr]); + printf("\"value\":%"PRIu64"}", value); + } +} + + int eprint(const char *format, ...) { char buf[1024]; @@ -139,7 +444,7 @@ int main(int argc, char *argv[]) { enum ctr_e ctr = ALL_COUNTER; enum cmd_e cmd = PRINT; - int i, j, s, state; + int i, j, s, state, num, json = 0; uint64_t value; if (argc > 1) { @@ -151,110 +456,108 @@ int main(int argc, char *argv[]) { "disable", 0, NULL, 'd' }, { "reset", 0, NULL, 'r' }, { "print", 0, NULL, 'p' }, + { "nonzero", 0, NULL, 'n' }, + { "json", 0, NULL, 'j' }, { NULL, 0, NULL, 0 } }; while (1) { opt = getopt_long(argc, argv, - "hvedrp", long_opts, &idx); + "hvedrpnj", long_opts, &idx); if (opt == -1) break; /* no more arguments */ switch (opt) { case 'h': printf(usage, name); - exit(0); - break; + return 0; case 'v': printf("%s: Linux on System z CPACF Crypto Activity Counters Client\n" "Version %s\n%s\n", name, RELEASE_STRING, COPYRIGHT); - exit(0); - break; + return 0; case 'e': cmd = ENABLE; + json = 0; break; case 'd': cmd = DISABLE; + json = 0; break; case 'r': cmd = RESET; break; case 'p': cmd = PRINT; + json = 0; + break; + case 'n': + paiprintnonzero = 1; + break; + case 'j': + cmd = PRINT; + json = 1; break; default: eprint("Invalid argument, try -h or --help for more information\n"); - exit(1); - break; + return EXIT_FAILURE; } } /* there may be an optional counter argument */ if (optind > 0 && optind < argc) { - for (i = 0; i <= ALL_COUNTER; i++) + for (i = 0; i < NUM_COUNTER; i++) if (strcmp(argv[optind], counter_str[i]) == 0) break; - if (i > ALL_COUNTER) { + if (i >= NUM_COUNTER) { eprint("Unknown counter '%s'\n", argv[optind]); - exit(1); + return EXIT_FAILURE; } ctr = (enum ctr_e) i; } } + if (json) + ctr = ALL_COUNTER; /* try to open and connect socket to the cpacfstatsd daemon */ s = open_socket(CLIENT); if (s < 0) { eprint("Can't connect to daemon\n"); - exit(1); + return EXIT_FAILURE; } /* send query */ if (send_query(s, cmd, ctr) != 0) { eprint("Error on sending query message to daemon\n"); close(s); - exit(1); + return EXIT_FAILURE; } if (ctr == ALL_COUNTER) { - for (i = 0; i < ALL_COUNTER; i++) { - /* receive answer */ - if (recv_answer(s, &j, &state, &value) != 0) { - eprint("Error on receiving answer message from daemon\n"); - exit(1); - } - if (state < 0) { - eprint("Received bad status code %d from daemon\n", - state); - close(s); - exit(1); - } - print_answer(j, state, value); - } + /* The -1 is for ALL_COUNTER which is not sent, +1 for + * hotplug state. */ + num = NUM_COUNTER - 1 + 1; } else { + /* +1 for hotplug state */ + num = 1 + 1; + } + if (json) + putchar('['); + for (i = 0; i < num; i++) { /* receive answer */ if (recv_answer(s, &j, &state, &value) != 0) { eprint("Error on receiving answer message from daemon\n"); - close(s); - exit(1); + return EXIT_FAILURE; } if (state < 0) { eprint("Received bad status code %d from daemon\n", - state); + state); close(s); - exit(1); + return EXIT_FAILURE; } - print_answer(j, state, value); + if (json) + json_print_answer(s, j, state, value); + else + print_answer(s, j, state, value); } - /* receive hotplug state */ - if (recv_answer(s, &j, &state, &value) != 0) { - eprint("Error on receiving hotplug state from daemon\n"); - close(s); - exit(1); - } - if (state < 0) { - eprint("Received bad status code %d from daemon\n", state); - close(s); - exit(1); - } - print_answer(j, state, value); + if (json) + putchar(']'); /* close connection */ close(s); diff --git a/cpacfstats/cpacfstats.h b/cpacfstats/cpacfstats.h index c39bc274..c458dc7d 100644 --- a/cpacfstats/cpacfstats.h +++ b/cpacfstats/cpacfstats.h @@ -3,7 +3,7 @@ * * common function prototypes and definitions * - * Copyright IBM Corp. 2015, 2020 + * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -16,15 +16,25 @@ #define COPYRIGHT "Copyright IBM Corp. 2015, 2022" -#define DEFAULT_SEND_TIMEOUT (2 * 1000) -#define DEFAULT_RECV_TIMEOUT (2 * 1000) +#define DEFAULT_SEND_TIMEOUT (30 * 1000) +#define DEFAULT_RECV_TIMEOUT (30 * 1000) + +/* + * Number of PAI counters for user space. This excludes PCKMO since + * this instruction is privileged. + */ +#define NUM_PAI_USER 143 +/* Number of PAI counters for kernel space. Contains all counters. */ +#define NUM_PAI_KERNEL 156 int eprint(const char *format, ...); /* * Counter names * ALL_COUNTER specifies the number of physical counters. Virtual - * counters might be added afterwards. + * counters might be added afterwards. NUM_COUNTER is the last + * managed counter (i.e., a counter that can be activated, reset, + * deactivated). */ enum ctr_e { DES_FUNCTIONS = 0, @@ -33,7 +43,10 @@ enum ctr_e { PRNG_FUNCTIONS, ECC_FUNCTIONS, ALL_COUNTER, - HOTPLUG_DETECTED + PAI_USER, + PAI_KERNEL, + NUM_COUNTER, + HOTPLUG_DETECTED = 0xffff }; enum type_e { @@ -68,7 +81,7 @@ struct msg_query { /* * answer send from daemon to client * Consist of: - * enum counter + * enum counter or PAI counter number if following PAI_USER or PAI_KERNEL * status code: < 0 error, 0 disabled, > 0 enabled * counter value */ @@ -114,9 +127,10 @@ void perf_stop(void); void perf_close(void); int perf_enable_ctr(enum ctr_e ctr); int perf_disable_ctr(enum ctr_e ctr); -int perf_reset_ctr(enum ctr_e ctr); +int perf_reset_ctr(enum ctr_e ctr, uint64_t *value); int perf_read_ctr(enum ctr_e ctr, uint64_t *value); int perf_ecc_supported(void); int perf_ctr_state(enum ctr_e ctr); +int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value); #endif diff --git a/cpacfstats/cpacfstatsd.c b/cpacfstats/cpacfstatsd.c index 43738d60..8248004b 100644 --- a/cpacfstats/cpacfstatsd.c +++ b/cpacfstats/cpacfstatsd.c @@ -3,7 +3,7 @@ * * cpacfstatsd daemon implementation * - * Copyright IBM Corp. 2015, 2017 + * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -85,13 +85,50 @@ static int send_answer(int s, int ctr, int state, uint64_t value) } +/* + * Print according to protocol for PAI: + * - first the state and the number of PAI counters that follow + * - if state is ENABLED: + * - for each PAI counter the value with state ENABLED + * Note that the PAI counters are 0-based, not 1 based as in PoP! + * Sending ends with the first error. + */ +static int do_send_pai(int s, int user) +{ + int ctr, state, i, maxctr, rc = 0; + uint64_t value; + + if (user) { + ctr = PAI_USER; + maxctr = NUM_PAI_USER; + } else { + ctr = PAI_KERNEL; + maxctr = NUM_PAI_KERNEL; + } + state = perf_ctr_state(ctr); + if (state != ENABLED) + return rc; + for (i = 0; i < maxctr; ++i) { + rc = perf_read_pai_ctr(i, user, &value); + if (rc != 0) { + send_answer(s, i, rc, 0); + break; + } + send_answer(s, i, state, value); + } + return rc; +} + + static int do_enable(int s, enum ctr_e ctr) { uint64_t value = 0; int i, rc = 0; int state; - for (i = 0; i < ALL_COUNTER; i++) { + for (i = 0; i < NUM_COUNTER; i++) { + if (i == ALL_COUNTER) + continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == DISABLED) { @@ -110,6 +147,10 @@ static int do_enable(int s, enum ctr_e ctr) } } send_answer(s, i, state, value); + if (i == PAI_USER) + rc = do_send_pai(s, 1); + if (i == PAI_KERNEL) + rc = do_send_pai(s, 0); } } if (rc == 0) { @@ -125,7 +166,9 @@ static int do_disable(int s, enum ctr_e ctr) int i, rc = 0; uint64_t value; - for (i = 0; i < ALL_COUNTER; i++) { + for (i = 0; i < NUM_COUNTER; i++) { + if (i == ALL_COUNTER) + continue; if (i == (int) ctr || ctr == ALL_COUNTER) { if (perf_ctr_state(i) == ENABLED) { rc = perf_disable_ctr(i); @@ -150,17 +193,23 @@ static int do_reset(int s, enum ctr_e ctr) int i, rc = 0, state; uint64_t value; - for (i = 0; i < ALL_COUNTER; i++) { + for (i = 0; i < NUM_COUNTER; i++) { + if (i == ALL_COUNTER) + continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == ENABLED) { - rc = perf_reset_ctr(i); + rc = perf_reset_ctr(i, &value); if (rc != 0) { send_answer(s, i, rc, 0); break; } } - send_answer(s, i, state, 0); + send_answer(s, i, state, value); + if (i == PAI_USER) + rc = do_send_pai(s, 1); + if (i == PAI_KERNEL) + rc = do_send_pai(s, 0); } } if (rc == 0) { @@ -170,12 +219,15 @@ static int do_reset(int s, enum ctr_e ctr) return rc; } + static int do_print(int s, enum ctr_e ctr) { int i, rc = 0, state; uint64_t value = 0; - for (i = 0; i < ALL_COUNTER; i++) { + for (i = 0; i < NUM_COUNTER; i++) { + if (i == ALL_COUNTER) + continue; if (i == (int) ctr || ctr == ALL_COUNTER) { state = perf_ctr_state(i); if (state == ENABLED) { @@ -186,6 +238,10 @@ static int do_print(int s, enum ctr_e ctr) } } send_answer(s, i, state, value); + if (i == PAI_USER) + rc = do_send_pai(s, 1); + if (i == PAI_KERNEL) + rc = do_send_pai(s, 0); } } if (rc == 0) { @@ -380,7 +436,7 @@ int main(int argc, char *argv[]) switch (opt) { case 'h': printf(usage, name); - exit(0); + return 0; case 'f': foreground = 1; break; @@ -388,37 +444,37 @@ int main(int argc, char *argv[]) printf("%s: Linux on System z CPACF Crypto Activity Counters Daemon\n" "Version %s\n%s\n", name, RELEASE_STRING, COPYRIGHT); - exit(0); + return 0; default: printf("%s: Invalid argument, try -h or --help for more information\n", name); - exit(1); + return EXIT_FAILURE; } } } if (check_pidfile() != 0) { eprint("Stalled pid file or daemon allready running, terminating\n"); - exit(1); + return EXIT_FAILURE; } if (!foreground) { if (become_daemon() != 0) { eprint("Couldn't daemonize\n"); - exit(1); + return EXIT_FAILURE; } } if (perf_init() != 0) { eprint("Couldn't initialize perf lib\n"); - exit(1); + return EXIT_FAILURE; } atexit(perf_close); sfd = open_socket(SERVER); if (sfd < 0) { eprint("Couldn't initialize server socket\n"); - exit(1); + return EXIT_FAILURE; } atexit(remove_sock); @@ -428,13 +484,15 @@ int main(int argc, char *argv[]) if (sigaction(SIGINT, &act, 0) != 0) { eprint("Couldn't establish signal handler for SIGINT, errno=%d [%s]\n", errno, strerror(errno)); - exit(1); + return EXIT_FAILURE; } if (sigaction(SIGTERM, &act, 0) != 0) { eprint("Couldn't establish signal handler for SIGTERM, errno=%d [%s]\n", errno, strerror(errno)); - exit(1); + return EXIT_FAILURE; } + /* Ignore SIGPIPE such that we see EPIPE as return from write. */ + signal(SIGPIPE, SIG_IGN); eprint("Running\n"); @@ -449,7 +507,7 @@ int main(int argc, char *argv[]) continue; eprint("Accept() failure, errno=%d [%s]\n", errno, strerror(errno)); - exit(1); + return EXIT_FAILURE; } rc = recv_query(s, &ctr, &cmd); diff --git a/cpacfstats/perf_crypto.c b/cpacfstats/perf_crypto.c index be14dfc9..a08af608 100644 --- a/cpacfstats/perf_crypto.c +++ b/cpacfstats/perf_crypto.c @@ -3,7 +3,7 @@ * * low level perf functions * - * Copyright IBM Corp. 2015, 2020 + * Copyright IBM Corp. 2015, 2022 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -31,14 +32,12 @@ #include "cpacfstats.h" #include "../include/lib/zt_common.h" -#define NUM_COUNTER ALL_COUNTER - /* correlation between counter and perf counter string */ static const struct { char pmu[20]; char pfm_name[60]; enum ctr_e ctr; -} pmf_counter_name[NUM_COUNTER] = { +} pmf_counter_name[ALL_COUNTER] = { {"cpum_cf", "DEA_FUNCTIONS", DES_FUNCTIONS}, {"cpum_cf", "AES_FUNCTIONS", AES_FUNCTIONS}, {"cpum_cf", "SHA_FUNCTIONS", SHA_FUNCTIONS}, @@ -49,10 +48,12 @@ static const struct { static struct pmf_data { int pmutype; int eventid; -} pmf_counter_data[NUM_COUNTER]; +} pmf_counter_data[ALL_COUNTER]; struct percpucounter { - int ctr_fds[NUM_COUNTER]; + int ctr_fds[ALL_COUNTER]; + int pai_user[NUM_PAI_USER]; + int pai_kernel[NUM_PAI_KERNEL]; unsigned int cpunum; struct percpucounter *next; }; @@ -71,6 +72,8 @@ static int ctr_state[NUM_COUNTER]; static volatile int stoprequested; +static int paipmutype, paipmueventstart; + static struct percpucounter *allocpercpucounter(unsigned int cpunum) { struct percpucounter *ppc; @@ -79,8 +82,12 @@ static struct percpucounter *allocpercpucounter(unsigned int cpunum) if (ppc) { int i; - for (i = 0; i < NUM_COUNTER; ++i) + for (i = 0; i < ALL_COUNTER; ++i) ppc->ctr_fds[i] = -1; + for (i = 0; i < NUM_PAI_USER; ++i) + ppc->pai_user[i] = -1; + for (i = 0; i < NUM_PAI_KERNEL; ++i) + ppc->pai_kernel[i] = -1; ppc->cpunum = cpunum; ppc->next = NULL; } @@ -91,8 +98,12 @@ static void freepercpucounter(struct percpucounter *pcpu) { int i; - for (i = 0; i < NUM_COUNTER; ++i) + for (i = 0; i < ALL_COUNTER; ++i) (void)close(pcpu->ctr_fds[i]); + for (i = 0; i < NUM_PAI_USER; ++i) + (void)close(pcpu->pai_user[i]); + for (i = 0; i < NUM_PAI_KERNEL; ++i) + (void)close(pcpu->pai_kernel[i]); free(pcpu); } @@ -139,6 +150,39 @@ static int perf_counter_supported(const char *pmu, const char *counter) return !access(buf, R_OK); } +static int cpumf_authorized(void) +{ + unsigned vermin, vermax, auth; + int res = 0, found = 0; + size_t linesize = 0; + char *line = NULL; + FILE *f; + + f = fopen("/proc/service_levels", "r"); + if (f == NULL) { + eprint("Failed to open /proc/service_levels (%d:%s)\n", + errno, strerror(errno)); + return 0; + } + while (getline(&line, &linesize, f) >= 0) { + if (sscanf(line, + "CPU-MF: Counter facility: version=%d.%d authorization=%x", + &vermin, &vermax, &auth) == 3) { + if (auth & 0x8) + res = 1; + else + eprint("CPU-MF counters not authorized.\n"); + found = 1; + break; + } + } + if (!found) + eprint("CPU-MF counters not available.\n"); + free(line); + fclose(f); + return res; +} + static int perf_event_encode(int *pmutype, int *eventid, const char *pmu, const char *event) { @@ -196,7 +240,8 @@ static int activatecpu(unsigned int cpu) freepercpucounter(ppc); return -1; } - for (i = 0; i < NUM_COUNTER; ++i) { + /* activate CPU-MF */ + for (i = 0; i < ALL_COUNTER; ++i) { if (ctr_state[i] == UNSUPPORTED) continue; memset(&pfm_event, 0, sizeof(pfm_event)); @@ -223,6 +268,67 @@ static int activatecpu(unsigned int cpu) ppc->ctr_fds[i] = fd; } } + /* activate pai_user and pai_kernel */ + /* invariant: + (ctr_state[PAI_USER] == UNSUPPORTED) == + (ctr_state[PAI_KERNEL] == UNSUPPORTED) */ + if (ctr_state[PAI_USER] != UNSUPPORTED) { + for (i = 1; i <= NUM_PAI_USER; ++i) { + memset(&pfm_event, 0, sizeof(pfm_event)); + pfm_event.size = sizeof(pfm_event); + pfm_event.type = paipmutype; + pfm_event.config = paipmueventstart + i; + pfm_event.exclude_kernel = 1; + pfm_event.exclude_user = 0; + pfm_event.disabled = ctr_state[PAI_USER] == DISABLED; + fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); + if (fd < 0) { + eprint("Perf_event_open() failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + ctr_state[PAI_USER] = UNSUPPORTED; + ctr_state[PAI_KERNEL] = UNSUPPORTED; + goto outevents; + } else { + ppc->pai_user[i - 1] = fd; + } + pfm_event.exclude_kernel = 0; + pfm_event.exclude_user = 1; + pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED; + fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); + if (fd < 0) { + eprint("Perf_event_open() failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + ctr_state[PAI_USER] = UNSUPPORTED; + ctr_state[PAI_KERNEL] = UNSUPPORTED; + goto outevents; + } else { + ppc->pai_kernel[i - 1] = fd; + } + } + for (; i <= NUM_PAI_KERNEL; ++i) { + memset(&pfm_event, 0, sizeof(pfm_event)); + pfm_event.size = sizeof(pfm_event); + pfm_event.type = paipmutype; + pfm_event.config = paipmueventstart + i; + pfm_event.exclude_kernel = 0; + pfm_event.exclude_user = 1; + pfm_event.disabled = ctr_state[PAI_KERNEL] == DISABLED; + fd = perf_event_open(&pfm_event, -1, cpu, -1, 0); + if (fd < 0) { + eprint("Perf_event_open() failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + ctr_state[PAI_USER] = UNSUPPORTED; + ctr_state[PAI_KERNEL] = UNSUPPORTED; + goto outevents; + } else { + ppc->pai_kernel[i - 1] = fd; + } + } + } +outevents: ppc->next = root; root = ppc; if (enabledcounter) @@ -240,8 +346,12 @@ static void deactivatecpu(unsigned int cpunum) return; pcpu = findcpu(cpunum, 1); if (pcpu != NULL) { - for (i = 0; i < NUM_COUNTER; ++i) + for (i = 0; i < ALL_COUNTER; ++i) (void)close(pcpu->ctr_fds[i]); + for (i = 0; i < NUM_PAI_USER; ++i) + (void)close(pcpu->pai_user[i]); + for (i = 0; i < NUM_PAI_KERNEL; ++i) + (void)close(pcpu->pai_kernel[i]); free(pcpu); if (enabledcounter) hotplugdetected = 1; @@ -286,13 +396,16 @@ static int perf_load_counter_data(void) { int i, res = 0; - for (i = 0; i < NUM_COUNTER; ++i) { + for (i = 0; i < ALL_COUNTER; ++i) { if (ctr_state[i] != UNSUPPORTED) res |= perf_event_encode(&pmf_counter_data[i].pmutype, &pmf_counter_data[i].eventid, pmf_counter_name[i].pmu, pmf_counter_name[i].pfm_name); } + if (ctr_state[PAI_USER] != UNSUPPORTED) + res |= perf_event_encode(&paipmutype, &paipmueventstart, + "pai_crypto", "CRYPTO_ALL"); return res; } @@ -351,7 +464,10 @@ static void *hotplughandler(void *UNUSED(unused)) int perf_init(void) { - int ecc_supported; + int ecc_supported, i, num; + unsigned long maxfd; + struct rlimit rlim; + FILE *f; /* initialize performance monitoring library */ if (!perf_supported()) { @@ -359,13 +475,56 @@ int perf_init(void) return -1; } + /* We currently support all cpumf counters plus two virtual + * counters for PAI. */ + num = ALL_COUNTER + 2; /* Check if ECC is supported on current hardware */ ecc_supported = perf_counter_supported("cpum_cf", "ECC_FUNCTION_COUNT"); - if (!ecc_supported) + + if (!cpumf_authorized()) { + for (i = 0; i < ALL_COUNTER; ++i) + ctr_state[i] = UNSUPPORTED; + num -= ALL_COUNTER; + } else if (!ecc_supported) { ctr_state[ECC_FUNCTIONS] = UNSUPPORTED; + --num; + } + + if (!perf_counter_supported("pai_crypto", "CRYPTO_ALL")) { + ctr_state[PAI_USER] = UNSUPPORTED; + ctr_state[PAI_KERNEL] = UNSUPPORTED; + num -= 2; + } + + if (num == 0) { + eprint("No crypto counters supported!\n"); + return -1; + } if (perf_load_counter_data()) return -1; + /* We have to adjust the number of FDs possible since we might + * need more than 1024 (the typical soft limit) */ + f = fopen("/proc/sys/fs/nr_open", "r"); + if (f == NULL) { + eprint("fopen failed for /proc/sys/fs/nr_open with errno=%d [%s]\n", + errno, strerror(errno)); + return -1; + } + if (fscanf(f, "%lu", &maxfd) != 1) { + fclose(f); + eprint("Failed to parse /proc/sys/fs/nr_open\n"); + return -1; + } + fclose(f); + rlim.rlim_cur = maxfd; + rlim.rlim_max = maxfd; + if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { + eprint("setrlimit failed with errno=%d [%s]\n", + errno, strerror(errno)); + return -1; + } + if (pthread_create(&hotplugthread, NULL, hotplughandler, NULL)) { eprint("Failed to start hotplug handler thread\n"); return -1; @@ -395,6 +554,22 @@ void perf_close(void) } +static int enable_array(int *arr, int size) +{ + int i, ec, rc = 0; + + for (i = 0; i < size; ++i) { + ec = ioctl(arr[i], PERF_EVENT_IOC_ENABLE, 0); + if (ec < 0) { + eprint("Ioctl(PERF_EVENT_IOC_ENABLE) failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + } + } + return rc; +} + + int perf_enable_ctr(enum ctr_e ctr) { struct percpucounter *pcpu; @@ -418,6 +593,24 @@ int perf_enable_ctr(enum ctr_e ctr) ctr_state[ctr] = ENABLED; ++enabledcounter; endforeachcpu(); + } else if (ctr == PAI_USER) { + foreachcpu(pcpu) { + ec = enable_array(pcpu->pai_user, NUM_PAI_USER); + if (ec < 0) + rc = -1; + } + ctr_state[ctr] = ENABLED; + ++enabledcounter; + endforeachcpu(); + } else if (ctr == PAI_KERNEL) { + foreachcpu(pcpu) { + ec = enable_array(pcpu->pai_kernel, NUM_PAI_KERNEL); + if (ec < 0) + rc = -1; + } + ctr_state[ctr] = ENABLED; + ++enabledcounter; + endforeachcpu(); } else { rc = -1; } @@ -426,6 +619,22 @@ int perf_enable_ctr(enum ctr_e ctr) } +static int disable_array(int *arr, int size) +{ + int i, ec, rc = 0; + + for (i = 0; i < size; ++i) { + ec = ioctl(arr[i], PERF_EVENT_IOC_DISABLE, 0); + if (ec < 0) { + eprint("Ioctl(PERF_EVENT_IOC_DISABLE) failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + } + } + return rc; +} + + int perf_disable_ctr(enum ctr_e ctr) { struct percpucounter *pcpu; @@ -451,6 +660,28 @@ int perf_disable_ctr(enum ctr_e ctr) if (enabledcounter == 0) hotplugdetected = 0; endforeachcpu(); + } else if (ctr == PAI_USER) { + foreachcpu(pcpu) { + ec = disable_array(pcpu->pai_user, NUM_PAI_USER); + if (ec < 0) + rc = -1; + } + ctr_state[ctr] = DISABLED; + --enabledcounter; + if (enabledcounter == 0) + hotplugdetected = 0; + endforeachcpu(); + } else if (ctr == PAI_KERNEL) { + foreachcpu(pcpu) { + ec = disable_array(pcpu->pai_kernel, NUM_PAI_KERNEL); + if (ec < 0) + rc = -1; + } + ctr_state[ctr] = DISABLED; + --enabledcounter; + if (enabledcounter == 0) + hotplugdetected = 0; + endforeachcpu(); } else { rc = -1; } @@ -459,14 +690,30 @@ int perf_disable_ctr(enum ctr_e ctr) } -int perf_reset_ctr(enum ctr_e ctr) +static int reset_array(int *arr, int size) +{ + int ec, rc = 0, i; + + for (i = 0; i < size; ++i) { + ec = ioctl(arr[i], PERF_EVENT_IOC_RESET, 0); + if (ec < 0) { + eprint("Ioctl(PERF_EVENT_IOC_RESET) failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + } + } + return rc; +} + + +int perf_reset_ctr(enum ctr_e ctr, uint64_t *value) { struct percpucounter *pcpu; int ec, rc = 0; if (ctr == ALL_COUNTER) { for (ctr = 0; ctr < ALL_COUNTER; ctr++) { - rc = perf_reset_ctr(ctr); + rc = perf_reset_ctr(ctr, value); if (rc != 0) return rc; } @@ -480,10 +727,25 @@ int perf_reset_ctr(enum ctr_e ctr) } } endforeachcpu(); + } else if (ctr == PAI_USER) { + foreachcpu(pcpu) { + ec = reset_array(pcpu->pai_user, NUM_PAI_USER); + if (ec < 0) + rc = -1; + } + endforeachcpu(); + } else if (ctr == PAI_KERNEL) { + foreachcpu(pcpu) { + ec = reset_array(pcpu->pai_kernel, NUM_PAI_KERNEL); + if (ec < 0) + rc = -1; + } + endforeachcpu(); } else { rc = -1; } - + if (rc == 0) + rc = perf_read_ctr(ctr, value); return rc; } @@ -491,7 +753,7 @@ int perf_reset_ctr(enum ctr_e ctr) int perf_read_ctr(enum ctr_e ctr, uint64_t *value) { struct percpucounter *pcpu; - int ec, rc = -1; + int ec, rc = 0; uint64_t val; if (!value) @@ -500,6 +762,14 @@ int perf_read_ctr(enum ctr_e ctr, uint64_t *value) *value = hotplugdetected; return 0; } + if (ctr == PAI_USER) { + *value = NUM_PAI_USER; + return 0; + } + if (ctr == PAI_KERNEL) { + *value = NUM_PAI_KERNEL; + return 0; + } if (ctr >= ALL_COUNTER) return -1; *value = 0; @@ -509,9 +779,9 @@ int perf_read_ctr(enum ctr_e ctr, uint64_t *value) if (ec != sizeof(val)) { eprint("Read() on perf file descriptor failed with errno=%d [%s]\n", errno, strerror(errno)); + rc = -1; } else { *value += val; - rc = 0; } } endforeachcpu(); @@ -525,7 +795,34 @@ int perf_ecc_supported(void) } int perf_ctr_state(enum ctr_e ctr) { - if (ctr < ALL_COUNTER) + if (ctr < NUM_COUNTER) return ctr_state[ctr]; return UNSUPPORTED; } + + +int perf_read_pai_ctr(unsigned int ctrnum, int user, uint64_t *value) +{ + struct percpucounter *pcpu; + unsigned int maxctr; + int *arr, ec, rc = 0; + uint64_t val; + + *value = 0; + maxctr = user ? NUM_PAI_USER : NUM_PAI_KERNEL; + if (ctrnum >= maxctr) + return -1; + foreachcpu(pcpu) { + arr = user ? pcpu->pai_user : pcpu->pai_kernel; + ec = read(arr[ctrnum], &val, sizeof(val)); + if (ec != sizeof(val)) { + eprint("Read() on perf file descriptor failed with errno=%d [%s]\n", + errno, strerror(errno)); + rc = -1; + } else { + *value += val; + } + } + endforeachcpu(); + return rc; +}