/* * Copyright (C) 2013-2020 Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * This code is a complete clean re-write of the stress tool by * Colin Ian King and attempts to be * backwardly compatible with the stress tool by Amos Waterland * but has more stress tests and more * functionality. * */ #include "stress-ng.h" static const stress_help_t help[] = { { NULL, "ipsec-mb N", "start N workers exercising the IPSec MB encoding" }, { NULL, "ipsec-mb-ops N", "stop after N ipsec bogo encoding operations" }, { NULL, "ipsec-mb-feature F","specify CPU feature F" }, { NULL, NULL, NULL } }; static int stress_set_ipsec_mb_feature(const char *opt); static const stress_opt_set_func_t opt_set_funcs[] = { { OPT_ipsec_mb_feature, stress_set_ipsec_mb_feature }, }; #if defined(HAVE_INTEL_IPSEC_MB_H) && \ defined(HAVE_LIB_IPSEC_MB) && \ defined(STRESS_ARCH_X86) && \ (defined(__x86_64__) || defined(__x86_64)) && \ defined(IMB_FEATURE_SSE4_2) && \ defined(IMB_FEATURE_CMOV) && \ defined(IMB_FEATURE_AESNI) && \ defined(IMB_FEATURE_AVX) && \ defined(IMB_FEATURE_AVX2) && \ defined(IMB_FEATURE_AVX512_SKX) #define FEATURE_SSE (IMB_FEATURE_SSE4_2 | IMB_FEATURE_CMOV | IMB_FEATURE_AESNI) #define FEATURE_AVX (IMB_FEATURE_AVX | IMB_FEATURE_CMOV | IMB_FEATURE_AESNI) #define FEATURE_AVX2 (FEATURE_AVX | IMB_FEATURE_AVX2) #define FEATURE_AVX512 (FEATURE_AVX2 | IMB_FEATURE_AVX512_SKX) typedef struct { const int features; const char *name; void (*init_func)(MB_MGR *p_mgr); } stress_init_mb_t; static stress_init_mb_t init_mb[] = { { FEATURE_SSE, "sse", init_mb_mgr_sse }, { FEATURE_AVX, "avx", init_mb_mgr_avx }, { FEATURE_AVX2, "avx2", init_mb_mgr_avx2 }, { FEATURE_AVX512, "avx512", init_mb_mgr_avx512 }, }; static int stress_set_ipsec_mb_feature(const char *opt) { size_t i; for (i = 0; i < SIZEOF_ARRAY(init_mb); i++) { if (!strcmp(opt, init_mb[i].name)) { int ipsec_mb_feature = init_mb[i].features; return stress_set_setting("ipsec-mb-feature", TYPE_ID_INT, &ipsec_mb_feature); } } (void)fprintf(stderr, "invalid ipsec-mb-feature '%s', allowed options are:", opt); for (i = 0; i < SIZEOF_ARRAY(init_mb); i++) { (void)fprintf(stderr, " %s", init_mb[i].name); } (void)fprintf(stderr, "\n"); return -1; } static const char *stress_get_ipsec_mb_feature(const int feature) { size_t i; for (i = 0; i < SIZEOF_ARRAY(init_mb); i++) { if (init_mb[i].features == feature) return init_mb[i].name; } return "(unknown)"; } /* * stress_ipsec_mb_features() * get list of CPU feature bits */ static int stress_ipsec_mb_features(const stress_args_t *args, MB_MGR *p_mgr) { int features; features = p_mgr->features; if (args->instance == 0) { char str[128] = ""; if ((features & FEATURE_SSE) == FEATURE_SSE) strcat(str, " sse"); if ((features & FEATURE_AVX) == FEATURE_AVX) strcat(str, " avx"); if ((features & FEATURE_AVX2) == FEATURE_AVX2) strcat(str, " avx2"); if ((features & FEATURE_AVX512) == FEATURE_AVX512) strcat(str, " avx512"); pr_inf("%s: features:%s\n", args->name, str); } return features; } /* * stress_ipsec_mb_supported() * check if ipsec_mb is supported */ static int stress_ipsec_mb_supported(const char *name) { /* Intel CPU? */ if (!stress_cpu_is_x86()) { pr_inf("%s stressor will be skipped, " "not a recognised Intel CPU\n", name); return -1; } return 0; } /* * stress_rnd_fill() * fill uint32_t aligned buf with n bytes of random data */ static void stress_rnd_fill(uint8_t *buf, size_t n) { register uint32_t *ptr = (uint32_t *)buf; register uint32_t *end = (uint32_t *)(buf + n); while (ptr < end) *(ptr++) = stress_mwc32(); } /* * stress_job_empty() * empty job queue */ static inline void stress_job_empty(struct MB_MGR *mb_mgr) { while (IMB_FLUSH_JOB(mb_mgr)) ; } static inline struct JOB_AES_HMAC *stress_job_get_next(struct MB_MGR *mb_mgr) { struct JOB_AES_HMAC *job; job = IMB_GET_NEXT_JOB(mb_mgr); (void)memset(job, 0, sizeof(*job)); return job; } /* * stress_job_check_status() * check if jobs has completed, report error if not */ static void stress_job_check_status( const stress_args_t *args, const char *name, struct JOB_AES_HMAC *job, int *jobs_done) { (*jobs_done)++; if (job->status != STS_COMPLETED) { pr_err("%s: %s: job not completed\n", args->name, name); } } /* * stress_jobs_done() * check if all the jobs have completed */ static void stress_jobs_done( const stress_args_t *args, const char *name, const int jobs, const int jobs_done) { if (jobs_done != jobs) pr_err("%s: %s: only processed %d of %d jobs\n", args->name, name, jobs_done, jobs); } static void stress_sha( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; const int sha_digest_size = 64; struct JOB_AES_HMAC *job; uint8_t padding[16]; const size_t alloc_len = sha_digest_size + (sizeof(padding) * 2); uint8_t *auth; uint8_t auth_data[alloc_len * jobs]; static const char name[] = "sha"; stress_job_empty(mb_mgr); for (auth = auth_data, j = 0; j < jobs; j++, auth += alloc_len) { job = stress_job_get_next(mb_mgr); job->cipher_direction = ENCRYPT; job->chain_order = HASH_CIPHER; job->auth_tag_output = auth + sizeof(padding); job->auth_tag_output_len_in_bytes = sha_digest_size; job->src = data; job->msg_len_to_hash_in_bytes = data_len; job->cipher_mode = NULL_CIPHER; job->hash_alg = PLAIN_SHA_512; job->user_data = auth; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_des( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; struct JOB_AES_HMAC *job; uint8_t encoded[jobs * data_len] ALIGNED(16); uint8_t k[32] ALIGNED(16); uint8_t iv[16] ALIGNED(16); uint32_t enc_keys[15*4] ALIGNED(16); uint32_t dec_keys[15*4] ALIGNED(16); uint8_t *dst; static const char name[] = "des"; stress_rnd_fill(k, sizeof(k)); stress_rnd_fill(iv, sizeof(iv)); stress_job_empty(mb_mgr); IMB_AES_KEYEXP_256(mb_mgr, k, enc_keys, dec_keys); for (dst = encoded, j = 0; j < jobs; j++, dst += data_len) { job = stress_job_get_next(mb_mgr); job->cipher_direction = ENCRYPT; job->chain_order = CIPHER_HASH; job->src = data; job->dst = dst; job->cipher_mode = CBC; job->aes_enc_key_expanded = enc_keys; job->aes_dec_key_expanded = dec_keys; job->aes_key_len_in_bytes = sizeof(k); job->iv = iv; job->iv_len_in_bytes = sizeof(iv); job->cipher_start_src_offset_in_bytes = 0; job->msg_len_to_cipher_in_bytes = data_len; job->user_data = dst; job->user_data2 = (void *)((uint64_t)j); job->hash_alg = NULL_HASH; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_cmac( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; struct JOB_AES_HMAC *job; uint8_t key[16] ALIGNED(16); uint32_t expkey[4 * 15] ALIGNED(16); uint32_t dust[4 * 15] ALIGNED(16); uint32_t skey1[4], skey2[4]; uint8_t output[jobs * data_len] ALIGNED(16); static const char name[] = "cmac"; uint8_t *dst; stress_rnd_fill(key, sizeof(key)); IMB_AES_KEYEXP_128(mb_mgr, key, expkey, dust); IMB_AES_CMAC_SUBKEY_GEN_128(mb_mgr, expkey, skey1, skey2); stress_job_empty(mb_mgr); for (dst = output, j = 0; j < jobs; j++, dst += 16) { job = stress_job_get_next(mb_mgr); job->cipher_direction = ENCRYPT; job->chain_order = HASH_CIPHER; job->cipher_mode = NULL_CIPHER; job->hash_alg = AES_CMAC; job->src = data; job->hash_start_src_offset_in_bytes = 0; job->msg_len_to_hash_in_bytes = data_len; job->auth_tag_output = dst; job->auth_tag_output_len_in_bytes = 16; job->u.CMAC._key_expanded = expkey; job->u.CMAC._skey1 = skey1; job->u.CMAC._skey2 = skey2; job->user_data = dst; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_ctr( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; struct JOB_AES_HMAC *job; uint8_t encoded[jobs * data_len] ALIGNED(16); uint8_t key[32] ALIGNED(16); uint8_t iv[12] ALIGNED(16); /* 4 byte nonce + 8 byte IV */ uint32_t expkey[4 * 15] ALIGNED(16); uint32_t dust[4 * 15] ALIGNED(16); uint8_t *dst; static const char name[] = "ctr"; stress_rnd_fill(key, sizeof(key)); stress_rnd_fill(iv, sizeof(iv)); IMB_AES_KEYEXP_256(mb_mgr, key, expkey, dust); stress_job_empty(mb_mgr); for (dst = encoded, j = 0; j < jobs; j++, dst += data_len) { job = stress_job_get_next(mb_mgr); job->cipher_direction = ENCRYPT; job->chain_order = CIPHER_HASH; job->cipher_mode = CNTR; job->hash_alg = NULL_HASH; job->src = data; job->dst = dst; job->aes_enc_key_expanded = expkey; job->aes_dec_key_expanded = expkey; job->aes_key_len_in_bytes = sizeof(key); job->iv = iv; job->iv_len_in_bytes = sizeof(iv); job->cipher_start_src_offset_in_bytes = 0; job->msg_len_to_cipher_in_bytes = data_len; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_hmac_md5( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; size_t i; struct JOB_AES_HMAC *job; const int digest_size = 16; const int block_size = 64; uint8_t key[block_size] ALIGNED(16); uint8_t buf[block_size] ALIGNED(16); uint8_t ipad_hash[digest_size] ALIGNED(16); uint8_t opad_hash[digest_size] ALIGNED(16); uint8_t output[jobs * digest_size] ALIGNED(16); uint8_t *dst; static const char name[] = "hmac_md5"; stress_rnd_fill(key, sizeof(key)); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x36; IMB_MD5_ONE_BLOCK(mb_mgr, buf, ipad_hash); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x5c; IMB_MD5_ONE_BLOCK(mb_mgr, buf, opad_hash); stress_job_empty(mb_mgr); for (dst = output, j = 0; j < jobs; j++, dst += digest_size) { job = stress_job_get_next(mb_mgr); job->aes_enc_key_expanded = NULL; job->aes_dec_key_expanded = NULL; job->cipher_direction = ENCRYPT; job->chain_order = HASH_CIPHER; job->dst = NULL; job->aes_key_len_in_bytes = 0; job->auth_tag_output = dst; job->auth_tag_output_len_in_bytes = digest_size; job->iv = NULL; job->iv_len_in_bytes = 0; job->src = data; job->cipher_start_src_offset_in_bytes = 0; job->msg_len_to_cipher_in_bytes = 0; job->hash_start_src_offset_in_bytes = 0; job->msg_len_to_hash_in_bytes = data_len; job->u.HMAC._hashed_auth_key_xor_ipad = ipad_hash; job->u.HMAC._hashed_auth_key_xor_opad = opad_hash; job->cipher_mode = NULL_CIPHER; job->hash_alg = MD5; job->user_data = dst; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_hmac_sha1( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; size_t i; struct JOB_AES_HMAC *job; const int digest_size = 20; const int block_size = 64; uint8_t key[block_size] ALIGNED(16); uint8_t buf[block_size] ALIGNED(16); uint8_t ipad_hash[digest_size] ALIGNED(16); uint8_t opad_hash[digest_size] ALIGNED(16); uint8_t output[jobs * digest_size] ALIGNED(16); uint8_t *dst; static const char name[] = "hmac_sha1"; stress_rnd_fill(key, sizeof(key)); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x36; IMB_MD5_ONE_BLOCK(mb_mgr, buf, ipad_hash); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x5c; IMB_MD5_ONE_BLOCK(mb_mgr, buf, opad_hash); stress_job_empty(mb_mgr); for (dst = output, j = 0; j < jobs; j++, dst += digest_size) { job = stress_job_get_next(mb_mgr); job->aes_enc_key_expanded = NULL; job->aes_dec_key_expanded = NULL; job->cipher_direction = ENCRYPT; job->chain_order = HASH_CIPHER; job->dst = NULL; job->aes_key_len_in_bytes = 0; job->auth_tag_output = dst; job->auth_tag_output_len_in_bytes = digest_size; job->iv = NULL; job->iv_len_in_bytes = 0; job->src = data; job->cipher_start_src_offset_in_bytes = 0; job->msg_len_to_cipher_in_bytes = 0; job->hash_start_src_offset_in_bytes = 0; job->msg_len_to_hash_in_bytes = data_len; job->u.HMAC._hashed_auth_key_xor_ipad = ipad_hash; job->u.HMAC._hashed_auth_key_xor_opad = opad_hash; job->cipher_mode = NULL_CIPHER; job->hash_alg = SHA1; job->user_data = dst; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } static void stress_hmac_sha512( const stress_args_t *args, struct MB_MGR *mb_mgr, const uint8_t *data, const size_t data_len, const int jobs) { int j, jobs_done = 0; size_t i; struct JOB_AES_HMAC *job; const int digest_size = SHA512_DIGEST_SIZE_IN_BYTES; const int block_size = SHA_512_BLOCK_SIZE; uint8_t rndkey[block_size] ALIGNED(16); uint8_t key[block_size] ALIGNED(16); uint8_t buf[block_size] ALIGNED(16); uint8_t ipad_hash[digest_size] ALIGNED(16); uint8_t opad_hash[digest_size] ALIGNED(16); uint8_t output[jobs * digest_size] ALIGNED(16); uint8_t *dst; static const char name[] = "hmac_sha512"; stress_rnd_fill(rndkey, sizeof(rndkey)); IMB_SHA512(mb_mgr, rndkey, SHA_512_BLOCK_SIZE, key); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x36; IMB_SHA512_ONE_BLOCK(mb_mgr, buf, ipad_hash); for (i = 0; i < sizeof(key); i++) buf[i] = key[i] ^ 0x5c; IMB_SHA512_ONE_BLOCK(mb_mgr, buf, opad_hash); stress_job_empty(mb_mgr); for (dst = output, j = 0; j < jobs; j++, dst += digest_size) { job = stress_job_get_next(mb_mgr); job->aes_enc_key_expanded = NULL; job->aes_dec_key_expanded = NULL; job->cipher_direction = ENCRYPT; job->chain_order = HASH_CIPHER; job->dst = NULL; job->aes_key_len_in_bytes = 0; job->auth_tag_output = dst; job->auth_tag_output_len_in_bytes = digest_size; job->iv = NULL; job->iv_len_in_bytes = 0; job->src = data; job->cipher_start_src_offset_in_bytes = 0; job->msg_len_to_cipher_in_bytes = 0; job->hash_start_src_offset_in_bytes = 0; job->msg_len_to_hash_in_bytes = data_len; job->u.HMAC._hashed_auth_key_xor_ipad = ipad_hash; job->u.HMAC._hashed_auth_key_xor_opad = opad_hash; job->cipher_mode = NULL_CIPHER; job->hash_alg = SHA_512; job->user_data = dst; job = IMB_SUBMIT_JOB(mb_mgr); if (job) stress_job_check_status(args, name, job, &jobs_done); } while ((job = IMB_FLUSH_JOB(mb_mgr)) != NULL) stress_job_check_status(args, name, job, &jobs_done); stress_jobs_done(args, name, jobs, jobs_done); stress_job_empty(mb_mgr); } /* * stress_ipsec_mb() * stress Intel ipsec_mb instruction */ static int stress_ipsec_mb(const stress_args_t *args) { MB_MGR *p_mgr = NULL; int features; uint8_t data[8192] ALIGNED(64); const size_t n_features = SIZEOF_ARRAY(init_mb); double t[n_features]; size_t i; uint64_t count = 0; bool got_features = false; int ipsec_mb_feature = ~0; p_mgr = alloc_mb_mgr(0); if (!p_mgr) { pr_inf("%s: failed to setup Intel IPSec MB library, skipping\n", args->name); return EXIT_NO_RESOURCE; } if (imb_get_version() < IMB_VERSION(0, 51, 0)) { pr_inf("%s: version %s of Intel IPSec MB library is too low, skipping\n", args->name, imb_get_version_str()); free_mb_mgr(p_mgr); return EXIT_NOT_IMPLEMENTED; } features = stress_ipsec_mb_features(args, p_mgr); for (i = 0; i < n_features; i++) { t[i] = 0.0; if ((init_mb[i].features & features) == init_mb[i].features) { got_features = true; break; } } if (!got_features) { pr_inf("%s: not enough CPU features to support Intel IPSec MB library, skipping\n", args->name); free_mb_mgr(p_mgr); return EXIT_NOT_IMPLEMENTED; } if (stress_get_setting("ipsec-mb-feature", &ipsec_mb_feature)) { const char *feature_name = stress_get_ipsec_mb_feature(ipsec_mb_feature); if ((ipsec_mb_feature & features) != ipsec_mb_feature) { pr_inf("%s: requested ipsec-mb-feature feature '%s' is not supported, skipping\n", args->name, feature_name); free_mb_mgr(p_mgr); return EXIT_NOT_IMPLEMENTED; } features = ipsec_mb_feature; if (args->instance == 0) pr_inf("%s: using just feature '%s'\n", args->name, feature_name); } stress_rnd_fill(data, sizeof(data)); do { for (i = 0; i < n_features; i++) { if ((init_mb[i].features & features) == init_mb[i].features) { double t1, t2; t1 = stress_time_now(); init_mb[i].init_func(p_mgr); stress_cmac(args, p_mgr, data, sizeof(data), 1); stress_ctr(args, p_mgr, data, sizeof(data), 1); stress_des(args, p_mgr, data, sizeof(data), 1); stress_hmac_md5(args, p_mgr, data, sizeof(data), 1); stress_hmac_sha1(args, p_mgr, data, sizeof(data), 1); stress_hmac_sha512(args, p_mgr, data, sizeof(data), 1); stress_sha(args, p_mgr, data, sizeof(data), 1); t2 = stress_time_now(); t[i] += (t2 - t1); } } count++; inc_counter(args); } while (keep_stressing()); for (i = 0; i < n_features; i++) { if (((init_mb[i].features & features) == init_mb[i].features) && (t[i] > 0.0)) pr_dbg("%s: %s %.3f bogo/ops per second\n", args->name, init_mb[i].name, (float)count / t[i]); } free_mb_mgr(p_mgr); return EXIT_SUCCESS; } stressor_info_t stress_ipsec_mb_info = { .stressor = stress_ipsec_mb, .supported = stress_ipsec_mb_supported, .opt_set_funcs = opt_set_funcs, .class = CLASS_CPU, .help = help }; #else static int stress_set_ipsec_mb_feature(const char *opt) { (void)opt; pr_inf("option --ipsec-mb-feature not supported on this system.\n"); return -1; } static int stress_ipsec_mb_supported(const char *name) { pr_inf("%s: stressor will be skipped, CPU " "needs to be an x86-64 and a recent IPSec MB library " "is required.\n", name); return -1; } stressor_info_t stress_ipsec_mb_info = { .stressor = stress_not_implemented, .supported = stress_ipsec_mb_supported, .opt_set_funcs = opt_set_funcs, .class = CLASS_CPU, .help = help }; #endif