1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * crypto_bufcall(9F) group of routines.
29 */
30
31 #include <sys/types.h>
32 #include <sys/sunddi.h>
33 #include <sys/callb.h>
34 #include <sys/ksynch.h>
35 #include <sys/systm.h>
36 #include <sys/taskq_impl.h>
37 #include <sys/crypto/api.h>
38 #include <sys/crypto/sched_impl.h>
39
40 /*
41 * All pending crypto bufcalls are put on a list. cbuf_list_lock
42 * protects changes to this list.
43 *
44 * The following locking order is maintained in the code - The
45 * global cbuf_list_lock followed by the individual lock
46 * in a crypto bufcall structure (kc_lock).
47 */
48 kmutex_t cbuf_list_lock;
49 kcondvar_t cbuf_list_cv; /* cv the service thread waits on */
50 static kcf_cbuf_elem_t *cbuf_list_head;
51 static kcf_cbuf_elem_t *cbuf_list_tail;
52
53 /*
54 * Allocate and return a handle to be used for crypto_bufcall().
55 * Can be called from user context only.
56 */
57 crypto_bc_t
crypto_bufcall_alloc(void)58 crypto_bufcall_alloc(void)
59 {
60 kcf_cbuf_elem_t *cbufp;
61
62 cbufp = kmem_zalloc(sizeof (kcf_cbuf_elem_t), KM_SLEEP);
63 mutex_init(&cbufp->kc_lock, NULL, MUTEX_DEFAULT, NULL);
64 cv_init(&cbufp->kc_cv, NULL, CV_DEFAULT, NULL);
65 cbufp->kc_state = CBUF_FREE;
66
67 return (cbufp);
68 }
69
70 /*
71 * Free the handle if possible. Returns CRYPTO_SUCCESS if the handle
72 * is freed. Else it returns CRYPTO_BUSY.
73 *
74 * The client should do a crypto_unbufcall() if it receives a
75 * CRYPTO_BUSY.
76 *
77 * Can be called both from user and interrupt context.
78 */
79 int
crypto_bufcall_free(crypto_bc_t bc)80 crypto_bufcall_free(crypto_bc_t bc)
81 {
82 kcf_cbuf_elem_t *cbufp = (kcf_cbuf_elem_t *)bc;
83
84 mutex_enter(&cbufp->kc_lock);
85 if (cbufp->kc_state != CBUF_FREE) {
86 mutex_exit(&cbufp->kc_lock);
87 return (CRYPTO_BUSY);
88 }
89 mutex_exit(&cbufp->kc_lock);
90
91 mutex_destroy(&cbufp->kc_lock);
92 cv_destroy(&cbufp->kc_cv);
93 kmem_free(cbufp, sizeof (kcf_cbuf_elem_t));
94
95 return (CRYPTO_SUCCESS);
96 }
97
98 /*
99 * Schedule func() to be called when queue space is available to
100 * submit a crypto request.
101 *
102 * Can be called both from user and interrupt context.
103 */
104 int
crypto_bufcall(crypto_bc_t bc,void (* func)(void * arg),void * arg)105 crypto_bufcall(crypto_bc_t bc, void (*func)(void *arg), void *arg)
106 {
107 kcf_cbuf_elem_t *cbufp;
108
109 cbufp = (kcf_cbuf_elem_t *)bc;
110 if (cbufp == NULL || func == NULL) {
111 return (CRYPTO_ARGUMENTS_BAD);
112 }
113
114 mutex_enter(&cbuf_list_lock);
115 mutex_enter(&cbufp->kc_lock);
116 if (cbufp->kc_state != CBUF_FREE) {
117 mutex_exit(&cbufp->kc_lock);
118 mutex_exit(&cbuf_list_lock);
119 return (CRYPTO_BUSY);
120 }
121
122 cbufp->kc_state = CBUF_WAITING;
123 cbufp->kc_func = func;
124 cbufp->kc_arg = arg;
125 cbufp->kc_prev = cbufp->kc_next = NULL;
126
127 if (cbuf_list_head == NULL) {
128 cbuf_list_head = cbuf_list_tail = cbufp;
129 } else {
130 cbuf_list_tail->kc_next = cbufp;
131 cbufp->kc_prev = cbuf_list_tail;
132 cbuf_list_tail = cbufp;
133 }
134
135 /*
136 * Signal the crypto_bufcall_service thread to start
137 * working on this crypto bufcall request.
138 */
139 cv_signal(&cbuf_list_cv);
140 mutex_exit(&cbufp->kc_lock);
141 mutex_exit(&cbuf_list_lock);
142
143 return (CRYPTO_SUCCESS);
144 }
145
146 /*
147 * Cancel a pending crypto bufcall request. If the bufcall
148 * is currently executing, we wait till it is complete.
149 *
150 * Can only be called from user context.
151 */
152 int
crypto_unbufcall(crypto_bc_t bc)153 crypto_unbufcall(crypto_bc_t bc)
154 {
155 kcf_cbuf_elem_t *cbufp = (kcf_cbuf_elem_t *)bc;
156
157 mutex_enter(&cbuf_list_lock);
158 mutex_enter(&cbufp->kc_lock);
159
160 if (cbufp->kc_state == CBUF_WAITING) {
161 kcf_cbuf_elem_t *nextp = cbufp->kc_next;
162 kcf_cbuf_elem_t *prevp = cbufp->kc_prev;
163
164 if (nextp != NULL)
165 nextp->kc_prev = prevp;
166 else
167 cbuf_list_tail = prevp;
168
169 if (prevp != NULL)
170 prevp->kc_next = nextp;
171 else
172 cbuf_list_head = nextp;
173 cbufp->kc_state = CBUF_FREE;
174 } else if (cbufp->kc_state == CBUF_RUNNING) {
175 mutex_exit(&cbuf_list_lock);
176 /*
177 * crypto_bufcall_service thread is working
178 * on this element. We will wait for that
179 * thread to signal us when done.
180 */
181 while (cbufp->kc_state == CBUF_RUNNING)
182 cv_wait(&cbufp->kc_cv, &cbufp->kc_lock);
183 mutex_exit(&cbufp->kc_lock);
184
185 return (CRYPTO_SUCCESS);
186 }
187
188 mutex_exit(&cbufp->kc_lock);
189 mutex_exit(&cbuf_list_lock);
190
191 return (CRYPTO_SUCCESS);
192 }
193
194 /*
195 * We sample the number of jobs. We do not hold the lock
196 * as it is not necessary to get the exact count.
197 */
198 #define KCF_GSWQ_AVAIL (gswq->gs_maxjobs - gswq->gs_njobs)
199
200 /*
201 * One queue space each for init, update, and final.
202 */
203 #define GSWQ_MINFREE 3
204
205 /*
206 * Go through the list of crypto bufcalls and do the necessary
207 * callbacks.
208 */
209 static void
kcf_run_cbufcalls(void)210 kcf_run_cbufcalls(void)
211 {
212 kcf_cbuf_elem_t *cbufp;
213 int count;
214
215 mutex_enter(&cbuf_list_lock);
216
217 /*
218 * Get estimate of available queue space from KCF_GSWQ_AVAIL.
219 * We can call 'n' crypto bufcall callback functions where
220 * n * GSWQ_MINFREE <= available queue space.
221 *
222 * TO DO - Extend the check to taskqs of hardware providers.
223 * For now, we handle only the software providers.
224 */
225 count = KCF_GSWQ_AVAIL;
226 while ((cbufp = cbuf_list_head) != NULL) {
227 if (GSWQ_MINFREE <= count) {
228 count -= GSWQ_MINFREE;
229 mutex_enter(&cbufp->kc_lock);
230 cbuf_list_head = cbufp->kc_next;
231 cbufp->kc_state = CBUF_RUNNING;
232 mutex_exit(&cbufp->kc_lock);
233 mutex_exit(&cbuf_list_lock);
234
235 (*cbufp->kc_func)(cbufp->kc_arg);
236
237 mutex_enter(&cbufp->kc_lock);
238 cbufp->kc_state = CBUF_FREE;
239 cv_broadcast(&cbufp->kc_cv);
240 mutex_exit(&cbufp->kc_lock);
241
242 mutex_enter(&cbuf_list_lock);
243 } else {
244 /*
245 * There is not enough queue space in this
246 * round. We bail out and try again
247 * later.
248 */
249 break;
250 }
251 }
252 if (cbuf_list_head == NULL)
253 cbuf_list_tail = NULL;
254
255 mutex_exit(&cbuf_list_lock);
256 }
257
258 /*
259 * Background processing of crypto bufcalls.
260 */
261 void
crypto_bufcall_service(void)262 crypto_bufcall_service(void)
263 {
264 callb_cpr_t cprinfo;
265
266 CALLB_CPR_INIT(&cprinfo, &cbuf_list_lock, callb_generic_cpr,
267 "crypto_bufcall_service");
268
269 mutex_enter(&cbuf_list_lock);
270
271 for (;;) {
272 if (cbuf_list_head != NULL && KCF_GSWQ_AVAIL >= GSWQ_MINFREE) {
273 mutex_exit(&cbuf_list_lock);
274 kcf_run_cbufcalls();
275 mutex_enter(&cbuf_list_lock);
276 }
277
278 if (cbuf_list_head != NULL) {
279 /*
280 * Wait 30 seconds for queue space to become available.
281 * This number is reasonable as it does not cause
282 * much CPU overhead. We could wait on a condition
283 * variable and the global software dequeue routine can
284 * signal us. But, it adds overhead to that routine
285 * which we want to avoid. Also, the client is prepared
286 * to wait any way.
287 */
288 CALLB_CPR_SAFE_BEGIN(&cprinfo);
289 mutex_exit(&cbuf_list_lock);
290 delay(30 * drv_usectohz(1000000));
291 mutex_enter(&cbuf_list_lock);
292 CALLB_CPR_SAFE_END(&cprinfo, &cbuf_list_lock);
293 }
294
295 /* Wait for new work to arrive */
296 if (cbuf_list_head == NULL) {
297 CALLB_CPR_SAFE_BEGIN(&cprinfo);
298 cv_wait(&cbuf_list_cv, &cbuf_list_lock);
299 CALLB_CPR_SAFE_END(&cprinfo, &cbuf_list_lock);
300 }
301 }
302 }
303