diff --git a/README.md b/README.md index e2d05e3..bf98ec3 100644 --- a/README.md +++ b/README.md @@ -121,18 +121,29 @@ lock/unlock pairs are reported as a performance measurement ## pkeyread -The `pkeyread` test repeatedly calls the [PEM_read_bio_PrivateKey()](https://docs.openssl.org/master/man3/PEM_read_bio_PrivateKey/) function on a -memory BIO with a private key of desired type, when it is running in pem mode -(-f pem). If test is running in der mode (-f der) it calls to -[d2i_PrivateKey_ex()](https://docs.openssl.org/master/man3/d2i_PrivateKey/) function to repeatedly read private key of desired type. -It does 10000 repetitions divided evenly among each thread. The number of -threads to use is provided by option `-t`. The test reports average time per -call. Use option `-k` to select key type for benchmark. The list of keys for -testing is as follows: dh, dhx, dsa, ec, rsa, xkey. To run benchmark for all -keys and formats using 4 threads run pkeyread as follows: +The `pkeyread` test benchmarks reading of private keys of the specified type +in the specified format. If PEM format is specified (`-f pem`), +the [`PEM_read_bio_PrivateKey()`](https://docs.openssl.org/master/man3/PEM_read_bio_PrivateKey/) +function is called repeatedly on a memory BIO with a private key +of desired type; in case of DER format specification (`-f der`), +[`d2i_PrivateKey_ex()`](https://docs.openssl.org/master/man3/d2i_PrivateKey/) +is called repeatedly to read private key of the desired type. +The calls are performed repeatedly until the timeout (specified in the `-T` +option, 5 seconds by default) is reached. +The number of threads to use is provided by the only positional option. +By default, the test reports average time per call. If `-t` ("terse output") +option is specified, only the number is printed, without key type and format. +If `-v` ("verbose") option is specified, additional information is printed +for each run: median, minimum, maximum time among threads, as well as standard +deviation. +If `-b` ("bind") option is provided, thread affinity is set to the threads +so the available cores are assigned in a round robin manner. +The mandatory option `-k` selects the key type for benchmark. The list of keys +for testing is as follows: dh, dhx, dsa, ec, rsa, x25519. To run benchmark +for all keys and formats using 4 threads, run `pkeyread` as follows: ```sh -./pkeyread -f all -k all -t 4 +./pkeyread -f all -k all 4 ``` ## evp_setpeer diff --git a/source/evp_fetch.c b/source/evp_fetch.c index 70b35b7..578c0e6 100644 --- a/source/evp_fetch.c +++ b/source/evp_fetch.c @@ -61,7 +61,7 @@ size_t *counts; OSSL_TIME max_time; -int err = 0; +int error = 0; int pq = 0; static int threadcount; @@ -194,7 +194,7 @@ void do_fetch(size_t num) fetch_alg = exclusive_fetch_alg; } - if (err == 1) + if (error == 1) return; switch (j) { @@ -203,7 +203,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (md == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_MD_free(md); @@ -214,7 +214,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (cph == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_CIPHER_free(cph); @@ -225,7 +225,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (kdf == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_KDF_free(kdf); @@ -236,7 +236,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (mac == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_MAC_free(mac); @@ -247,7 +247,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (rnd == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_RAND_free(rnd); @@ -258,7 +258,7 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (kem == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_KEM_free(kem); @@ -269,14 +269,14 @@ void do_fetch(size_t num) fetch_entries[j].propq); if (sig == NULL) { fprintf(stderr, "Failed to fetch %s\n", fetch_alg); - err = 1; + error = 1; return; } EVP_SIGNATURE_free(sig); break; } default: - err = 1; + error = 1; return; } counts[num]++; @@ -372,7 +372,7 @@ int main(int argc, char *argv[]) goto out; } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/evp_setpeer.c b/source/evp_setpeer.c index 302db5d..5bb0c78 100644 --- a/source/evp_setpeer.c +++ b/source/evp_setpeer.c @@ -24,7 +24,7 @@ #define RUN_TIME 5 -int err = 0; +int error = 0; size_t num_calls; static int threadcount; @@ -42,13 +42,13 @@ void do_setpeer(size_t num) pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL); if (pkey_ctx == NULL) { - err = 1; + error = 1; printf("Failed to create pkey_ctx\n"); return; } if (EVP_PKEY_derive_init(pkey_ctx) <= 0) { - err = 1; + error = 1; printf("Failed to init pkey_ctx\n"); EVP_PKEY_CTX_free(pkey_ctx); return; @@ -58,7 +58,7 @@ void do_setpeer(size_t num) do { if (EVP_PKEY_derive_set_peer(pkey_ctx, pkey) <= 0) { - err = 1; + error = 1; break; } counts[num]++; @@ -97,7 +97,7 @@ static double get_avcalltime(void) static void report_result(int key_id, int terse) { - if (err) { + if (error) { fprintf(stderr, "Error during test of %s\n", sample_names[key_id]); exit(EXIT_FAILURE); @@ -228,7 +228,7 @@ int main(int argc, char *argv[]) EVP_PKEY_free(pkey); } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/handshake.c b/source/handshake.c index d64b034..a33d6f9 100644 --- a/source/handshake.c +++ b/source/handshake.c @@ -25,7 +25,7 @@ #define RUN_TIME 5 -int err = 0; +int error = 0; typedef enum { INIT_LIB_CTX, @@ -102,7 +102,7 @@ static void do_handshake(size_t num) } while (time.t < max_time.t); if (!ret) - err = 1; + error = 1; } static void do_handshake_ossl_lib_ctx_per_thread(size_t num) @@ -117,7 +117,7 @@ static void do_handshake_ossl_lib_ctx_per_thread(size_t num) libctx = OSSL_LIB_CTX_new(); if (libctx == NULL) { fprintf(stderr, "%s:%d: Failed to create ossl lib context\n", __FILE__, __LINE__); - err = 1; + error = 1; return; } @@ -132,7 +132,7 @@ static void do_handshake_ossl_lib_ctx_per_thread(size_t num) privkey)) { ERR_print_errors_fp(stderr); fprintf(stderr, "%s:%d: Failed to create SSL_CTX pair\n", __FILE__, __LINE__); - err = 1; + error = 1; return; } } @@ -157,7 +157,7 @@ static void do_handshake_ossl_lib_ctx_per_thread(size_t num) SSL_CTX_free(lcctx); if (!ret) - err = 1; + error = 1; OSSL_LIB_CTX_free(libctx); } @@ -187,7 +187,7 @@ static void do_handshake_ctx_pool(size_t num) privkey)) { ERR_print_errors_fp(stderr); fprintf(stderr, "%s:%d: Failed to create SSL_CTX pair\n", __FILE__, __LINE__); - err = 1; + error = 1; return; } } @@ -203,7 +203,7 @@ static void do_handshake_ctx_pool(size_t num) privkey)) { ERR_print_errors_fp(stderr); fprintf(stderr, "%s:%d: Failed to create SSL_CTX pair\n", __FILE__, __LINE__); - err = 1; + error = 1; return; } } @@ -230,7 +230,7 @@ static void do_handshake_ctx_pool(size_t num) } if (!ret) - err = 1; + error = 1; } static void free_ctx_pool() @@ -482,7 +482,7 @@ int main(int argc, char * const argv[]) goto err; }; - if (err) { + if (error) { printf("Error during test\n"); goto err; } diff --git a/source/newrawkey.c b/source/newrawkey.c index 3b380a9..ed9e112 100644 --- a/source/newrawkey.c +++ b/source/newrawkey.c @@ -37,7 +37,7 @@ enum { } algorithm = ALGO_X25519; const char *alg_name = "X25519"; -int err = 0; +int error = 0; static unsigned char key_x25519[32] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, @@ -377,7 +377,7 @@ void do_newrawkey(size_t num) sizeof(key_data)); #endif if (pkey == NULL) - err = 1; + error = 1; else EVP_PKEY_free(pkey); counts[num]++; @@ -451,7 +451,7 @@ int main(int argc, char *argv[]) goto out; } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/perflib/err.c b/source/perflib/err.c index 3a3164d..c4c4088 100644 --- a/source/perflib/err.c +++ b/source/perflib/err.c @@ -7,21 +7,52 @@ * https://www.openssl.org/source/license.html */ +#include #include #include #include +#include + const char *progname; +static ossl_inline void * +get_progname(void) +{ + if (progname != NULL) + return progname; + +#if defined(__GLIBC__) + if (program_invocation_name && program_invocation_name[0]) + return program_invocation_name; +#endif + + return NULL; +} + void vwarnx(const char *fmt, va_list ap) { - if (progname != NULL) - fprintf(stderr, "%s: ", progname); + if (get_progname() != NULL) + fprintf(stderr, "%s: ", get_progname()); vfprintf(stderr, fmt, ap); putc('\n', stderr); } +void +vwarn(const char *fmt, va_list ap) +{ + int saved_errno = errno; + + if (get_progname() != NULL) + fprintf(stderr, "%s: ", get_progname()); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + + errno = saved_errno; + perror(NULL); +} + void errx(int status, const char *fmt, ...) { @@ -33,6 +64,17 @@ errx(int status, const char *fmt, ...) exit(status); } +void +err(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + exit(status); +} + void warnx(const char *fmt, ...) { @@ -42,3 +84,13 @@ warnx(const char *fmt, ...) vwarnx(fmt, ap); va_end(ap); } + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); +} diff --git a/source/perflib/err.h b/source/perflib/err.h index 0bb7a53..b9721ad 100644 --- a/source/perflib/err.h +++ b/source/perflib/err.h @@ -11,13 +11,49 @@ # define OSSL_PERFLIB_ERR_H # pragma once -#include +# include + +# if !defined(_WIN32) + +# include + +# else /* _WIN32 */ + +# include extern const char *progname; extern void vwarnx(const char *, va_list); +extern void vwarn(const char *, va_list); + extern void errx(int, const char *, ...); +extern void err(int, const char *, ...); + extern void warnx(const char *, ...); +extern void warn(const char *, ...); + +# endif /* !_WIN32 */ + +# define WARN(...) \ + do { \ + fprintf(stderr, "%s:%d(%s): ", __FILE__, __LINE__, __FUNCTION__); \ + warn(__VA_ARGS__); \ + } while (0) +# define WARNX(...) \ + do { \ + fprintf(stderr, "%s:%d(%s): ", __FILE__, __LINE__, __FUNCTION__); \ + warnx(__VA_ARGS__); \ + } while (0) +# define ERR(...) \ + do { \ + fprintf(stderr, "%s:%d(%s): ", __FILE__, __LINE__, __FUNCTION__); \ + err(__VA_ARGS__); \ + } while (0) +# define ERRX(...) \ + do { \ + fprintf(stderr, "%s:%d(%s): ", __FILE__, __LINE__, __FUNCTION__); \ + errx(__VA_ARGS__); \ + } while (0) #endif diff --git a/source/perflib/perfhelper.c b/source/perflib/perfhelper.c index 1cbad59..67e25b5 100644 --- a/source/perflib/perfhelper.c +++ b/source/perflib/perfhelper.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/source/perflib/perflib.h b/source/perflib/perflib.h index e41f816..1048a7a 100644 --- a/source/perflib/perflib.h +++ b/source/perflib/perflib.h @@ -21,6 +21,7 @@ # include typedef HANDLE thread_t; +typedef DWORD_PTR affinity_t; # define strcasecmp(_a, _b) _stricmp((_a), (_b)) @@ -29,6 +30,7 @@ typedef HANDLE thread_t; # include typedef pthread_t thread_t; +typedef unsigned long affinity_t; # endif @@ -37,10 +39,60 @@ struct thread_arg_st { size_t num; }; -int perflib_run_thread(thread_t *t, struct thread_arg_st *arg); +/** + * A callback that allows setting CPU affinity for the threads being run. + * Gets the set of available CPUs in the cpu_set argument and expected + * to update it in accordance with the information provided in num and arg + * arguments. + * + * Currently supported only on glibc on Linux (because of a non-privileged + * pthread_attr_setaffinity_np, with a maximum of 1024 CPUs) and Windows + * (with a limitation of using only the initial process group). + * + * @param[in,out] cpu_set On entering, contains the set of CPUs available + * to the test. The callback is supposed + * to update the set in accordance + * with the information provided in num, cnt, + * and arg parameters. + * @param[in] cpu_set_bits Size of cpu_set_bits, in bits. + * @param[in] num Index of a thread index being run, + * counted from 0. + * @param[in] cnt Total count of available CPUs + * (popcnt(cpu_set_bits)). + * @param arg An opaque pointer that a caller has provided + * along the callback. + * @return 1 on success, 0 on error. + */ +typedef int (*perflib_affinity_fn)(affinity_t *cpu_set, size_t cpu_set_bits, + size_t num, size_t cnt, void *arg); + +/** + * A simple affinity callback that assigns each thread a single CPU + * in a round robin fashion. arg must be NULL. + */ +int perflib_roundrobin_affinity(affinity_t *cpu_set_bits, size_t cpu_set_size, + size_t num, size_t cnt, void *arg); + +int perflib_run_thread_ex(thread_t *t, struct thread_arg_st *arg, + perflib_affinity_fn affinity_cb, + void *affinity_cb_arg); +static ossl_unused ossl_inline int +perflib_run_thread(thread_t *t, struct thread_arg_st *arg) +{ + return perflib_run_thread_ex(t, arg, NULL, NULL); +} int perflib_wait_for_thread(thread_t thread); -int perflib_run_multi_thread_test(void (*f)(size_t), size_t threadcount, - OSSL_TIME *duration); +int perflib_run_multi_thread_test_ex(void (*f)(size_t), size_t threadcount, + OSSL_TIME *duration, + perflib_affinity_fn affinity_cb, + void *affinity_cb_arg); +static ossl_unused ossl_inline int +perflib_run_multi_thread_test(void (*f)(size_t), size_t threadcount, + OSSL_TIME *duration) +{ + return perflib_run_multi_thread_test_ex(f, threadcount, duration, + NULL, NULL); +} char *perflib_mk_file_path(const char *dir, const char *file); char *perflib_glue_strings(const char *list[], size_t *out_len); diff --git a/source/perflib/threads.c b/source/perflib/threads.c index a6cd1dc..8cf3a76 100644 --- a/source/perflib/threads.c +++ b/source/perflib/threads.c @@ -7,10 +7,88 @@ * https://www.openssl.org/source/license.html */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include + +#include "perflib/err.h" #include "perflib/perflib.h" +#define OSSL_NELEM(x) (sizeof(x)/sizeof((x)[0])) + +/** affinity_t-typed value with nth bit set. */ +#define AFFINITY_BIT(n) ((affinity_t)1U << (n)) + +#if defined(__GNUC__) + +static ossl_inline unsigned int popcount(affinity_t a) +{ + return __builtin_popcountl(a); +} + +#else /* !__GNUC__ */ + +static ossl_inline unsigned int popcount(affinity_t a) +{ + unsigned int ret = 0; + + for (size_t i = 0; i < sizeof(a) * CHAR_BIT; i++) + ret += (a & AFFINITY_BIT(i)) != 0; + + return ret; +} + +#endif /* __GNUC__ */ + +int perflib_roundrobin_affinity(affinity_t *cpu_set_bits, size_t cpu_set_size, + size_t num, size_t cnt, void *arg) +{ + enum { BITS_PER_ELEM = sizeof(cpu_set_bits[0]) * CHAR_BIT }; + size_t set_cnt = 0; + size_t i; + + if (arg != NULL) { + WARNX("Non-NULL arg"); + + return 0; + } + + /* Finding (num % cnt)th set bit in the provided mask */ + for (i = 0; i < cpu_set_size; i++) { + if (cpu_set_bits[i / BITS_PER_ELEM] & AFFINITY_BIT(i % BITS_PER_ELEM)) + set_cnt++; + + if (set_cnt == (num % cnt + 1)) + break; + } + + if (set_cnt != (num % cnt + 1)) { + WARNX("Only %zu bits are set in the affinity mask, %zu expected", + set_cnt, num % cnt + 1); + + return 0; + } + + memset(cpu_set_bits, 0, cpu_set_size / CHAR_BIT); + + cpu_set_bits[i / BITS_PER_ELEM] = AFFINITY_BIT(i % BITS_PER_ELEM); + + return 1; +} + #if defined(_WIN32) +struct thread_affinity { + /* + * It is not a "DWORD *", as any sane person would think, but "__int3264", + * which is 32-bit wide on 32-bit systems and 64-bit wide on 64-bit ones. + */ + DWORD_PTR affinity; +}; + static DWORD WINAPI thread_run(LPVOID varg) { struct thread_arg_st *arg = varg; @@ -20,18 +98,80 @@ static DWORD WINAPI thread_run(LPVOID varg) return 0; } -int perflib_run_thread(thread_t *t, struct thread_arg_st *arg) +static int prepare_affinity_args(perflib_affinity_fn affinity_cb, + void *affinity_cb_arg, + struct thread_affinity *ta, + size_t start, size_t count) { - *t = CreateThread(NULL, 0, thread_run, arg, 0, NULL); + HANDLE process; + DWORD_PTR dummy; + unsigned int cnt; + + if (count == 0) + return 1; + + process = GetCurrentProcess(); + /* TODO: support multiple process groups */ + if (!GetProcessAffinityMask(process, &ta[0].affinity, &dummy)) { + WARNX("Error getting process affinity mask: %lu", GetLastError()); + + return 0; + } + + cnt = popcount(ta[0].affinity); + + for (size_t i = 1; i < count; i++) + ta[i].affinity = ta[0].affinity; + + for (size_t i = 0; i < count; i++) { + if (affinity_cb(&ta[i].affinity, sizeof(DWORD_PTR) * CHAR_BIT, + start + i, cnt, affinity_cb_arg) != 1) { + WARNX("Error calling thread affinity callback for thread %zu", + start + i); + + return 0; + } + } + + return 1; +} + +static void cleanup_affinity_arg(struct thread_affinity *ta, + size_t start, size_t count) +{ +} + +static int perflib_run_thread_(thread_t *t, struct thread_arg_st *arg, + struct thread_affinity *ta) +{ + DWORD thread_id; + + *t = CreateThread(NULL, 0, thread_run, arg, CREATE_SUSPENDED, &thread_id); + + if (t == NULL) { + WARNX("Error creating thread %zu: %lu", arg->num, GetLastError()); + } else { + if (!SetThreadAffinityMask(t, ta->affinity)) + WARNX("Error setting thread affinity for thread %zu: %lu", + arg->num, GetLastError()); + if (ResumeThread(t) < 0) + WARNX("Error resuming thread %zu: %lu", arg->num, GetLastError()); + } + return *t != NULL; } + int perflib_wait_for_thread(thread_t thread) { return WaitForSingleObject(thread, INFINITE) == 0; } -#else +#else /* !_WIN32 */ + +struct thread_affinity { + pthread_attr_t attr; +}; static void *thread_run(void *varg) { @@ -42,9 +182,102 @@ static void *thread_run(void *varg) return NULL; } -int perflib_run_thread(thread_t *t, struct thread_arg_st *arg) +# if defined(__linux) && defined(__GLIBC__) +# include +# include +# include +# include + +static int prepare_affinity_args_linux(perflib_affinity_fn affinity_cb, + void *affinity_cb_arg, + struct thread_affinity *ta, + size_t start, size_t count) +{ + cpu_set_t process_affinity; + unsigned int cnt = 0; + + if (!affinity_cb) + return 1; + + /* TODO: support more than 1024 CPUs */ + if (sched_getaffinity(getpid(), sizeof(process_affinity), + &process_affinity) != 0) { + WARN("sched_getaffinity"); + + return 0; + } + + for (size_t i = 0; i < OSSL_NELEM(process_affinity.__bits); i++) + cnt += popcount(process_affinity.__bits[i]); + + for (size_t i = 0; i < count; i++) { + cpu_set_t thread_affinity = process_affinity; + int ret; + + if (affinity_cb(thread_affinity.__bits, + sizeof(thread_affinity) * CHAR_BIT, + start + i, cnt, affinity_cb_arg) != 1) { + WARNX("Error calling thread affinity callback for thread %zu", + start + i); + + return 0; + } + + ret = pthread_attr_setaffinity_np(&ta[i].attr, sizeof(thread_affinity), + &thread_affinity); + + if (ret != 0) { + WARNX("Error setting thread affinity afttribute for thread %zu", + start + i); + + return 0; + } + } + + return 1; +} +# endif /* __linux */ + +static int prepare_affinity_args(perflib_affinity_fn affinity_cb, + void *affinity_cb_arg, + struct thread_affinity *ta, + size_t start, size_t count) +{ + if (count == 0) + return 1; + + for (size_t i = 0; i < count; i++) + pthread_attr_init(&ta[i].attr); + +# if defined(__linux) && defined(__GLIBC__) + return prepare_affinity_args_linux(affinity_cb, affinity_cb_arg, ta, + start, count); +# else /* !(__linux && __GLIBC__) */ + /* + * So far, setting thread affinity is only supported on Linux+glibc on POSIX + * systems. + */ + if (affinity_cb) { + WARNX("Setting thread affinity is not supported in this environment"); + + return 0; + } + + return 1; +# endif +} + +static void cleanup_affinity_arg(struct thread_affinity *ta, + size_t start, size_t count) { - return pthread_create(t, NULL, thread_run, arg) == 0; + for (size_t i = 0; i < count; i++) + pthread_attr_destroy(&ta[i].attr); +} + +int perflib_run_thread_(thread_t *t, struct thread_arg_st *arg, + struct thread_affinity *ta) +{ + return pthread_create(t, &ta->attr, thread_run, arg) == 0; } int perflib_wait_for_thread(thread_t thread) @@ -52,42 +285,87 @@ int perflib_wait_for_thread(thread_t thread) return pthread_join(thread, NULL) == 0; } -#endif +#endif /* _WIN32 */ -int perflib_run_multi_thread_test(void (*f)(size_t), size_t threadcount, - OSSL_TIME *duration) +int perflib_run_thread_ex(thread_t *t, struct thread_arg_st *arg, + perflib_affinity_fn affinity_cb, + void *affinity_cb_arg) +{ + struct thread_affinity ta; + + prepare_affinity_args(affinity_cb, affinity_cb_arg, &ta, arg->num, 1); + + return perflib_run_thread_(t, arg, &ta); +} + +int perflib_run_multi_thread_test_ex(void (*f)(size_t), size_t threadcount, + OSSL_TIME *duration, + perflib_affinity_fn affinity_cb, + void *affinity_cb_arg) { OSSL_TIME start, end; - thread_t *threads; + thread_t *threads = NULL; + int *run_threads = NULL; + struct thread_arg_st *args = NULL; + struct thread_affinity *ta = NULL; size_t i; - struct thread_arg_st *args; + int ret = 0; threads = OPENSSL_malloc(sizeof(*threads) * threadcount); - if (threads == NULL) - return 0; + if (threads == NULL) { + WARN("Could not allocate threads array"); + + goto err; + } + + run_threads = OPENSSL_zalloc(sizeof(*run_threads) * threadcount); + if (run_threads == NULL) { + WARN("Could not allocate run_threads array"); + + goto err; + } args = OPENSSL_malloc(sizeof(*args) * threadcount); if (args == NULL) { - OPENSSL_free(threads); - return 0; + WARN("Could not allocate args array"); + + goto err; } + ta = OPENSSL_malloc(sizeof(*ta) * threadcount); + if (ta == NULL) { + WARN("Could not allocate args array"); + + goto err; + } + if (!prepare_affinity_args(affinity_cb, affinity_cb_arg, + ta, 0, threadcount)) + goto err; + start = ossl_time_now(); + ret = 1; for (i = 0; i < threadcount; i++) { args[i].func = f; args[i].num = i; - perflib_run_thread(&threads[i], &args[i]); + if (!(run_threads[i] = perflib_run_thread_(&threads[i], &args[i], + ta + i))) + ret = 0; } - for (i = 0; i < threadcount; i++) - perflib_wait_for_thread(threads[i]); + for (i = 0; i < threadcount; i++) { + if (run_threads[i]) + perflib_wait_for_thread(threads[i]); + } end = ossl_time_now(); - OPENSSL_free(threads); - OPENSSL_free(args); - *duration = ossl_time_subtract(end, start); - return 1; +err: + OPENSSL_free(ta); + OPENSSL_free(args); + OPENSSL_free(run_threads); + OPENSSL_free(threads); + + return ret; } diff --git a/source/pkeyread.c b/source/pkeyread.c index e04c131..d2afc20 100644 --- a/source/pkeyread.c +++ b/source/pkeyread.c @@ -50,7 +50,7 @@ size_t num_calls; size_t *counts; OSSL_TIME max_time; -int err = 0; +int error = 0; static int threadcount; @@ -69,7 +69,7 @@ static void do_pemread(size_t num) if (sample_id >= SAMPLE_ALL) { fprintf(stderr, "%s no sample key set for test\n", __func__); - err = 1; + error = 1; return; } @@ -80,7 +80,7 @@ static void do_pemread(size_t num) if (pem == NULL) { fprintf(stderr, "%s Cannot create mem BIO [%s PEM]\n", __func__, sample_names[sample_id]); - err = 1; + error = 1; return; } @@ -93,14 +93,14 @@ static void do_pemread(size_t num) if (key == NULL) { fprintf(stderr, "Failed to create key [%s PEM]\n", sample_names[sample_id]); - err = 1; + error = 1; goto end; } EVP_PKEY_free(key); if (BIO_reset(pem) == 0) { fprintf(stderr, "Failed to reset BIO [%s PEM]\n", sample_names[sample_id]); - err = 1; + error = 1; goto end; } @@ -130,7 +130,7 @@ static void do_derread(size_t num) if (sample_id >= SAMPLE_ALL) { fprintf(stderr, "%s no sample key set for test\n", __func__); - err = 1; + error = 1; return; } @@ -144,7 +144,7 @@ static void do_derread(size_t num) if (pkey == NULL) { fprintf(stderr, "%s pkey is NULL [%s DER]\n", __func__, sample_names[sample_id]); - err = 1; + error = 1; goto error; } error: @@ -238,7 +238,7 @@ static void report_result(int key_id, int format_id, int verbosity) { struct call_times times = { 0 }; - if (err) { + if (error) { fprintf(stderr, "Error during test of %s in %s format\n", sample_names[key_id], format_names[format_id]); exit(EXIT_FAILURE); @@ -275,7 +275,8 @@ static void usage(char * const argv[]) fprintf(stderr, "%s -k key_name -f format_name [-t] [-v] [-T time] threadcount\n" "\t-t terse output\n" "\t-v verbose output, includes min, max, stddev, and median times\n" - "\t-T timeout for each test run in seconds, can be fractional" + "\t-T timeout for each test run in seconds, can be fractional\n" + "\t-b Set CPU affinity for the threads (in round robin fashion)\n" "\twhere key_name is one of these: ", argv[0]); fprintf(stderr, "%s", *key_name); do { @@ -303,6 +304,7 @@ int main(int argc, char * const argv[]) int key_id, key_id_min, key_id_max, k; int format_id, format_id_min, format_id_max, f; int verbosity = VERBOSITY_DEFAULT; + int bind_threads = 0; char *key = NULL; char *key_format = NULL; void (*do_f[2])(size_t) = { @@ -313,7 +315,7 @@ int main(int argc, char * const argv[]) key_id = SAMPLE_INVALID; format_id = FORMAT_INVALID; - while ((ch = getopt(argc, argv, "T:k:f:tv")) != -1) { + while ((ch = getopt(argc, argv, "T:k:f:tvb")) != -1) { switch (ch) { case 'T': { double timeout_s; @@ -342,6 +344,9 @@ int main(int argc, char * const argv[]) case 'v': verbosity = VERBOSITY_VERBOSE; break; + case 'b': + bind_threads = 1; + break; } } @@ -396,7 +401,7 @@ int main(int argc, char * const argv[]) counts = OPENSSL_malloc(sizeof(size_t) * threadcount); if (counts == NULL) { - printf("Failed to create counts array\n"); + fprintf(stderr, "Failed to create counts array\n"); return EXIT_FAILURE; } @@ -419,8 +424,10 @@ int main(int argc, char * const argv[]) for (f = format_id_min; f < format_id_max; f++) { sample_id = k; max_time = ossl_time_add(ossl_time_now(), ossl_us2time(timeout_us)); - if (!perflib_run_multi_thread_test(do_f[f], threadcount, &duration)) { - fprintf(stderr, "Failed to run the test %s in %s format]\n", + if (!perflib_run_multi_thread_test_ex(do_f[f], threadcount, + &duration, bind_threads ? perflib_roundrobin_affinity : NULL, + NULL)) { + fprintf(stderr, "Failed to run the test %s in %s format\n", sample_names[k], format_names[f]); OPENSSL_free(counts); return EXIT_FAILURE; diff --git a/source/randbytes.c b/source/randbytes.c index a72881a..85761f3 100644 --- a/source/randbytes.c +++ b/source/randbytes.c @@ -28,7 +28,7 @@ size_t num_calls; size_t *counts; OSSL_TIME max_time; -int err = 0; +int error = 0; static int threadcount; @@ -42,7 +42,7 @@ void do_randbytes(size_t num) do { if (!RAND_bytes(buf, sizeof(buf))) - err = 1; + error = 1; counts[num]++; time = ossl_time_now(); } while (time.t < max_time.t); @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) goto out; } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/rsasign.c b/source/rsasign.c index b683c54..0097eb7 100644 --- a/source/rsasign.c +++ b/source/rsasign.c @@ -26,7 +26,7 @@ #define RUN_TIME 5 -int err = 0; +int error = 0; EVP_PKEY *rsakey = NULL; size_t *counts; @@ -61,7 +61,7 @@ void do_rsasign(size_t num) if (EVP_PKEY_sign_init(ctx) <= 0 || EVP_PKEY_sign(ctx, sig, &siglen, (const unsigned char*)tbs, SHA_DIGEST_LENGTH) <= 0) { - err = 1; + error = 1; break; } counts[num]++; @@ -130,7 +130,7 @@ int main(int argc, char *argv[]) goto out; } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/rwlocks.c b/source/rwlocks.c index 176c4d9..27747d2 100644 --- a/source/rwlocks.c +++ b/source/rwlocks.c @@ -26,7 +26,7 @@ #define RUN_TIME 5 size_t threadcount = 0; -int err = 0; +int error = 0; unsigned long *dataval = NULL; int writers = 0; int readers = 0; @@ -161,7 +161,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (err) { + if (error) { printf("Error during test\n"); return EXIT_FAILURE; } diff --git a/source/ssl_poll_perf.c b/source/ssl_poll_perf.c index 378058d..dbcca81 100644 --- a/source/ssl_poll_perf.c +++ b/source/ssl_poll_perf.c @@ -33,7 +33,6 @@ #ifdef _WIN32 /* Windows */ # include #else /* Linux/Unix */ -# include # include # include # include @@ -54,10 +53,11 @@ #else # include # include "perflib/basename.h" -# include "perflib/err.h" # include "perflib/getopt.h" #endif /* _WIN32 */ +#include "perflib/err.h" + /* * The code here is based on QUIC poll server found in demos/quic/poll-server * in OpenSSL source code repository. Here we take the demo one step further diff --git a/source/sslnew.c b/source/sslnew.c index 4dd815c..598cfaf 100644 --- a/source/sslnew.c +++ b/source/sslnew.c @@ -24,7 +24,7 @@ #define RUN_TIME 5 -int err = 0; +int error = 0; static SSL_CTX *ctx; static int threadcount; @@ -46,7 +46,7 @@ void do_sslnew(size_t num) wbio = BIO_new(BIO_s_mem()); if (s == NULL || rbio == NULL || wbio == NULL) { - err = 1; + error = 1; BIO_free(rbio); BIO_free(wbio); } else { @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) goto out; } - if (err) { + if (error) { printf("Error during test\n"); goto out; } diff --git a/source/writeread.c b/source/writeread.c index de324f4..d276ad2 100644 --- a/source/writeread.c +++ b/source/writeread.c @@ -23,7 +23,7 @@ #define RUN_TIME 5 -int err = 0; +int error = 0; static SSL_CTX *sctx = NULL, *cctx = NULL; static int share_ctx = 1; @@ -57,7 +57,7 @@ static void do_writeread(size_t num) if (!perflib_create_ssl_ctx_pair(smethod, cmethod, 0, 0, &lsctx, &lcctx, cert, privkey)) { fprintf(stderr, "Failed to create SSL_CTX pair\n"); - err = 1; + error = 1; return; } } @@ -68,7 +68,7 @@ static void do_writeread(size_t num) ret &= perflib_create_bare_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE); if (!ret) { - err = 1; + error = 1; return; } @@ -76,18 +76,18 @@ static void do_writeread(size_t num) size_t written = 0; if (SSL_write_ex(clientssl, cbuf, buf_size, &written) <= 0) { fprintf(stderr, "Failed to write data\n"); - err = 1; + error = 1; return; } size_t readbytes; if (SSL_read_ex(serverssl, sbuf, buf_size, &readbytes) <= 0) { fprintf(stderr, "Failed to read data\n"); - err = 1; + error = 1; return; } if (readbytes != written) { fprintf(stderr, "Failed to read %ld bytes, got %ld\n", written, readbytes); - err = 1; + error = 1; return; } counts[num]++; @@ -190,7 +190,7 @@ int main(int argc, char * const argv[]) goto err; } - if (err) { + if (error) { fprintf(stderr, "Error during test\n"); goto err; }