#include <crypto/hash.h>
#include <linux/scatterlist.h>
+#include <linux/pagemap.h>
#include <libcfs/libcfs.h>
#include <libcfs/libcfs_crypto.h>
#include <libcfs/linux/linux-crypto.h>
-#ifndef crypto_ahash_alg_name
+#ifndef HAVE_CRYPTO_HASH_HELPERS
static inline const char *crypto_ahash_alg_name(struct crypto_ahash *tfm)
{
return crypto_tfm_alg_name(crypto_ahash_tfm(tfm));
}
-#endif
-#ifndef crypto_ahash_driver_name
static inline const char *crypto_ahash_driver_name(struct crypto_ahash *tfm)
{
return crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm));
int err = 0;
*type = cfs_crypto_hash_type(hash_alg);
-
- if (*type == NULL) {
+ if (!*type) {
CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
hash_alg, CFS_HASH_ALG_MAX);
return -EINVAL;
}
- tfm = crypto_alloc_ahash((*type)->cht_name, 0, CRYPTO_ALG_ASYNC);
+
+ /* Keys are only supported for the hmac version */
+ if (key && key_len > 0) {
+ char *algo_name;
+
+ algo_name = kasprintf(GFP_KERNEL, "hmac(%s)",
+ (*type)->cht_name);
+ if (!algo_name)
+ return -ENOMEM;
+
+ tfm = crypto_alloc_ahash(algo_name, 0, CRYPTO_ALG_ASYNC);
+ kfree(algo_name);
+ } else {
+ tfm = crypto_alloc_ahash((*type)->cht_name, 0,
+ CRYPTO_ALG_ASYNC);
+ }
if (IS_ERR(tfm)) {
CDEBUG(D_INFO, "Failed to alloc crypto hash %s\n",
(*type)->cht_name);
if (!*req) {
CDEBUG(D_INFO, "Failed to alloc ahash_request for %s\n",
(*type)->cht_name);
- crypto_free_ahash(tfm);
- return -ENOMEM;
+ GOTO(out_free_tfm, err = -ENOMEM);
}
ahash_request_set_callback(*req, 0, NULL, NULL);
err = crypto_ahash_setkey(tfm,
(unsigned char *)&((*type)->cht_key),
(*type)->cht_size);
-
- if (err != 0) {
- ahash_request_free(*req);
- crypto_free_ahash(tfm);
- return err;
- }
+ if (err)
+ GOTO(out_free_req, err);
CDEBUG(D_INFO, "Using crypto hash: %s (%s) speed %d MB/s\n",
crypto_ahash_alg_name(tfm), crypto_ahash_driver_name(tfm),
err = crypto_ahash_init(*req);
if (err) {
+out_free_req:
ahash_request_free(*req);
+out_free_tfm:
crypto_free_ahash(tfm);
}
return err;
/**
* Compute the speed of specified hash function
*
- * Run a speed test on the given hash algorithm on buffer of the given size.
+ * Run a speed test on the given hash algorithm on buffer using a 1MB buffer
+ * size. This is a reasonable buffer size for Lustre RPCs, even if the actual
+ * RPC size is larger or smaller.
+ *
* The speed is stored internally in the cfs_crypto_hash_speeds[] array, and
* is available through the cfs_crypto_hash_speed() function.
*
int buf_len = max(PAGE_SIZE, 1048576UL);
void *buf;
unsigned long start, end;
- int bcount, err = 0;
+ int err = 0;
+ unsigned long bcount;
struct page *page;
unsigned char hash[CFS_CRYPTO_HASH_DIGESTSIZE_MAX];
unsigned int hash_len = sizeof(hash);
memset(buf, 0xAD, PAGE_SIZE);
kunmap(page);
- for (start = jiffies, end = start + msecs_to_jiffies(MSEC_PER_SEC),
- bcount = 0;
- time_before(jiffies, end) && err == 0; bcount++) {
+ for (start = jiffies, end = start + msecs_to_jiffies(MSEC_PER_SEC / 4),
+ bcount = 0; time_before(jiffies, end) && err == 0; bcount++) {
struct cfs_crypto_hash_desc *hdesc;
int i;
/**
* 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().
+ * Return the performance of the specified \a hash_alg that was
+ * computed using cfs_crypto_performance_test(). If the performance
+ * has not yet been computed, do that when it is first requested.
+ * That avoids computing the speed when it is not actually needed.
+ * To avoid competing threads computing the checksum speed at the
+ * same time, only compute a single checksum speed at one time.
*
* \param[in] hash_alg hash algorithm id (CFS_HASH_ALG_*)
*
*/
int cfs_crypto_hash_speed(enum cfs_crypto_hash_alg hash_alg)
{
- if (hash_alg < CFS_HASH_ALG_MAX)
+ if (hash_alg < CFS_HASH_ALG_MAX) {
+ if (unlikely(cfs_crypto_hash_speeds[hash_alg] == 0)) {
+ static DEFINE_MUTEX(crypto_hash_speed_mutex);
+
+ mutex_lock(&crypto_hash_speed_mutex);
+ if (cfs_crypto_hash_speeds[hash_alg] == 0)
+ cfs_crypto_performance_test(hash_alg);
+ mutex_unlock(&crypto_hash_speed_mutex);
+ }
return cfs_crypto_hash_speeds[hash_alg];
+ }
return -ENOENT;
}
/**
* 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.
+ * Run the cfs_crypto_performance_test() benchmark for some of the available
+ * hash functions at module load time. This can't be reliably done at runtime
+ * since the CPUs may be under load from thousands of connecting clients when
+ * the first client connects and the checksum speeds are needed.
*
* Since the setup cost and computation speed of various hash algorithms is
* a function of the buffer size (and possibly internal contention of offload
{
enum cfs_crypto_hash_alg hash_alg;
- for (hash_alg = 0; hash_alg < CFS_HASH_ALG_MAX; hash_alg++)
+ for (hash_alg = 1; hash_alg < CFS_HASH_ALG_SPEED_MAX; hash_alg++)
cfs_crypto_performance_test(hash_alg);
return 0;