| // 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 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 void ctx_free(struct ksmbd_crypto_ctx *ctx) | |
| { | |
| int 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_obj(struct ksmbd_crypto_ctx, KSMBD_DEFAULT_GFP); | |
| 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_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_obj(struct ksmbd_crypto_ctx, KSMBD_DEFAULT_GFP); | |
| if (!ctx) | |
| return -ENOMEM; | |
| list_add(&ctx->list, &ctx_list.idle_ctx); | |
| return 0; | |
| } |