From 0723b8be34503b31dbfc6c877d7a855091c625b4 Mon Sep 17 00:00:00 2001 From: Andreas Dilger Date: Mon, 18 Nov 2013 01:47:44 -0700 Subject: [PATCH] LU-5053 libcfs: clean up cfs_crypto_hash code Remove from and only include it into the places where it is actually needed. This works out to be the same places as , so put it there. Fix the cfs_crypto_hash_* functions to take enum cfs_crypto_hash_alg as the algorithm type, instead of an unsigned char. Return CFS_HASH_ALG_UNKOWN for unknown hash names instead of "0xFF". Rename some variables to be consistent across functions. Add comment blocks for cfs_crypto_hash_*() in linux-crypto.c. Some of these functions could be shared with user-crypto.c in a common crypto.c file, but the code shuffling should be done separately. Change cfs_crypto_hash_final() to always clean up the hash descrptor instead of not doing this in error cases. All of the callers were just calling cfs_crypto_hash_final() immediately to clean up the descriptor anyway, and the old behaviour is unlike other init/fini functions, and prone to memory leaks and other incorrect usage. The callers can call cfs_crypto_digest_size() to determine the hash size in advance if needed, and avoid complexity in cfs_crypto_hash_final(). Signed-off-by: Andreas Dilger Change-Id: I79884eb2ee31a2b375420cf62af5ce8ff22e0e75 Reviewed-on: http://review.whamcloud.com/9990 Tested-by: Jenkins Tested-by: Maloo Reviewed-by: Bob Glossman Reviewed-by: James Simmons Reviewed-by: Oleg Drokin --- libcfs/include/libcfs/libcfs.h | 1 - libcfs/include/libcfs/libcfs_crypto.h | 168 ++++++++----------- libcfs/libcfs/linux/linux-crypto.c | 293 ++++++++++++++++++++++++---------- libcfs/libcfs/user-crypto.c | 74 +++++---- lustre/include/obd_cksum.h | 1 + lustre/osc/osc_request.c | 10 +- lustre/ptlrpc/sec_bulk.c | 26 +-- lustre/target/tgt_handler.c | 4 +- 8 files changed, 337 insertions(+), 240 deletions(-) diff --git a/libcfs/include/libcfs/libcfs.h b/libcfs/include/libcfs/libcfs.h index 321712e..8280eb1 100644 --- a/libcfs/include/libcfs/libcfs.h +++ b/libcfs/include/libcfs/libcfs.h @@ -253,7 +253,6 @@ void cfs_get_random_bytes(void *buf, int size); #include #include #include -#include /* container_of depends on "likely" which is defined in libcfs_private.h */ static inline void *__container_of(const void *ptr, unsigned long shift) diff --git a/libcfs/include/libcfs/libcfs_crypto.h b/libcfs/include/libcfs/libcfs_crypto.h index 64ca62f..10f94d3 100644 --- a/libcfs/include/libcfs/libcfs_crypto.h +++ b/libcfs/include/libcfs/libcfs_crypto.h @@ -37,7 +37,7 @@ struct cfs_crypto_hash_type { }; enum cfs_crypto_hash_alg { - CFS_HASH_ALG_NULL = 0, + CFS_HASH_ALG_NULL = 0, CFS_HASH_ALG_ADLER32, CFS_HASH_ALG_CRC32, CFS_HASH_ALG_MD5, @@ -46,89 +46,104 @@ enum cfs_crypto_hash_alg { CFS_HASH_ALG_SHA384, CFS_HASH_ALG_SHA512, CFS_HASH_ALG_CRC32C, - CFS_HASH_ALG_MAX + CFS_HASH_ALG_MAX, + CFS_HASH_ALG_UNKNOWN = 0xff }; static struct cfs_crypto_hash_type hash_types[] = { - [CFS_HASH_ALG_NULL] = { "null", 0, 0 }, - [CFS_HASH_ALG_ADLER32] = { "adler32", 1, 4 }, - [CFS_HASH_ALG_CRC32] = { "crc32", ~0, 4 }, - [CFS_HASH_ALG_CRC32C] = { "crc32c", ~0, 4 }, - [CFS_HASH_ALG_MD5] = { "md5", 0, 16 }, - [CFS_HASH_ALG_SHA1] = { "sha1", 0, 20 }, - [CFS_HASH_ALG_SHA256] = { "sha256", 0, 32 }, - [CFS_HASH_ALG_SHA384] = { "sha384", 0, 48 }, - [CFS_HASH_ALG_SHA512] = { "sha512", 0, 64 }, + [CFS_HASH_ALG_NULL] = { "null", 0, 0 }, + [CFS_HASH_ALG_ADLER32] = { "adler32", 1, 4 }, + [CFS_HASH_ALG_CRC32] = { "crc32", ~0, 4 }, + [CFS_HASH_ALG_CRC32C] = { "crc32c", ~0, 4 }, + [CFS_HASH_ALG_MD5] = { "md5", 0, 16 }, + [CFS_HASH_ALG_SHA1] = { "sha1", 0, 20 }, + [CFS_HASH_ALG_SHA256] = { "sha256", 0, 32 }, + [CFS_HASH_ALG_SHA384] = { "sha384", 0, 48 }, + [CFS_HASH_ALG_SHA512] = { "sha512", 0, 64 }, + [CFS_HASH_ALG_MAX] = { NULL, 0, 64 }, }; -/** Return pointer to type of hash for valid hash algorithm identifier */ -static inline const struct cfs_crypto_hash_type * - cfs_crypto_hash_type(unsigned char hash_alg) +/* Maximum size of hash_types[].cht_size */ +#define CFS_CRYPTO_HASH_DIGESTSIZE_MAX 64 + +/** + * Return hash algorithm information for the specified algorithm identifier + * + * Hash information includes algorithm name, initial seed, hash size. + * + * \retval cfs_crypto_hash_type for valid ID (CFS_HASH_ALG_*) + * \retval NULL for unknown algorithm identifier + */ +static inline const struct +cfs_crypto_hash_type *cfs_crypto_hash_type(enum cfs_crypto_hash_alg hash_alg) { struct cfs_crypto_hash_type *ht; if (hash_alg < CFS_HASH_ALG_MAX) { ht = &hash_types[hash_alg]; - if (ht->cht_name) + if (ht->cht_name != NULL) return ht; } return NULL; } -/** Return hash name for valid hash algorithm identifier or "unknown" */ -static inline const char *cfs_crypto_hash_name(unsigned char hash_alg) +/** + * Return hash name for hash algorithm identifier + * + * \param[in] hash_alg hash alrgorithm id (CFS_HASH_ALG_*) + * + * \retval string name of known hash algorithm + * \retval "unknown" if hash algorithm is unknown + */ +static inline const +char *cfs_crypto_hash_name(enum cfs_crypto_hash_alg hash_alg) { const struct cfs_crypto_hash_type *ht; ht = cfs_crypto_hash_type(hash_alg); if (ht) return ht->cht_name; - else - return "unknown"; + + return "unknown"; } -/** Return digest size for valid algorithm identifier or 0 */ -static inline int cfs_crypto_hash_digestsize(unsigned char hash_alg) +/** + * Return digest size for hash algorithm type + * + * \param[in] hash_alg hash alrgorithm id (CFS_HASH_ALG_*) + * + * \retval hash algorithm digest size in bytes + * \retval 0 if hash algorithm type is unknown + */ +static inline int cfs_crypto_hash_digestsize(enum cfs_crypto_hash_alg hash_alg) { const struct cfs_crypto_hash_type *ht; ht = cfs_crypto_hash_type(hash_alg); - if (ht) + if (ht != NULL) return ht->cht_size; - else - return 0; + + return 0; } -/** Return hash identifier for valid hash algorithm name or 0xFF */ +/** + * Find hash algorithm ID for the specified algorithm name + * + * \retval hash algorithm ID for valid ID (CFS_HASH_ALG_*) + * \retval CFS_HASH_ALG_UNKNOWN for unknown algorithm name + */ static inline unsigned char cfs_crypto_hash_alg(const char *algname) { - unsigned char i; + enum cfs_crypto_hash_alg hash_alg; + + for (hash_alg = 0; hash_alg < CFS_HASH_ALG_MAX; hash_alg++) + if (strcmp(hash_types[hash_alg].cht_name, algname) == 0) + return hash_alg; - for (i = 0; i < CFS_HASH_ALG_MAX; i++) - if (!strcmp(hash_types[i].cht_name, algname)) - break; - return (i == CFS_HASH_ALG_MAX ? 0xFF : i); + return CFS_HASH_ALG_UNKNOWN; } -/** Calculate hash digest for buffer. - * @param alg id of hash algorithm - * @param buf buffer of data - * @param buf_len buffer len - * @param key initial value for algorithm, if it is NULL, - * default initial value should be used. - * @param key_len len of initial value - * @param hash [out] pointer to hash, if it is NULL, hash_len is - * set to valid digest size in bytes, retval -ENOSPC. - * @param hash_len [in,out] size of hash buffer - * @returns status of operation - * @retval -EINVAL if buf, buf_len, hash_len or alg_id is invalid - * @retval -ENODEV if this algorithm is unsupported - * @retval -ENOSPC if pointer to hash is NULL, or hash_len less than - * digest size - * @retval 0 for success - * @retval < 0 other errors from lower layers. - */ -int cfs_crypto_hash_digest(unsigned char alg, +int cfs_crypto_hash_digest(enum cfs_crypto_hash_alg hash_alg, const void *buf, unsigned int buf_len, unsigned char *key, unsigned int key_len, unsigned char *hash, unsigned int *hash_len); @@ -136,66 +151,17 @@ int cfs_crypto_hash_digest(unsigned char alg, /* cfs crypto hash descriptor */ struct cfs_crypto_hash_desc; -/** Allocate and initialize desriptor for hash algorithm. - * @param alg algorithm id - * @param key initial value for algorithm, if it is NULL, - * default initial value should be used. - * @param key_len len of initial value - * @returns pointer to descriptor of hash instance - * @retval ERR_PTR(error) when errors occured. - */ -struct cfs_crypto_hash_desc* - cfs_crypto_hash_init(unsigned char alg, +struct cfs_crypto_hash_desc * + cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg, unsigned char *key, unsigned int key_len); - -/** Update digest by part of data. - * @param desc hash descriptor - * @param page data page - * @param offset data offset - * @param len data len - * @returns status of operation - * @retval 0 for success. - */ int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc, struct page *page, unsigned int offset, unsigned int len); - -/** Update digest by part of data. - * @param desc hash descriptor - * @param buf pointer to data buffer - * @param buf_len size of data at buffer - * @returns status of operation - * @retval 0 for success. - */ int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf, unsigned int buf_len); - -/** Finalize hash calculation, copy hash digest to buffer, destroy hash - * descriptor. - * @param desc hash descriptor - * @param hash buffer pointer to store hash digest - * @param hash_len pointer to hash buffer size, if NULL - * destory hash descriptor - * @returns status of operation - * @retval -ENOSPC if hash is NULL, or *hash_len less than - * digest size - * @retval 0 for success - * @retval < 0 other errors from lower layers. - */ int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc, unsigned char *hash, unsigned int *hash_len); -/** - * Register crypto hash algorithms - */ int cfs_crypto_register(void); - -/** - * Unregister - */ void cfs_crypto_unregister(void); - -/** Return hash speed in Mbytes per second for valid hash algorithm - * identifier. If test was unsuccessfull -1 would be return. - */ -int cfs_crypto_hash_speed(unsigned char hash_alg); +int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg); #endif diff --git a/libcfs/libcfs/linux/linux-crypto.c b/libcfs/libcfs/linux/linux-crypto.c index 8398e9e..b5f376d 100644 --- a/libcfs/libcfs/linux/linux-crypto.c +++ b/libcfs/libcfs/linux/linux-crypto.c @@ -30,61 +30,100 @@ #include #include #include +#include #include /** - * Array of hash algorithm speed in MByte per second + * Array of hash algorithm speed in MByte per second */ static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX]; -static int cfs_crypto_hash_alloc(unsigned char alg_id, +/** + * Initialize the state descriptor for the specified hash algorithm. + * + * An internal routine to allocate the hash-specific state in \a hdesc for + * use with cfs_crypto_hash_digest() to compute the hash of a single message, + * though possibly in multiple chunks. The descriptor internal state should + * be freed with cfs_crypto_hash_final(). + * + * \param[in] hash_alg hash algorithm id (CFS_HASH_ALG_*) + * \param[out] type pointer to the hash description in hash_types[] array + * \param[in,out] hdesc hash state descriptor to be initialized + * \param[in] key initial hash value/state, NULL to use default value + * \param[in] key_len length of \a key + * + * \retval 0 on success + * \retval negative errno on failure + */ +static int cfs_crypto_hash_alloc(enum cfs_crypto_hash_alg hash_alg, const struct cfs_crypto_hash_type **type, - struct hash_desc *desc, unsigned char *key, + struct hash_desc *hdesc, unsigned char *key, unsigned int key_len) { - int err = 0; + int err = 0; - *type = cfs_crypto_hash_type(alg_id); + *type = cfs_crypto_hash_type(hash_alg); if (*type == NULL) { CWARN("Unsupported hash algorithm id = %d, max id is %d\n", - alg_id, CFS_HASH_ALG_MAX); + hash_alg, CFS_HASH_ALG_MAX); return -EINVAL; } - desc->tfm = crypto_alloc_hash((*type)->cht_name, 0, 0); + hdesc->tfm = crypto_alloc_hash((*type)->cht_name, 0, 0); - if (desc->tfm == NULL) + if (hdesc->tfm == NULL) return -EINVAL; - if (IS_ERR(desc->tfm)) { + if (IS_ERR(hdesc->tfm)) { CDEBUG(D_INFO, "Failed to alloc crypto hash %s\n", (*type)->cht_name); - return PTR_ERR(desc->tfm); + return PTR_ERR(hdesc->tfm); } - desc->flags = 0; + hdesc->flags = 0; - if (key != NULL) { - err = crypto_hash_setkey(desc->tfm, key, key_len); - } else if ((*type)->cht_key != 0) { - err = crypto_hash_setkey(desc->tfm, + if (key != NULL) + err = crypto_hash_setkey(hdesc->tfm, key, key_len); + else if ((*type)->cht_key != 0) + err = crypto_hash_setkey(hdesc->tfm, (unsigned char *)&((*type)->cht_key), (*type)->cht_size); - } if (err != 0) { - crypto_free_hash(desc->tfm); + crypto_free_hash(hdesc->tfm); return err; } CDEBUG(D_INFO, "Using crypto hash: %s (%s) speed %d MB/s\n", - (crypto_hash_tfm(desc->tfm))->__crt_alg->cra_name, - (crypto_hash_tfm(desc->tfm))->__crt_alg->cra_driver_name, - cfs_crypto_hash_speeds[alg_id]); + (crypto_hash_tfm(hdesc->tfm))->__crt_alg->cra_name, + (crypto_hash_tfm(hdesc->tfm))->__crt_alg->cra_driver_name, + cfs_crypto_hash_speeds[hash_alg]); - return crypto_hash_init(desc); + return crypto_hash_init(hdesc); } -int cfs_crypto_hash_digest(unsigned char alg_id, +/** + * Calculate hash digest for the passed buffer. + * + * This should be used when computing the hash on a single contiguous buffer. + * It combines the hash initialization, computation, and cleanup. + * + * \param[in] hash_alg id of hash algorithm (CFS_HASH_ALG_*) + * \param[in] buf data buffer on which to compute hash + * \param[in] buf_len length of \a buf in bytes + * \param[in] key initial value/state for algorithm, if \a key = NULL + * use default initial value + * \param[in] key_len length of \a key in bytes + * \param[out] hash pointer to computed hash value, if \a hash = NULL then + * \a hash_len is to digest size in bytes, retval -ENOSPC + * \param[in,out] hash_len size of \a hash buffer + * + * \retval -EINVAL \a buf, \a buf_len, \a hash_len, \a alg_id invalid + * \retval -ENOENT \a hash_alg is unsupported + * \retval -ENOSPC \a hash is NULL, or \a hash_len less than digest size + * \retval 0 for success + * \retval negative errno for other errors from lower layers. + */ +int cfs_crypto_hash_digest(enum cfs_crypto_hash_alg hash_alg, const void *buf, unsigned int buf_len, unsigned char *key, unsigned int key_len, unsigned char *hash, unsigned int *hash_len) @@ -97,7 +136,7 @@ int cfs_crypto_hash_digest(unsigned char alg_id, if (buf == NULL || buf_len == 0 || hash_len == NULL) return -EINVAL; - err = cfs_crypto_hash_alloc(alg_id, &type, &hdesc, key, key_len); + err = cfs_crypto_hash_alloc(hash_alg, &type, &hdesc, key, key_len); if (err != 0) return err; @@ -116,29 +155,57 @@ int cfs_crypto_hash_digest(unsigned char alg_id, } EXPORT_SYMBOL(cfs_crypto_hash_digest); +/** + * Allocate and initialize desriptor for hash algorithm. + * + * This should be used to initialize a hash descriptor for multiple calls + * to a single hash function when computing the hash across multiple + * separate buffers or pages using cfs_crypto_hash_update{,_page}(). + * + * The hash descriptor should be freed with cfs_crypto_hash_final(). + * + * \param[in] hash_alg algorithm id (CFS_HASH_ALG_*) + * \param[in] key initial value/state for algorithm, if \a key = NULL + * use default initial value + * \param[in] key_len length of \a key in bytes + * + * \retval pointer to descriptor of hash instance + * \retval ERR_PTR(errno) in case of error + */ struct cfs_crypto_hash_desc * - cfs_crypto_hash_init(unsigned char alg_id, + cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg, unsigned char *key, unsigned int key_len) { - struct hash_desc *hdesc; - int err; + struct hash_desc *hdesc; + int err; const struct cfs_crypto_hash_type *type; hdesc = kmalloc(sizeof(*hdesc), 0); if (hdesc == NULL) return ERR_PTR(-ENOMEM); - err = cfs_crypto_hash_alloc(alg_id, &type, hdesc, key, key_len); + err = cfs_crypto_hash_alloc(hash_alg, &type, hdesc, key, key_len); if (err) { kfree(hdesc); - return ERR_PTR(err); + hdesc = ERR_PTR(err); } return (struct cfs_crypto_hash_desc *)hdesc; } EXPORT_SYMBOL(cfs_crypto_hash_init); +/** + * Update hash digest computed on data within the given \a page + * + * \param[in] hdesc hash state descriptor + * \param[in] page data page on which to compute the hash + * \param[in] offset offset within \a page at which to start hash + * \param[in] len length of data on which to compute hash + * + * \retval 0 for success + * \retval negative errno on failure + */ int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc, struct page *page, unsigned int offset, unsigned int len) @@ -152,6 +219,16 @@ int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc, } EXPORT_SYMBOL(cfs_crypto_hash_update_page); +/** + * Update hash digest computed on the specified data + * + * \param[in] hdesc hash state descriptor + * \param[in] buf data buffer on which to compute the hash + * \param[in] buf_len length of \buf on which to compute hash + * + * \retval 0 for success + * \retval negative errno on failure + */ int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *hdesc, const void *buf, unsigned int buf_len) { @@ -163,102 +240,146 @@ int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *hdesc, } EXPORT_SYMBOL(cfs_crypto_hash_update); -/* If hash_len pointer is NULL - destroy descriptor. */ +/** + * Finish hash calculation, copy hash digest to buffer, clean up hash descriptor + * + * \param[in] hdesc hash descriptor + * \param[out] hash pointer to hash buffer to store hash digest + * \param[in,out] hash_len pointer to hash buffer size, if \a hdesc = NULL + * only free \a hdesc instead of computing the hash + * + * \retval -ENOSPC if \a hash = NULL, or \a hash_len < digest size + * \retval 0 for success + * \retval negative errno for other errors from lower layers + */ int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *hdesc, unsigned char *hash, unsigned int *hash_len) { - int err; int size = crypto_hash_digestsize(((struct hash_desc *)hdesc)->tfm); + int err; if (hash_len == NULL) { - crypto_free_hash(((struct hash_desc *)hdesc)->tfm); - kfree(hdesc); - return 0; + err = 0; + goto free; } if (hash == NULL || *hash_len < size) { - *hash_len = size; - return -ENOSPC; - } - err = crypto_hash_final((struct hash_desc *) hdesc, hash); - - if (err < 0) { - /* May be caller can fix error */ - return err; + err = -ENOSPC; + goto free; } + err = crypto_hash_final((struct hash_desc *)hdesc, hash); +free: crypto_free_hash(((struct hash_desc *)hdesc)->tfm); kfree(hdesc); + return err; } EXPORT_SYMBOL(cfs_crypto_hash_final); -static void cfs_crypto_performance_test(unsigned char alg_id, +/** + * Compute the speed of specified hash function + * + * Run a speed test on the given hash algorithm on buffer of the given size. + * The speed is stored internally in the cfs_crypto_hash_speeds[] array, and + * is available through the cfs_crypto_hash_speed() function. + * + * \param[in] hash_alg hash algorithm id (CFS_HASH_ALG_*) + * \param[in] buf data buffer on which to compute the hash + * \param[in] buf_len length of \buf on which to compute hash + */ +static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg, const unsigned char *buf, unsigned int buf_len) { - unsigned long start, end; - int bcount, err = 0; - int sec = 1; /* do test only 1 sec */ - unsigned char hash[64]; - unsigned int hash_len = 64; + unsigned long start, end; + int bcount, err = 0; + int sec = 1; /* do test only 1 sec */ + unsigned char hash[64]; + unsigned int hash_len = sizeof(hash); for (start = jiffies, end = start + sec * HZ, bcount = 0; time_before(jiffies, end); bcount++) { - err = cfs_crypto_hash_digest(alg_id, buf, buf_len, NULL, 0, + err = cfs_crypto_hash_digest(hash_alg, buf, buf_len, NULL, 0, hash, &hash_len); - if (err) + if (err != 0) break; } end = jiffies; - if (err) { - cfs_crypto_hash_speeds[alg_id] = -1; - CDEBUG(D_INFO, "Crypto hash algorithm %s, err = %d\n", - cfs_crypto_hash_name(alg_id), err); + if (err != 0) { + cfs_crypto_hash_speeds[hash_alg] = err; + CDEBUG(D_INFO, "Crypto hash algorithm %s test error: rc = %d\n", + cfs_crypto_hash_name(hash_alg), err); } else { unsigned long tmp; + tmp = ((bcount * buf_len / jiffies_to_msecs(end - start)) * 1000) / (1024 * 1024); - cfs_crypto_hash_speeds[alg_id] = (int)tmp; + cfs_crypto_hash_speeds[hash_alg] = (int)tmp; + CDEBUG(D_CONFIG, "Crypto hash algorithm %s speed = %d MB/s\n", + cfs_crypto_hash_name(hash_alg), + cfs_crypto_hash_speeds[hash_alg]); } - CDEBUG(D_CONFIG, "Crypto hash algorithm %s speed = %d MB/s\n", - cfs_crypto_hash_name(alg_id), cfs_crypto_hash_speeds[alg_id]); } -int cfs_crypto_hash_speed(unsigned char hash_alg) +/** + * hash speed in Mbytes per second for valid hash algorithm + * + * Return the performance of the specified \a hash_alg that was previously + * computed using cfs_crypto_performance_test(). + * + * \param[in] hash_alg hash algorithm id (CFS_HASH_ALG_*) + * + * \retval positive speed of the hash function in MB/s + * \retval -ENOENT if \a hash_alg is unsupported + * \retval negative errno if \a hash_alg speed is unavailable + */ +int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg) { if (hash_alg < CFS_HASH_ALG_MAX) return cfs_crypto_hash_speeds[hash_alg]; - else - return -1; + + return -ENOENT; } EXPORT_SYMBOL(cfs_crypto_hash_speed); /** - * Do performance test for all hash algorithms. + * Run the performance test for all hash algorithms. + * + * Run the cfs_crypto_performance_test() benchmark for all of the available + * hash functions using a 1MB buffer size. This is a reasonable buffer size + * for Lustre RPCs, even if the actual RPC size is larger or smaller. + * + * Since the setup cost and computation speed of various hash algorithms is + * a function of the buffer size (and possibly internal contention of offload + * engines), this speed only represents an estimate of the actual speed under + * actual usage, but is reasonable for comparing available algorithms. + * + * The actual speeds are available via cfs_crypto_hash_speed() for later + * comparison. + * + * \retval 0 on success + * \retval -ENOMEM if no memory is available for test buffer */ static int cfs_crypto_test_hashes(void) { - unsigned char i; - unsigned char *data; - unsigned int j; - /* Data block size for testing hash. Maximum - * kmalloc size for 2.6.18 kernel is 128K */ - unsigned int data_len = 1 * 128 * 1024; - - data = kmalloc(data_len, 0); + enum cfs_crypto_hash_alg hash_alg; + unsigned char *data; + /* Data block size for testing hash. Use bulk RPC size. */ + unsigned int data_len = 1024 * 1024; + + data = vmalloc(data_len); if (data == NULL) { - CERROR("Failed to allocate mem\n"); + CERROR("Failed to allocate buffer for hash speed test\n"); return -ENOMEM; } - for (j = 0; j < data_len; j++) - data[j] = j & 0xff; + memset(data, 0xAD, data_len); - for (i = 0; i < CFS_HASH_ALG_MAX; i++) - cfs_crypto_performance_test(i, data, data_len); + for (hash_alg = 0; hash_alg < CFS_HASH_ALG_MAX; hash_alg++) + cfs_crypto_performance_test(hash_alg, data, data_len); - kfree(data); + vfree(data); return 0; } @@ -269,13 +390,18 @@ static int crc32; #endif #ifdef HAVE_PCLMULQDQ #ifdef NEED_CRC32_ACCEL -static int crc32pclmul; +static int crc32_pclmul; #endif #ifdef NEED_CRC32C_ACCEL static int crc32c_pclmul; #endif -#endif +#endif /* HAVE_PCLMULQDQ */ +/** + * Register available hash functions + * + * \retval 0 + */ int cfs_crypto_register(void) { request_module("crc32c"); @@ -287,16 +413,22 @@ int cfs_crypto_register(void) #endif #ifdef HAVE_PCLMULQDQ #ifdef NEED_CRC32_ACCEL - crc32pclmul = cfs_crypto_crc32_pclmul_register(); + crc32_pclmul = cfs_crypto_crc32_pclmul_register(); #endif #ifdef NEED_CRC32C_ACCEL crc32c_pclmul = cfs_crypto_crc32c_pclmul_register(); #endif -#endif +#endif /* HAVE_PCLMULQDQ */ + /* check all algorithms and do performance test */ cfs_crypto_test_hashes(); + return 0; } + +/** + * Unregister previously registered hash functions + */ void cfs_crypto_unregister(void) { if (adler32 == 0) @@ -308,13 +440,12 @@ void cfs_crypto_unregister(void) #endif #ifdef HAVE_PCLMULQDQ #ifdef NEED_CRC32_ACCEL - if (crc32pclmul == 0) + if (crc32_pclmul == 0) cfs_crypto_crc32_pclmul_unregister(); #endif #ifdef NEED_CRC32C_ACCEL if (crc32c_pclmul == 0) cfs_crypto_crc32c_pclmul_unregister(); #endif -#endif - return; +#endif /* HAVE_PCLMULQDQ */ } diff --git a/libcfs/libcfs/user-crypto.c b/libcfs/libcfs/user-crypto.c index efc6782..9c549b5 100644 --- a/libcfs/libcfs/user-crypto.c +++ b/libcfs/libcfs/user-crypto.c @@ -30,9 +30,13 @@ */ #include +#include #include #include +/** + * Array of hash algorithm speed in MByte per second + */ static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX]; struct __hash_alg { @@ -170,19 +174,20 @@ static struct __hash_alg crypto_hash[] = { }; /** - * Go through hashes to find the hash with max priority - * for the alg_id algorithm. This is done for different implementation - * of the same algorithm. Priotity is staticaly defined by developer, and - * can be zeroed if initialization of algo is unsuccessfull. + * Go through hashes to find the hash with max priority for the hash_alg + * algorithm. This is done for different implementation of the same + * algorithm. Priority is staticaly defined by developer, and can be zeroed + * if initialization of algo is unsuccessful. */ -static const struct __hash_alg *cfs_crypto_hash_best_alg(unsigned char alg_id) +static const struct __hash_alg +*cfs_crypto_hash_best_alg(enum cfs_crypto_hash_alg hash_alg) { int max_priority = 0; const struct __hash_alg *alg = NULL; int i; for (i = 0; i < ARRAY_SIZE(crypto_hash); i++) { - if (alg_id == crypto_hash[i].ha_id && + if (hash_alg == crypto_hash[i].ha_id && max_priority < crypto_hash[i].ha_priority) { max_priority = crypto_hash[i].ha_priority; alg = &crypto_hash[i]; @@ -193,22 +198,22 @@ static const struct __hash_alg *cfs_crypto_hash_best_alg(unsigned char alg_id) } struct cfs_crypto_hash_desc - *cfs_crypto_hash_init(unsigned char alg, - unsigned char *key, unsigned int key_len) +*cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg, + unsigned char *key, unsigned int key_len) { struct hash_desc *hdesc = NULL; const struct cfs_crypto_hash_type *type; const struct __hash_alg *ha = NULL; int err; - type = cfs_crypto_hash_type(alg); + type = cfs_crypto_hash_type(hash_alg); if (type == NULL) { CWARN("Unsupported hash algorithm id = %d, max id is %d\n", - alg, CFS_HASH_ALG_MAX); + hash_alg, CFS_HASH_ALG_MAX); return ERR_PTR(-EINVAL); } - ha = cfs_crypto_hash_best_alg(alg); + ha = cfs_crypto_hash_best_alg(hash_alg); if (ha == NULL) { CERROR("Failed to get hash algorithm\n"); return ERR_PTR(-ENODEV); @@ -268,45 +273,43 @@ int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc, size = type->cht_size; if (hash_len == NULL) { - kfree(d); - return 0; + err = 0; + goto free; } if (hash == NULL || *hash_len < size) { - *hash_len = d->hd_hash->ha_ctx_size; - return -ENOMEM; + err = -ENOMEM; + goto free; } LASSERT(d->hd_hash->final != NULL); err = d->hd_hash->final(d->hd_ctx, hash, *hash_len); - if (err == 0) { - /* If get final digest success free hash descriptor */ - kfree(d); - } +free: + kfree(d); return err; } -int cfs_crypto_hash_digest(unsigned char alg, +int cfs_crypto_hash_digest(enum cfs_crypto_hash_alg hash_alg, const void *buf, unsigned int buf_len, unsigned char *key, unsigned int key_len, unsigned char *hash, unsigned int *hash_len) { struct cfs_crypto_hash_desc *desc; - int err; + int err, err2; - desc = cfs_crypto_hash_init(alg, key, key_len); + desc = cfs_crypto_hash_init(hash_alg, key, key_len); if (IS_ERR(desc)) return PTR_ERR(desc); err = cfs_crypto_hash_update(desc, buf, buf_len); - if (err) { - cfs_crypto_hash_final(desc, NULL, NULL); - return err; - } - err = cfs_crypto_hash_final(desc, hash, hash_len); if (err != 0) - cfs_crypto_hash_final(desc, NULL, NULL); + hash_len = NULL; + + err2 = cfs_crypto_hash_final(desc, hash, hash_len); + if (err2 != 0 && err == 0) + err = err2; + return err; } @@ -327,7 +330,7 @@ static long cfs_crypto_get_sec(struct timeval *start) return cfs_timeval_sub(&end, start, NULL); } -static void cfs_crypto_performance_test(unsigned char alg_id, +static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg, const unsigned char *buf, unsigned int buf_len) { @@ -339,7 +342,7 @@ static void cfs_crypto_performance_test(unsigned char alg_id, cfs_crypto_start_timer(&start); for (bcount = 0; bcount < iteration; bcount++) { - err = cfs_crypto_hash_digest(alg_id, buf, buf_len, NULL, 0, + err = cfs_crypto_hash_digest(hash_alg, buf, buf_len, NULL, 0, hash, &hash_len); if (err) break; @@ -348,18 +351,19 @@ static void cfs_crypto_performance_test(unsigned char alg_id, msec = (int)(cfs_crypto_get_sec(&start) / 1000.0); if (err) { - cfs_crypto_hash_speeds[alg_id] = -1; + cfs_crypto_hash_speeds[hash_alg] = -1; CDEBUG(D_INFO, "Crypto hash algorithm err = %d\n", err); } else { long tmp; tmp = ((bcount * buf_len / msec) * 1000) / (1024 * 1024); - cfs_crypto_hash_speeds[alg_id] = (int)tmp; + cfs_crypto_hash_speeds[hash_alg] = (int)tmp; } - CDEBUG(D_INFO, "Crypto hash algorithm %s speed = %d MB/s\n", - cfs_crypto_hash_name(alg_id), cfs_crypto_hash_speeds[alg_id]); + CDEBUG(D_CONFIG, "Crypto hash algorithm %s speed = %d MB/s\n", + cfs_crypto_hash_name(hash_alg), + cfs_crypto_hash_speeds[hash_alg]); } -int cfs_crypto_hash_speed(unsigned char hash_alg) +int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg) { if (hash_alg < CFS_HASH_ALG_MAX) return cfs_crypto_hash_speeds[hash_alg]; diff --git a/lustre/include/obd_cksum.h b/lustre/include/obd_cksum.h index d546bb8..7d00abb 100644 --- a/lustre/include/obd_cksum.h +++ b/lustre/include/obd_cksum.h @@ -35,6 +35,7 @@ #ifndef __OBD_CKSUM #define __OBD_CKSUM #include +#include #include static inline unsigned char cksum_obd2cfs(cksum_type_t cksum_type) diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c index 9421044..5a8165c 100644 --- a/lustre/osc/osc_request.c +++ b/lustre/osc/osc_request.c @@ -1195,12 +1195,13 @@ static obd_count osc_checksum_bulk(int nob, obd_count pg_count, OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_RECEIVE)) { unsigned char *ptr = kmap(pga[i]->pg); int off = pga[i]->off & ~CFS_PAGE_MASK; + memcpy(ptr + off, "bad1", min(4, nob)); kunmap(pga[i]->pg); } cfs_crypto_hash_update_page(hdesc, pga[i]->pg, - pga[i]->off & ~CFS_PAGE_MASK, - count); + pga[i]->off & ~CFS_PAGE_MASK, + count); LL_CDEBUG_PAGE(D_PAGE, pga[i]->pg, "off %d\n", (int)(pga[i]->off & ~CFS_PAGE_MASK)); @@ -1209,12 +1210,9 @@ static obd_count osc_checksum_bulk(int nob, obd_count pg_count, i++; } - bufsize = 4; + bufsize = sizeof(cksum); err = cfs_crypto_hash_final(hdesc, (unsigned char *)&cksum, &bufsize); - if (err) - cfs_crypto_hash_final(hdesc, NULL, NULL); - /* For sending we only compute the wrong checksum instead * of corrupting the data so it is still correct on a redo */ if (opc == OST_WRITE && OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_SEND)) diff --git a/lustre/ptlrpc/sec_bulk.c b/lustre/ptlrpc/sec_bulk.c index 9da60ad..578715c 100644 --- a/lustre/ptlrpc/sec_bulk.c +++ b/lustre/ptlrpc/sec_bulk.c @@ -41,12 +41,6 @@ #define DEBUG_SUBSYSTEM S_SEC #include -#ifndef __KERNEL__ -#include -#include -#else -#include -#endif #include #include @@ -899,12 +893,17 @@ int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset, int swabbed) } EXPORT_SYMBOL(bulk_sec_desc_unpack); +/* + * Compute the checksum of an RPC buffer payload. If the return \a buflen + * is not large enough, truncate the result to fit so that it is possible + * to use a hash function with a large hash space, but only use a part of + * the resulting hash. + */ int sptlrpc_get_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u8 alg, void *buf, int buflen) { struct cfs_crypto_hash_desc *hdesc; int hashsize; - char hashbuf[64]; unsigned int bufsize; int i, err; @@ -930,19 +929,20 @@ int sptlrpc_get_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u8 alg, desc->bd_iov[i].iov_len); #endif } + if (hashsize > buflen) { + unsigned char hashbuf[CFS_CRYPTO_HASH_DIGESTSIZE_MAX]; + bufsize = sizeof(hashbuf); - err = cfs_crypto_hash_final(hdesc, (unsigned char *)hashbuf, - &bufsize); + LASSERTF(bufsize >= hashsize, "bufsize = %u < hashsize %u\n", + bufsize, hashsize); + err = cfs_crypto_hash_final(hdesc, hashbuf, &bufsize); memcpy(buf, hashbuf, buflen); } else { bufsize = buflen; - err = cfs_crypto_hash_final(hdesc, (unsigned char *)buf, - &bufsize); + err = cfs_crypto_hash_final(hdesc, buf, &bufsize); } - if (err) - cfs_crypto_hash_final(hdesc, NULL, NULL); return err; } EXPORT_SYMBOL(sptlrpc_get_bulk_checksum); diff --git a/lustre/target/tgt_handler.c b/lustre/target/tgt_handler.c index d986c40..af9cb58 100644 --- a/lustre/target/tgt_handler.c +++ b/lustre/target/tgt_handler.c @@ -1636,10 +1636,8 @@ static __u32 tgt_checksum_bulk(struct lu_target *tgt, } } - bufsize = 4; + bufsize = sizeof(cksum); err = cfs_crypto_hash_final(hdesc, (unsigned char *)&cksum, &bufsize); - if (err) - cfs_crypto_hash_final(hdesc, NULL, NULL); return cksum; } -- 1.8.3.1