xref: /linux/fs/smb/server/crypto_ctx.c (revision 39f1c201b93f4ff71631bac72cff6eb155f976a4)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   Copyright (C) 2019 Samsung Electronics Co., Ltd.
4  */
5 
6 #include <linux/kernel.h>
7 #include <linux/string.h>
8 #include <linux/err.h>
9 #include <linux/slab.h>
10 #include <linux/wait.h>
11 #include <linux/sched.h>
12 
13 #include "glob.h"
14 #include "crypto_ctx.h"
15 
16 struct crypto_ctx_list {
17 	spinlock_t		ctx_lock;
18 	int			avail_ctx;
19 	struct list_head	idle_ctx;
20 	wait_queue_head_t	ctx_wait;
21 };
22 
23 static struct crypto_ctx_list ctx_list;
24 
25 static inline void free_aead(struct crypto_aead *aead)
26 {
27 	if (aead)
28 		crypto_free_aead(aead);
29 }
30 
31 static struct crypto_aead *alloc_aead(int id)
32 {
33 	struct crypto_aead *tfm = NULL;
34 
35 	switch (id) {
36 	case CRYPTO_AEAD_AES_GCM:
37 		tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
38 		break;
39 	case CRYPTO_AEAD_AES_CCM:
40 		tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
41 		break;
42 	default:
43 		pr_err("Does not support encrypt ahead(id : %d)\n", id);
44 		return NULL;
45 	}
46 
47 	if (IS_ERR(tfm)) {
48 		pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
49 		return NULL;
50 	}
51 
52 	return tfm;
53 }
54 
55 static void ctx_free(struct ksmbd_crypto_ctx *ctx)
56 {
57 	int i;
58 
59 	for (i = 0; i < CRYPTO_AEAD_MAX; i++)
60 		free_aead(ctx->ccmaes[i]);
61 	kfree(ctx);
62 }
63 
64 static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
65 {
66 	struct ksmbd_crypto_ctx *ctx;
67 
68 	while (1) {
69 		spin_lock(&ctx_list.ctx_lock);
70 		if (!list_empty(&ctx_list.idle_ctx)) {
71 			ctx = list_entry(ctx_list.idle_ctx.next,
72 					 struct ksmbd_crypto_ctx,
73 					 list);
74 			list_del(&ctx->list);
75 			spin_unlock(&ctx_list.ctx_lock);
76 			return ctx;
77 		}
78 
79 		if (ctx_list.avail_ctx > num_online_cpus()) {
80 			spin_unlock(&ctx_list.ctx_lock);
81 			wait_event(ctx_list.ctx_wait,
82 				   !list_empty(&ctx_list.idle_ctx));
83 			continue;
84 		}
85 
86 		ctx_list.avail_ctx++;
87 		spin_unlock(&ctx_list.ctx_lock);
88 
89 		ctx = kzalloc_obj(struct ksmbd_crypto_ctx, KSMBD_DEFAULT_GFP);
90 		if (!ctx) {
91 			spin_lock(&ctx_list.ctx_lock);
92 			ctx_list.avail_ctx--;
93 			spin_unlock(&ctx_list.ctx_lock);
94 			wait_event(ctx_list.ctx_wait,
95 				   !list_empty(&ctx_list.idle_ctx));
96 			continue;
97 		}
98 		break;
99 	}
100 	return ctx;
101 }
102 
103 void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
104 {
105 	if (!ctx)
106 		return;
107 
108 	spin_lock(&ctx_list.ctx_lock);
109 	if (ctx_list.avail_ctx <= num_online_cpus()) {
110 		list_add(&ctx->list, &ctx_list.idle_ctx);
111 		spin_unlock(&ctx_list.ctx_lock);
112 		wake_up(&ctx_list.ctx_wait);
113 		return;
114 	}
115 
116 	ctx_list.avail_ctx--;
117 	spin_unlock(&ctx_list.ctx_lock);
118 	ctx_free(ctx);
119 }
120 
121 static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
122 {
123 	struct ksmbd_crypto_ctx *ctx;
124 
125 	if (id >= CRYPTO_AEAD_MAX)
126 		return NULL;
127 
128 	ctx = ksmbd_find_crypto_ctx();
129 	if (ctx->ccmaes[id])
130 		return ctx;
131 
132 	ctx->ccmaes[id] = alloc_aead(id);
133 	if (ctx->ccmaes[id])
134 		return ctx;
135 	ksmbd_release_crypto_ctx(ctx);
136 	return NULL;
137 }
138 
139 struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
140 {
141 	return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM);
142 }
143 
144 struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
145 {
146 	return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM);
147 }
148 
149 void ksmbd_crypto_destroy(void)
150 {
151 	struct ksmbd_crypto_ctx *ctx;
152 
153 	while (!list_empty(&ctx_list.idle_ctx)) {
154 		ctx = list_entry(ctx_list.idle_ctx.next,
155 				 struct ksmbd_crypto_ctx,
156 				 list);
157 		list_del(&ctx->list);
158 		ctx_free(ctx);
159 	}
160 }
161 
162 int ksmbd_crypto_create(void)
163 {
164 	struct ksmbd_crypto_ctx *ctx;
165 
166 	spin_lock_init(&ctx_list.ctx_lock);
167 	INIT_LIST_HEAD(&ctx_list.idle_ctx);
168 	init_waitqueue_head(&ctx_list.ctx_wait);
169 	ctx_list.avail_ctx = 1;
170 
171 	ctx = kzalloc_obj(struct ksmbd_crypto_ctx, KSMBD_DEFAULT_GFP);
172 	if (!ctx)
173 		return -ENOMEM;
174 	list_add(&ctx->list, &ctx_list.idle_ctx);
175 	return 0;
176 }
177