]>
Commit | Line | Data |
---|---|---|
a3667c31 MC |
1 | /* crypto/async/async.c */ |
2 | /* | |
3 | * Written by Matt Caswell (matt@openssl.org) for the OpenSSL project. | |
4 | */ | |
5 | /* ==================================================================== | |
6 | * Copyright (c) 2015 The OpenSSL Project. All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in | |
17 | * the documentation and/or other materials provided with the | |
18 | * distribution. | |
19 | * | |
20 | * 3. All advertising materials mentioning features or use of this | |
21 | * software must display the following acknowledgment: | |
22 | * "This product includes software developed by the OpenSSL Project | |
23 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" | |
24 | * | |
25 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to | |
26 | * endorse or promote products derived from this software without | |
27 | * prior written permission. For written permission, please contact | |
28 | * licensing@OpenSSL.org. | |
29 | * | |
30 | * 5. Products derived from this software may not be called "OpenSSL" | |
31 | * nor may "OpenSSL" appear in their names without prior written | |
32 | * permission of the OpenSSL Project. | |
33 | * | |
34 | * 6. Redistributions of any form whatsoever must retain the following | |
35 | * acknowledgment: | |
36 | * "This product includes software developed by the OpenSSL Project | |
37 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" | |
38 | * | |
39 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY | |
40 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
42 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR | |
43 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
44 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
45 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
46 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
48 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
49 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
50 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
51 | * ==================================================================== | |
52 | */ | |
53 | ||
4abc7681 MC |
54 | /* |
55 | * Without this we start getting longjmp crashes because it thinks we're jumping | |
56 | * up the stack when in fact we are jumping to an entirely different stack. The | |
57 | * cost of this is not having certain buffer overrun/underrun checks etc for | |
58 | * this source file :-( | |
59 | */ | |
60 | #undef _FORTIFY_SOURCE | |
61 | ||
079a1a90 | 62 | #include <openssl/err.h> |
a3667c31 | 63 | #include <openssl/async.h> |
a3667c31 | 64 | #include <string.h> |
50108304 | 65 | #include "async_locl.h" |
a3667c31 MC |
66 | |
67 | #define ASYNC_JOB_RUNNING 0 | |
68 | #define ASYNC_JOB_PAUSING 1 | |
69 | #define ASYNC_JOB_PAUSED 2 | |
70 | #define ASYNC_JOB_STOPPING 3 | |
71 | ||
636ca4ff | 72 | static async_ctx *async_ctx_new(void) |
a3667c31 | 73 | { |
636ca4ff | 74 | async_ctx *nctx = NULL; |
a3667c31 | 75 | |
e38565f5 MC |
76 | nctx = OPENSSL_malloc(sizeof (async_ctx)); |
77 | if (nctx == NULL) { | |
079a1a90 | 78 | ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE); |
a3667c31 MC |
79 | goto err; |
80 | } | |
81 | ||
636ca4ff | 82 | async_fibre_init_dispatcher(&nctx->dispatcher); |
a3667c31 | 83 | nctx->currjob = NULL; |
e8dfb5bf | 84 | nctx->blocked = 0; |
e38565f5 | 85 | if (!async_set_ctx(nctx)) |
9ec1e031 | 86 | goto err; |
a3667c31 MC |
87 | |
88 | return nctx; | |
89 | err: | |
e38565f5 | 90 | OPENSSL_free(nctx); |
a3667c31 MC |
91 | |
92 | return NULL; | |
93 | } | |
94 | ||
636ca4ff | 95 | static int async_ctx_free(void) |
a3667c31 | 96 | { |
e38565f5 | 97 | async_ctx *ctx; |
a3667c31 | 98 | |
e38565f5 MC |
99 | ctx = async_get_ctx(); |
100 | ||
101 | if (!async_set_ctx(NULL)) | |
9ec1e031 | 102 | return 0; |
a3667c31 | 103 | |
e38565f5 MC |
104 | OPENSSL_free(ctx); |
105 | ||
a3667c31 MC |
106 | return 1; |
107 | } | |
108 | ||
636ca4ff | 109 | static ASYNC_JOB *async_job_new(void) |
a3667c31 MC |
110 | { |
111 | ASYNC_JOB *job = NULL; | |
2b2c78d4 | 112 | OSSL_ASYNC_FD pipefds[2]; |
a3667c31 | 113 | |
e38565f5 MC |
114 | job = OPENSSL_malloc(sizeof (ASYNC_JOB)); |
115 | if (job == NULL) { | |
079a1a90 | 116 | ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE); |
50108304 | 117 | return NULL; |
a3667c31 MC |
118 | } |
119 | ||
e38565f5 | 120 | if (!async_pipe(pipefds)) { |
f4da39d2 | 121 | OPENSSL_free(job); |
079a1a90 | 122 | ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ASYNC_R_CANNOT_CREATE_WAIT_PIPE); |
f4da39d2 MC |
123 | return NULL; |
124 | } | |
125 | ||
126 | job->wake_set = 0; | |
127 | job->wait_fd = pipefds[0]; | |
128 | job->wake_fd = pipefds[1]; | |
129 | ||
a3667c31 MC |
130 | job->status = ASYNC_JOB_RUNNING; |
131 | job->funcargs = NULL; | |
132 | ||
133 | return job; | |
a3667c31 MC |
134 | } |
135 | ||
636ca4ff | 136 | static void async_job_free(ASYNC_JOB *job) |
a3667c31 | 137 | { |
e38565f5 MC |
138 | if (job != NULL) { |
139 | OPENSSL_free(job->funcargs); | |
636ca4ff | 140 | async_fibre_free(&job->fibrectx); |
a3667c31 MC |
141 | OPENSSL_free(job); |
142 | } | |
143 | } | |
144 | ||
252d6d3a | 145 | static ASYNC_JOB *async_get_pool_job(void) { |
a3667c31 | 146 | ASYNC_JOB *job; |
0ff2b9ac | 147 | STACK_OF(ASYNC_JOB) *pool; |
a3667c31 | 148 | |
0ff2b9ac | 149 | pool = async_get_pool(); |
252d6d3a | 150 | if (pool == NULL) { |
50108304 | 151 | /* |
252d6d3a | 152 | * Pool has not been initialised, so init with the defaults, i.e. |
9f078e19 | 153 | * no max size and no pre-created jobs |
50108304 | 154 | */ |
0ff2b9ac | 155 | if (ASYNC_init_pool(0, 0) == 0) |
252d6d3a | 156 | return NULL; |
0ff2b9ac | 157 | pool = async_get_pool(); |
252d6d3a MC |
158 | } |
159 | ||
160 | job = sk_ASYNC_JOB_pop(pool); | |
161 | if (job == NULL) { | |
162 | /* Pool is empty */ | |
0ff2b9ac | 163 | if (!async_pool_can_grow()) |
252d6d3a | 164 | return NULL; |
0ff2b9ac | 165 | |
636ca4ff | 166 | job = async_job_new(); |
252d6d3a | 167 | if (job) { |
636ca4ff | 168 | async_fibre_makecontext(&job->fibrectx); |
0ff2b9ac | 169 | async_increment_pool_size(); |
252d6d3a MC |
170 | } |
171 | } | |
172 | return job; | |
173 | } | |
174 | ||
175 | static void async_release_job(ASYNC_JOB *job) { | |
e38565f5 | 176 | OPENSSL_free(job->funcargs); |
252d6d3a MC |
177 | job->funcargs = NULL; |
178 | /* Ignore error return */ | |
0ff2b9ac | 179 | async_release_job_to_pool(job); |
252d6d3a MC |
180 | } |
181 | ||
636ca4ff | 182 | void async_start_func(void) |
252d6d3a MC |
183 | { |
184 | ASYNC_JOB *job; | |
185 | ||
186 | while (1) { | |
187 | /* Run the job */ | |
636ca4ff | 188 | job = async_get_ctx()->currjob; |
252d6d3a MC |
189 | job->ret = job->func(job->funcargs); |
190 | ||
191 | /* Stop the job */ | |
192 | job->status = ASYNC_JOB_STOPPING; | |
e38565f5 MC |
193 | if (!async_fibre_swapcontext(&job->fibrectx, |
194 | &async_get_ctx()->dispatcher, 1)) { | |
252d6d3a | 195 | /* |
079a1a90 MC |
196 | * Should not happen. Getting here will close the thread...can't do |
197 | * much about it | |
252d6d3a | 198 | */ |
079a1a90 | 199 | ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
252d6d3a | 200 | } |
50108304 | 201 | } |
a3667c31 MC |
202 | } |
203 | ||
204 | int ASYNC_start_job(ASYNC_JOB **job, int *ret, int (*func)(void *), | |
205 | void *args, size_t size) | |
206 | { | |
e38565f5 | 207 | if (async_get_ctx() == NULL && async_ctx_new() == NULL) { |
a3667c31 MC |
208 | return ASYNC_ERR; |
209 | } | |
210 | ||
e38565f5 | 211 | if (*job) { |
636ca4ff | 212 | async_get_ctx()->currjob = *job; |
a3667c31 MC |
213 | } |
214 | ||
50108304 | 215 | for (;;) { |
e38565f5 MC |
216 | if (async_get_ctx()->currjob != NULL) { |
217 | if (async_get_ctx()->currjob->status == ASYNC_JOB_STOPPING) { | |
636ca4ff MC |
218 | *ret = async_get_ctx()->currjob->ret; |
219 | async_release_job(async_get_ctx()->currjob); | |
220 | async_get_ctx()->currjob = NULL; | |
82676094 | 221 | *job = NULL; |
50108304 MC |
222 | return ASYNC_FINISH; |
223 | } | |
224 | ||
e38565f5 | 225 | if (async_get_ctx()->currjob->status == ASYNC_JOB_PAUSING) { |
636ca4ff MC |
226 | *job = async_get_ctx()->currjob; |
227 | async_get_ctx()->currjob->status = ASYNC_JOB_PAUSED; | |
228 | async_get_ctx()->currjob = NULL; | |
50108304 MC |
229 | return ASYNC_PAUSE; |
230 | } | |
231 | ||
e38565f5 | 232 | if (async_get_ctx()->currjob->status == ASYNC_JOB_PAUSED) { |
636ca4ff | 233 | async_get_ctx()->currjob = *job; |
50108304 | 234 | /* Resume previous job */ |
e38565f5 MC |
235 | if (!async_fibre_swapcontext(&async_get_ctx()->dispatcher, |
236 | &async_get_ctx()->currjob->fibrectx, 1)) { | |
079a1a90 MC |
237 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, |
238 | ASYNC_R_FAILED_TO_SWAP_CONTEXT); | |
50108304 | 239 | goto err; |
079a1a90 | 240 | } |
50108304 MC |
241 | continue; |
242 | } | |
243 | ||
244 | /* Should not happen */ | |
079a1a90 | 245 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR); |
636ca4ff MC |
246 | async_release_job(async_get_ctx()->currjob); |
247 | async_get_ctx()->currjob = NULL; | |
82676094 | 248 | *job = NULL; |
50108304 | 249 | return ASYNC_ERR; |
a3667c31 MC |
250 | } |
251 | ||
50108304 | 252 | /* Start a new job */ |
e38565f5 | 253 | if ((async_get_ctx()->currjob = async_get_pool_job()) == NULL) { |
252d6d3a | 254 | return ASYNC_NO_JOBS; |
a3667c31 MC |
255 | } |
256 | ||
e38565f5 | 257 | if (args != NULL) { |
636ca4ff | 258 | async_get_ctx()->currjob->funcargs = OPENSSL_malloc(size); |
e38565f5 | 259 | if (async_get_ctx()->currjob->funcargs == NULL) { |
079a1a90 | 260 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE); |
636ca4ff MC |
261 | async_release_job(async_get_ctx()->currjob); |
262 | async_get_ctx()->currjob = NULL; | |
50108304 MC |
263 | return ASYNC_ERR; |
264 | } | |
636ca4ff | 265 | memcpy(async_get_ctx()->currjob->funcargs, args, size); |
50108304 | 266 | } else { |
636ca4ff | 267 | async_get_ctx()->currjob->funcargs = NULL; |
a3667c31 MC |
268 | } |
269 | ||
636ca4ff | 270 | async_get_ctx()->currjob->func = func; |
e38565f5 MC |
271 | if (!async_fibre_swapcontext(&async_get_ctx()->dispatcher, |
272 | &async_get_ctx()->currjob->fibrectx, 1)) { | |
079a1a90 | 273 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
50108304 | 274 | goto err; |
079a1a90 | 275 | } |
a3667c31 MC |
276 | } |
277 | ||
50108304 | 278 | err: |
636ca4ff MC |
279 | async_release_job(async_get_ctx()->currjob); |
280 | async_get_ctx()->currjob = NULL; | |
82676094 | 281 | *job = NULL; |
a3667c31 MC |
282 | return ASYNC_ERR; |
283 | } | |
284 | ||
285 | ||
286 | int ASYNC_pause_job(void) | |
287 | { | |
288 | ASYNC_JOB *job; | |
289 | ||
e8dfb5bf MC |
290 | if (async_get_ctx() == NULL |
291 | || async_get_ctx()->currjob == NULL | |
292 | || async_get_ctx()->blocked) { | |
079a1a90 | 293 | /* |
05a6347f MC |
294 | * Could be we've deliberately not been started within a job so this is |
295 | * counted as success. | |
079a1a90 | 296 | */ |
05a6347f | 297 | return 1; |
079a1a90 | 298 | } |
a3667c31 | 299 | |
636ca4ff | 300 | job = async_get_ctx()->currjob; |
a3667c31 MC |
301 | job->status = ASYNC_JOB_PAUSING; |
302 | ||
e8dfb5bf MC |
303 | if (!async_fibre_swapcontext(&job->fibrectx, |
304 | &async_get_ctx()->dispatcher, 1)) { | |
079a1a90 | 305 | ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
a3667c31 MC |
306 | return 0; |
307 | } | |
308 | ||
309 | return 1; | |
310 | } | |
311 | ||
d63de0eb MC |
312 | static void async_empty_pool(STACK_OF(ASYNC_JOB) *pool) |
313 | { | |
314 | ASYNC_JOB *job; | |
315 | ||
316 | do { | |
317 | job = sk_ASYNC_JOB_pop(pool); | |
636ca4ff | 318 | async_job_free(job); |
d63de0eb MC |
319 | } while (job); |
320 | } | |
321 | ||
0ff2b9ac | 322 | int ASYNC_init_pool(size_t max_size, size_t init_size) |
252d6d3a | 323 | { |
0ff2b9ac MC |
324 | STACK_OF(ASYNC_JOB) *pool; |
325 | size_t curr_size = 0; | |
326 | ||
079a1a90 MC |
327 | if (init_size > max_size) { |
328 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ASYNC_R_INVALID_POOL_SIZE); | |
252d6d3a | 329 | return 0; |
079a1a90 | 330 | } |
252d6d3a | 331 | |
252d6d3a MC |
332 | pool = sk_ASYNC_JOB_new_null(); |
333 | if (pool == NULL) { | |
079a1a90 | 334 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ERR_R_MALLOC_FAILURE); |
252d6d3a MC |
335 | return 0; |
336 | } | |
337 | /* Pre-create jobs as required */ | |
338 | while (init_size) { | |
339 | ASYNC_JOB *job; | |
636ca4ff | 340 | job = async_job_new(); |
252d6d3a | 341 | if (job) { |
636ca4ff | 342 | async_fibre_makecontext(&job->fibrectx); |
252d6d3a MC |
343 | job->funcargs = NULL; |
344 | sk_ASYNC_JOB_push(pool, job); | |
345 | curr_size++; | |
346 | init_size--; | |
347 | } else { | |
348 | /* | |
349 | * Not actually fatal because we already created the pool, just skip | |
350 | * creation of any more jobs | |
351 | */ | |
352 | init_size = 0; | |
353 | } | |
354 | } | |
355 | ||
d63de0eb | 356 | if (!async_set_pool(pool, curr_size, max_size)) { |
079a1a90 | 357 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ASYNC_R_FAILED_TO_SET_POOL); |
d63de0eb MC |
358 | async_empty_pool(pool); |
359 | sk_ASYNC_JOB_free(pool); | |
360 | return 0; | |
361 | } | |
0ff2b9ac | 362 | |
252d6d3a MC |
363 | return 1; |
364 | } | |
365 | ||
366 | void ASYNC_free_pool(void) | |
367 | { | |
0ff2b9ac | 368 | STACK_OF(ASYNC_JOB) *pool; |
252d6d3a | 369 | |
0ff2b9ac MC |
370 | pool = async_get_pool(); |
371 | if (pool == NULL) | |
372 | return; | |
d63de0eb MC |
373 | |
374 | async_empty_pool(pool); | |
0ff2b9ac | 375 | async_release_pool(); |
636ca4ff | 376 | async_ctx_free(); |
252d6d3a | 377 | } |
f4da39d2 MC |
378 | |
379 | ASYNC_JOB *ASYNC_get_current_job(void) | |
380 | { | |
636ca4ff | 381 | async_ctx *ctx; |
e38565f5 MC |
382 | |
383 | ctx = async_get_ctx(); | |
384 | if(ctx == NULL) | |
f4da39d2 MC |
385 | return NULL; |
386 | ||
387 | return ctx->currjob; | |
388 | } | |
389 | ||
2b2c78d4 | 390 | OSSL_ASYNC_FD ASYNC_get_wait_fd(ASYNC_JOB *job) |
f4da39d2 MC |
391 | { |
392 | return job->wait_fd; | |
393 | } | |
394 | ||
395 | void ASYNC_wake(ASYNC_JOB *job) | |
396 | { | |
397 | char dummy = 0; | |
398 | ||
399 | if (job->wake_set) | |
400 | return; | |
401 | async_write1(job->wake_fd, &dummy); | |
402 | job->wake_set = 1; | |
403 | } | |
404 | ||
405 | void ASYNC_clear_wake(ASYNC_JOB *job) | |
406 | { | |
407 | char dummy = 0; | |
408 | if (!job->wake_set) | |
409 | return; | |
410 | async_read1(job->wait_fd, &dummy); | |
411 | job->wake_set = 0; | |
412 | } | |
e8dfb5bf MC |
413 | |
414 | void ASYNC_block_pause(void) | |
415 | { | |
416 | if (async_get_ctx() == NULL | |
417 | || async_get_ctx()->currjob == NULL) { | |
418 | /* | |
419 | * We're not in a job anyway so ignore this | |
420 | */ | |
421 | return; | |
422 | } | |
423 | async_get_ctx()->blocked++; | |
424 | } | |
425 | ||
426 | void ASYNC_unblock_pause(void) | |
427 | { | |
428 | if (async_get_ctx() == NULL | |
429 | || async_get_ctx()->currjob == NULL) { | |
430 | /* | |
431 | * We're not in a job anyway so ignore this | |
432 | */ | |
433 | return; | |
434 | } | |
435 | if(async_get_ctx()->blocked > 0) | |
436 | async_get_ctx()->blocked--; | |
437 | } |