267 lines
5.4 KiB
C
267 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/err.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include "glob.h"
|
|
#include "crypto_ctx.h"
|
|
|
|
struct crypto_ctx_list {
|
|
spinlock_t ctx_lock;
|
|
int avail_ctx;
|
|
struct list_head idle_ctx;
|
|
wait_queue_head_t ctx_wait;
|
|
};
|
|
|
|
static struct crypto_ctx_list ctx_list;
|
|
|
|
static inline void free_aead(struct crypto_aead *aead)
|
|
{
|
|
if (aead)
|
|
crypto_free_aead(aead);
|
|
}
|
|
|
|
static void free_shash(struct shash_desc *shash)
|
|
{
|
|
if (shash) {
|
|
crypto_free_shash(shash->tfm);
|
|
kfree(shash);
|
|
}
|
|
}
|
|
|
|
static struct crypto_aead *alloc_aead(int id)
|
|
{
|
|
struct crypto_aead *tfm = NULL;
|
|
|
|
switch (id) {
|
|
case CRYPTO_AEAD_AES_GCM:
|
|
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
|
|
break;
|
|
case CRYPTO_AEAD_AES_CCM:
|
|
tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
|
|
break;
|
|
default:
|
|
pr_err("Does not support encrypt ahead(id : %d)\n", id);
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_ERR(tfm)) {
|
|
pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
|
|
return NULL;
|
|
}
|
|
|
|
return tfm;
|
|
}
|
|
|
|
static struct shash_desc *alloc_shash_desc(int id)
|
|
{
|
|
struct crypto_shash *tfm = NULL;
|
|
struct shash_desc *shash;
|
|
|
|
switch (id) {
|
|
case CRYPTO_SHASH_HMACMD5:
|
|
tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
|
|
break;
|
|
case CRYPTO_SHASH_HMACSHA256:
|
|
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
|
break;
|
|
case CRYPTO_SHASH_CMACAES:
|
|
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
|
|
break;
|
|
case CRYPTO_SHASH_SHA256:
|
|
tfm = crypto_alloc_shash("sha256", 0, 0);
|
|
break;
|
|
case CRYPTO_SHASH_SHA512:
|
|
tfm = crypto_alloc_shash("sha512", 0, 0);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_ERR(tfm))
|
|
return NULL;
|
|
|
|
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
|
GFP_KERNEL);
|
|
if (!shash)
|
|
crypto_free_shash(tfm);
|
|
else
|
|
shash->tfm = tfm;
|
|
return shash;
|
|
}
|
|
|
|
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
|
|
free_shash(ctx->desc[i]);
|
|
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
|
|
free_aead(ctx->ccmaes[i]);
|
|
kfree(ctx);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
while (1) {
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
if (!list_empty(&ctx_list.idle_ctx)) {
|
|
ctx = list_entry(ctx_list.idle_ctx.next,
|
|
struct ksmbd_crypto_ctx,
|
|
list);
|
|
list_del(&ctx->list);
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
return ctx;
|
|
}
|
|
|
|
if (ctx_list.avail_ctx > num_online_cpus()) {
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wait_event(ctx_list.ctx_wait,
|
|
!list_empty(&ctx_list.idle_ctx));
|
|
continue;
|
|
}
|
|
|
|
ctx_list.avail_ctx++;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
|
|
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
|
|
if (!ctx) {
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
ctx_list.avail_ctx--;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wait_event(ctx_list.ctx_wait,
|
|
!list_empty(&ctx_list.idle_ctx));
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
|
|
spin_lock(&ctx_list.ctx_lock);
|
|
if (ctx_list.avail_ctx <= num_online_cpus()) {
|
|
list_add(&ctx->list, &ctx_list.idle_ctx);
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
wake_up(&ctx_list.ctx_wait);
|
|
return;
|
|
}
|
|
|
|
ctx_list.avail_ctx--;
|
|
spin_unlock(&ctx_list.ctx_lock);
|
|
ctx_free(ctx);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
if (id >= CRYPTO_SHASH_MAX)
|
|
return NULL;
|
|
|
|
ctx = ksmbd_find_crypto_ctx();
|
|
if (ctx->desc[id])
|
|
return ctx;
|
|
|
|
ctx->desc[id] = alloc_shash_desc(id);
|
|
if (ctx->desc[id])
|
|
return ctx;
|
|
ksmbd_release_crypto_ctx(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
|
|
{
|
|
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
|
|
}
|
|
|
|
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
if (id >= CRYPTO_AEAD_MAX)
|
|
return NULL;
|
|
|
|
ctx = ksmbd_find_crypto_ctx();
|
|
if (ctx->ccmaes[id])
|
|
return ctx;
|
|
|
|
ctx->ccmaes[id] = alloc_aead(id);
|
|
if (ctx->ccmaes[id])
|
|
return ctx;
|
|
ksmbd_release_crypto_ctx(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
|
|
{
|
|
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM);
|
|
}
|
|
|
|
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
|
|
{
|
|
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM);
|
|
}
|
|
|
|
void ksmbd_crypto_destroy(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
while (!list_empty(&ctx_list.idle_ctx)) {
|
|
ctx = list_entry(ctx_list.idle_ctx.next,
|
|
struct ksmbd_crypto_ctx,
|
|
list);
|
|
list_del(&ctx->list);
|
|
ctx_free(ctx);
|
|
}
|
|
}
|
|
|
|
int ksmbd_crypto_create(void)
|
|
{
|
|
struct ksmbd_crypto_ctx *ctx;
|
|
|
|
spin_lock_init(&ctx_list.ctx_lock);
|
|
INIT_LIST_HEAD(&ctx_list.idle_ctx);
|
|
init_waitqueue_head(&ctx_list.ctx_wait);
|
|
ctx_list.avail_ctx = 1;
|
|
|
|
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
list_add(&ctx->list, &ctx_list.idle_ctx);
|
|
return 0;
|
|
}
|