Whamcloud - gitweb
LU-8602 gss: get rid of cfs_crypto_hash_desc
[fs/lustre-release.git] / libcfs / libcfs / linux / linux-crypto.c
index 6d01de3..dce1734 100644 (file)
 
 #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));
@@ -79,13 +78,27 @@ static int cfs_crypto_hash_alloc(enum cfs_crypto_hash_alg hash_alg,
        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);
@@ -96,8 +109,7 @@ static int cfs_crypto_hash_alloc(enum cfs_crypto_hash_alg hash_alg,
        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);
@@ -108,12 +120,8 @@ static int cfs_crypto_hash_alloc(enum cfs_crypto_hash_alg hash_alg,
                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),
@@ -121,7 +129,9 @@ static int cfs_crypto_hash_alloc(enum cfs_crypto_hash_alg hash_alg,
 
        err = crypto_ahash_init(*req);
        if (err) {
+out_free_req:
                ahash_request_free(*req);
+out_free_tfm:
                crypto_free_ahash(tfm);
        }
        return err;
@@ -197,10 +207,10 @@ EXPORT_SYMBOL(cfs_crypto_hash_digest);
  *                     use default initial value
  * \param[in] key_len  length of \a key in bytes
  *
- * \retval             pointer to descriptor of hash instance
+ * \retval             pointer to ahash request
  * \retval             ERR_PTR(errno) in case of error
  */
-struct cfs_crypto_hash_desc *
+struct ahash_request *
        cfs_crypto_hash_init(enum cfs_crypto_hash_alg hash_alg,
                             unsigned char *key, unsigned int key_len)
 {
@@ -211,14 +221,14 @@ struct cfs_crypto_hash_desc *
        err = cfs_crypto_hash_alloc(hash_alg, &type, &req, key, key_len);
        if (err)
                return ERR_PTR(err);
-       return (struct cfs_crypto_hash_desc *)req;
+       return req;
 }
 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] req      ahash request
  * \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
@@ -226,11 +236,10 @@ EXPORT_SYMBOL(cfs_crypto_hash_init);
  * \retval             0 for success
  * \retval             negative errno on failure
  */
-int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc,
+int cfs_crypto_hash_update_page(struct ahash_request *req,
                                struct page *page, unsigned int offset,
                                unsigned int len)
 {
-       struct ahash_request *req = (void *)hdesc;
        struct scatterlist sl;
 
        sg_init_table(&sl, 1);
@@ -244,17 +253,16 @@ EXPORT_SYMBOL(cfs_crypto_hash_update_page);
 /**
  * Update hash digest computed on the specified data
  *
- * \param[in] hdesc    hash state descriptor
+ * \param[in] req      ahash request
  * \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,
+int cfs_crypto_hash_update(struct ahash_request *req,
                           const void *buf, unsigned int buf_len)
 {
-       struct ahash_request *req = (void *)hdesc;
        struct scatterlist sl;
 
        sg_init_one(&sl, (void *)buf, buf_len);
@@ -267,7 +275,7 @@ EXPORT_SYMBOL(cfs_crypto_hash_update);
 /**
  * Finish hash calculation, copy hash digest to buffer, clean up hash descriptor
  *
- * \param[in]  hdesc           hash descriptor
+ * \param[in]  req             ahash request
  * \param[out] hash            pointer to hash buffer to store hash digest
  * \param[in,out] hash_len     pointer to hash buffer size, if \a hash == NULL
  *                             or hash_len == NULL only free \a hdesc instead
@@ -277,10 +285,9 @@ EXPORT_SYMBOL(cfs_crypto_hash_update);
  * \retval             -EOVERFLOW if hash_len is too small for the hash digest
  * \retval             negative errno for other errors from lower layers
  */
-int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *hdesc,
+int cfs_crypto_hash_final(struct ahash_request *req,
                          unsigned char *hash, unsigned int *hash_len)
 {
-       struct ahash_request *req = (void *)hdesc;
        int size = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
        int err;
 
@@ -308,10 +315,16 @@ EXPORT_SYMBOL(cfs_crypto_hash_final);
 /**
  * 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.
  *
+ * This function needs to stay the same as obd_t10_performance_test() so that
+ * the speeds are comparable.
+ *
  * \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
@@ -321,7 +334,8 @@ static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg)
        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);
@@ -336,26 +350,25 @@ static void cfs_crypto_performance_test(enum cfs_crypto_hash_alg hash_alg)
        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++) {
-               struct cfs_crypto_hash_desc *hdesc;
+       for (start = jiffies, end = start + msecs_to_jiffies(MSEC_PER_SEC / 4),
+            bcount = 0; time_before(jiffies, end) && err == 0; bcount++) {
+               struct ahash_request *req;
                int i;
 
-               hdesc = cfs_crypto_hash_init(hash_alg, NULL, 0);
-               if (IS_ERR(hdesc)) {
-                       err = PTR_ERR(hdesc);
+               req = cfs_crypto_hash_init(hash_alg, NULL, 0);
+               if (IS_ERR(req)) {
+                       err = PTR_ERR(req);
                        break;
                }
 
                for (i = 0; i < buf_len / PAGE_SIZE; i++) {
-                       err = cfs_crypto_hash_update_page(hdesc, page, 0,
+                       err = cfs_crypto_hash_update_page(req, page, 0,
                                                          PAGE_SIZE);
                        if (err != 0)
                                break;
                }
 
-               err = cfs_crypto_hash_final(hdesc, hash, &hash_len);
+               err = cfs_crypto_hash_final(req, hash, &hash_len);
                if (err != 0)
                        break;
        }
@@ -381,8 +394,12 @@ out_err:
 /**
  * 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_*)
  *
@@ -392,8 +409,17 @@ out_err:
  */
 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;
 }
@@ -402,9 +428,10 @@ EXPORT_SYMBOL(cfs_crypto_hash_speed);
 /**
  * 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
@@ -421,7 +448,7 @@ static int cfs_crypto_test_hashes(void)
 {
        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;