]>
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 | ||
27949c35 MC |
72 | static void async_free_pool_internal(async_pool *pool); |
73 | ||
636ca4ff | 74 | static async_ctx *async_ctx_new(void) |
a3667c31 | 75 | { |
636ca4ff | 76 | async_ctx *nctx = NULL; |
a3667c31 | 77 | |
e38565f5 MC |
78 | nctx = OPENSSL_malloc(sizeof (async_ctx)); |
79 | if (nctx == NULL) { | |
079a1a90 | 80 | ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE); |
a3667c31 MC |
81 | goto err; |
82 | } | |
83 | ||
636ca4ff | 84 | async_fibre_init_dispatcher(&nctx->dispatcher); |
a3667c31 | 85 | nctx->currjob = NULL; |
e8dfb5bf | 86 | nctx->blocked = 0; |
e38565f5 | 87 | if (!async_set_ctx(nctx)) |
9ec1e031 | 88 | goto err; |
a3667c31 MC |
89 | |
90 | return nctx; | |
91 | err: | |
e38565f5 | 92 | OPENSSL_free(nctx); |
a3667c31 MC |
93 | |
94 | return NULL; | |
95 | } | |
96 | ||
636ca4ff | 97 | static int async_ctx_free(void) |
a3667c31 | 98 | { |
e38565f5 | 99 | async_ctx *ctx; |
a3667c31 | 100 | |
e38565f5 MC |
101 | ctx = async_get_ctx(); |
102 | ||
103 | if (!async_set_ctx(NULL)) | |
9ec1e031 | 104 | return 0; |
a3667c31 | 105 | |
e38565f5 MC |
106 | OPENSSL_free(ctx); |
107 | ||
a3667c31 MC |
108 | return 1; |
109 | } | |
110 | ||
636ca4ff | 111 | static ASYNC_JOB *async_job_new(void) |
a3667c31 MC |
112 | { |
113 | ASYNC_JOB *job = NULL; | |
2b2c78d4 | 114 | OSSL_ASYNC_FD pipefds[2]; |
a3667c31 | 115 | |
e38565f5 MC |
116 | job = OPENSSL_malloc(sizeof (ASYNC_JOB)); |
117 | if (job == NULL) { | |
079a1a90 | 118 | ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE); |
50108304 | 119 | return NULL; |
a3667c31 MC |
120 | } |
121 | ||
e38565f5 | 122 | if (!async_pipe(pipefds)) { |
f4da39d2 | 123 | OPENSSL_free(job); |
079a1a90 | 124 | ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ASYNC_R_CANNOT_CREATE_WAIT_PIPE); |
f4da39d2 MC |
125 | return NULL; |
126 | } | |
127 | ||
128 | job->wake_set = 0; | |
129 | job->wait_fd = pipefds[0]; | |
130 | job->wake_fd = pipefds[1]; | |
131 | ||
a3667c31 MC |
132 | job->status = ASYNC_JOB_RUNNING; |
133 | job->funcargs = NULL; | |
134 | ||
135 | return job; | |
a3667c31 MC |
136 | } |
137 | ||
636ca4ff | 138 | static void async_job_free(ASYNC_JOB *job) |
a3667c31 | 139 | { |
e38565f5 MC |
140 | if (job != NULL) { |
141 | OPENSSL_free(job->funcargs); | |
636ca4ff | 142 | async_fibre_free(&job->fibrectx); |
27949c35 MC |
143 | async_close_fd(job->wait_fd); |
144 | async_close_fd(job->wake_fd); | |
a3667c31 MC |
145 | OPENSSL_free(job); |
146 | } | |
147 | } | |
148 | ||
252d6d3a | 149 | static ASYNC_JOB *async_get_pool_job(void) { |
a3667c31 | 150 | ASYNC_JOB *job; |
27949c35 | 151 | async_pool *pool; |
a3667c31 | 152 | |
0ff2b9ac | 153 | pool = async_get_pool(); |
252d6d3a | 154 | if (pool == NULL) { |
50108304 | 155 | /* |
252d6d3a | 156 | * Pool has not been initialised, so init with the defaults, i.e. |
9f078e19 | 157 | * no max size and no pre-created jobs |
50108304 | 158 | */ |
0ff2b9ac | 159 | if (ASYNC_init_pool(0, 0) == 0) |
252d6d3a | 160 | return NULL; |
0ff2b9ac | 161 | pool = async_get_pool(); |
252d6d3a MC |
162 | } |
163 | ||
27949c35 | 164 | job = sk_ASYNC_JOB_pop(pool->jobs); |
252d6d3a MC |
165 | if (job == NULL) { |
166 | /* Pool is empty */ | |
27949c35 | 167 | if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size)) |
252d6d3a | 168 | return NULL; |
0ff2b9ac | 169 | |
636ca4ff | 170 | job = async_job_new(); |
252d6d3a | 171 | if (job) { |
636ca4ff | 172 | async_fibre_makecontext(&job->fibrectx); |
27949c35 | 173 | pool->curr_size++; |
252d6d3a MC |
174 | } |
175 | } | |
176 | return job; | |
177 | } | |
178 | ||
179 | static void async_release_job(ASYNC_JOB *job) { | |
27949c35 MC |
180 | async_pool *pool; |
181 | ||
182 | pool = async_get_pool(); | |
e38565f5 | 183 | OPENSSL_free(job->funcargs); |
252d6d3a | 184 | job->funcargs = NULL; |
27949c35 | 185 | sk_ASYNC_JOB_push(pool->jobs, job); |
252d6d3a MC |
186 | } |
187 | ||
636ca4ff | 188 | void async_start_func(void) |
252d6d3a MC |
189 | { |
190 | ASYNC_JOB *job; | |
191 | ||
192 | while (1) { | |
193 | /* Run the job */ | |
636ca4ff | 194 | job = async_get_ctx()->currjob; |
252d6d3a MC |
195 | job->ret = job->func(job->funcargs); |
196 | ||
197 | /* Stop the job */ | |
198 | job->status = ASYNC_JOB_STOPPING; | |
e38565f5 MC |
199 | if (!async_fibre_swapcontext(&job->fibrectx, |
200 | &async_get_ctx()->dispatcher, 1)) { | |
252d6d3a | 201 | /* |
079a1a90 MC |
202 | * Should not happen. Getting here will close the thread...can't do |
203 | * much about it | |
252d6d3a | 204 | */ |
079a1a90 | 205 | ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
252d6d3a | 206 | } |
50108304 | 207 | } |
a3667c31 MC |
208 | } |
209 | ||
210 | int ASYNC_start_job(ASYNC_JOB **job, int *ret, int (*func)(void *), | |
211 | void *args, size_t size) | |
212 | { | |
e38565f5 | 213 | if (async_get_ctx() == NULL && async_ctx_new() == NULL) { |
a3667c31 MC |
214 | return ASYNC_ERR; |
215 | } | |
216 | ||
e38565f5 | 217 | if (*job) { |
636ca4ff | 218 | async_get_ctx()->currjob = *job; |
a3667c31 MC |
219 | } |
220 | ||
50108304 | 221 | for (;;) { |
e38565f5 MC |
222 | if (async_get_ctx()->currjob != NULL) { |
223 | if (async_get_ctx()->currjob->status == ASYNC_JOB_STOPPING) { | |
636ca4ff MC |
224 | *ret = async_get_ctx()->currjob->ret; |
225 | async_release_job(async_get_ctx()->currjob); | |
226 | async_get_ctx()->currjob = NULL; | |
82676094 | 227 | *job = NULL; |
50108304 MC |
228 | return ASYNC_FINISH; |
229 | } | |
230 | ||
e38565f5 | 231 | if (async_get_ctx()->currjob->status == ASYNC_JOB_PAUSING) { |
636ca4ff MC |
232 | *job = async_get_ctx()->currjob; |
233 | async_get_ctx()->currjob->status = ASYNC_JOB_PAUSED; | |
234 | async_get_ctx()->currjob = NULL; | |
50108304 MC |
235 | return ASYNC_PAUSE; |
236 | } | |
237 | ||
e38565f5 | 238 | if (async_get_ctx()->currjob->status == ASYNC_JOB_PAUSED) { |
636ca4ff | 239 | async_get_ctx()->currjob = *job; |
50108304 | 240 | /* Resume previous job */ |
e38565f5 MC |
241 | if (!async_fibre_swapcontext(&async_get_ctx()->dispatcher, |
242 | &async_get_ctx()->currjob->fibrectx, 1)) { | |
079a1a90 MC |
243 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, |
244 | ASYNC_R_FAILED_TO_SWAP_CONTEXT); | |
50108304 | 245 | goto err; |
079a1a90 | 246 | } |
50108304 MC |
247 | continue; |
248 | } | |
249 | ||
250 | /* Should not happen */ | |
079a1a90 | 251 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR); |
636ca4ff MC |
252 | async_release_job(async_get_ctx()->currjob); |
253 | async_get_ctx()->currjob = NULL; | |
82676094 | 254 | *job = NULL; |
50108304 | 255 | return ASYNC_ERR; |
a3667c31 MC |
256 | } |
257 | ||
50108304 | 258 | /* Start a new job */ |
e38565f5 | 259 | if ((async_get_ctx()->currjob = async_get_pool_job()) == NULL) { |
252d6d3a | 260 | return ASYNC_NO_JOBS; |
a3667c31 MC |
261 | } |
262 | ||
e38565f5 | 263 | if (args != NULL) { |
636ca4ff | 264 | async_get_ctx()->currjob->funcargs = OPENSSL_malloc(size); |
e38565f5 | 265 | if (async_get_ctx()->currjob->funcargs == NULL) { |
079a1a90 | 266 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE); |
636ca4ff MC |
267 | async_release_job(async_get_ctx()->currjob); |
268 | async_get_ctx()->currjob = NULL; | |
50108304 MC |
269 | return ASYNC_ERR; |
270 | } | |
636ca4ff | 271 | memcpy(async_get_ctx()->currjob->funcargs, args, size); |
50108304 | 272 | } else { |
636ca4ff | 273 | async_get_ctx()->currjob->funcargs = NULL; |
a3667c31 MC |
274 | } |
275 | ||
636ca4ff | 276 | async_get_ctx()->currjob->func = func; |
e38565f5 MC |
277 | if (!async_fibre_swapcontext(&async_get_ctx()->dispatcher, |
278 | &async_get_ctx()->currjob->fibrectx, 1)) { | |
079a1a90 | 279 | ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
50108304 | 280 | goto err; |
079a1a90 | 281 | } |
a3667c31 MC |
282 | } |
283 | ||
50108304 | 284 | err: |
636ca4ff MC |
285 | async_release_job(async_get_ctx()->currjob); |
286 | async_get_ctx()->currjob = NULL; | |
82676094 | 287 | *job = NULL; |
a3667c31 MC |
288 | return ASYNC_ERR; |
289 | } | |
290 | ||
291 | ||
292 | int ASYNC_pause_job(void) | |
293 | { | |
294 | ASYNC_JOB *job; | |
295 | ||
e8dfb5bf MC |
296 | if (async_get_ctx() == NULL |
297 | || async_get_ctx()->currjob == NULL | |
298 | || async_get_ctx()->blocked) { | |
079a1a90 | 299 | /* |
05a6347f MC |
300 | * Could be we've deliberately not been started within a job so this is |
301 | * counted as success. | |
079a1a90 | 302 | */ |
05a6347f | 303 | return 1; |
079a1a90 | 304 | } |
a3667c31 | 305 | |
636ca4ff | 306 | job = async_get_ctx()->currjob; |
a3667c31 MC |
307 | job->status = ASYNC_JOB_PAUSING; |
308 | ||
e8dfb5bf MC |
309 | if (!async_fibre_swapcontext(&job->fibrectx, |
310 | &async_get_ctx()->dispatcher, 1)) { | |
079a1a90 | 311 | ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT); |
a3667c31 MC |
312 | return 0; |
313 | } | |
314 | ||
315 | return 1; | |
316 | } | |
317 | ||
27949c35 | 318 | static void async_empty_pool(async_pool *pool) |
d63de0eb MC |
319 | { |
320 | ASYNC_JOB *job; | |
321 | ||
27949c35 MC |
322 | if (!pool || !pool->jobs) |
323 | return; | |
324 | ||
d63de0eb | 325 | do { |
27949c35 | 326 | job = sk_ASYNC_JOB_pop(pool->jobs); |
636ca4ff | 327 | async_job_free(job); |
d63de0eb MC |
328 | } while (job); |
329 | } | |
330 | ||
0ff2b9ac | 331 | int ASYNC_init_pool(size_t max_size, size_t init_size) |
252d6d3a | 332 | { |
27949c35 | 333 | async_pool *pool; |
0ff2b9ac MC |
334 | size_t curr_size = 0; |
335 | ||
27949c35 | 336 | if (init_size > max_size || max_size == 0) { |
079a1a90 | 337 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ASYNC_R_INVALID_POOL_SIZE); |
252d6d3a | 338 | return 0; |
079a1a90 | 339 | } |
252d6d3a | 340 | |
27949c35 MC |
341 | if(async_get_pool() != NULL) { |
342 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ASYNC_R_POOL_ALREADY_INITED); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | pool = OPENSSL_zalloc(sizeof *pool); | |
252d6d3a | 347 | if (pool == NULL) { |
079a1a90 | 348 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ERR_R_MALLOC_FAILURE); |
252d6d3a MC |
349 | return 0; |
350 | } | |
27949c35 MC |
351 | |
352 | pool->jobs = sk_ASYNC_JOB_new_null(); | |
353 | if (pool->jobs == NULL) { | |
354 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ERR_R_MALLOC_FAILURE); | |
355 | OPENSSL_free(pool); | |
356 | return 0; | |
357 | } | |
358 | ||
359 | pool->max_size = max_size; | |
360 | ||
252d6d3a MC |
361 | /* Pre-create jobs as required */ |
362 | while (init_size) { | |
363 | ASYNC_JOB *job; | |
636ca4ff | 364 | job = async_job_new(); |
252d6d3a | 365 | if (job) { |
636ca4ff | 366 | async_fibre_makecontext(&job->fibrectx); |
252d6d3a | 367 | job->funcargs = NULL; |
27949c35 | 368 | sk_ASYNC_JOB_push(pool->jobs, job); |
252d6d3a MC |
369 | curr_size++; |
370 | init_size--; | |
371 | } else { | |
372 | /* | |
373 | * Not actually fatal because we already created the pool, just skip | |
374 | * creation of any more jobs | |
375 | */ | |
376 | init_size = 0; | |
377 | } | |
378 | } | |
27949c35 | 379 | pool->curr_size = curr_size; |
252d6d3a | 380 | |
27949c35 | 381 | if (!async_set_pool(pool)) { |
079a1a90 | 382 | ASYNCerr(ASYNC_F_ASYNC_INIT_POOL, ASYNC_R_FAILED_TO_SET_POOL); |
27949c35 | 383 | goto err; |
d63de0eb | 384 | } |
0ff2b9ac | 385 | |
252d6d3a | 386 | return 1; |
27949c35 MC |
387 | err: |
388 | async_free_pool_internal(pool); | |
389 | return 0; | |
252d6d3a MC |
390 | } |
391 | ||
27949c35 | 392 | static void async_free_pool_internal(async_pool *pool) |
252d6d3a | 393 | { |
0ff2b9ac MC |
394 | if (pool == NULL) |
395 | return; | |
d63de0eb MC |
396 | |
397 | async_empty_pool(pool); | |
27949c35 MC |
398 | sk_ASYNC_JOB_free(pool->jobs); |
399 | OPENSSL_free(pool); | |
400 | async_set_pool(NULL); | |
636ca4ff | 401 | async_ctx_free(); |
252d6d3a | 402 | } |
f4da39d2 | 403 | |
27949c35 MC |
404 | void ASYNC_free_pool(void) |
405 | { | |
406 | async_free_pool_internal(async_get_pool()); | |
407 | } | |
408 | ||
f4da39d2 MC |
409 | ASYNC_JOB *ASYNC_get_current_job(void) |
410 | { | |
636ca4ff | 411 | async_ctx *ctx; |
e38565f5 MC |
412 | |
413 | ctx = async_get_ctx(); | |
414 | if(ctx == NULL) | |
f4da39d2 MC |
415 | return NULL; |
416 | ||
417 | return ctx->currjob; | |
418 | } | |
419 | ||
2b2c78d4 | 420 | OSSL_ASYNC_FD ASYNC_get_wait_fd(ASYNC_JOB *job) |
f4da39d2 MC |
421 | { |
422 | return job->wait_fd; | |
423 | } | |
424 | ||
425 | void ASYNC_wake(ASYNC_JOB *job) | |
426 | { | |
427 | char dummy = 0; | |
428 | ||
429 | if (job->wake_set) | |
430 | return; | |
431 | async_write1(job->wake_fd, &dummy); | |
432 | job->wake_set = 1; | |
433 | } | |
434 | ||
435 | void ASYNC_clear_wake(ASYNC_JOB *job) | |
436 | { | |
437 | char dummy = 0; | |
438 | if (!job->wake_set) | |
439 | return; | |
440 | async_read1(job->wait_fd, &dummy); | |
441 | job->wake_set = 0; | |
442 | } | |
e8dfb5bf MC |
443 | |
444 | void ASYNC_block_pause(void) | |
445 | { | |
446 | if (async_get_ctx() == NULL | |
447 | || async_get_ctx()->currjob == NULL) { | |
448 | /* | |
449 | * We're not in a job anyway so ignore this | |
450 | */ | |
451 | return; | |
452 | } | |
453 | async_get_ctx()->blocked++; | |
454 | } | |
455 | ||
456 | void ASYNC_unblock_pause(void) | |
457 | { | |
458 | if (async_get_ctx() == NULL | |
459 | || async_get_ctx()->currjob == NULL) { | |
460 | /* | |
461 | * We're not in a job anyway so ignore this | |
462 | */ | |
463 | return; | |
464 | } | |
465 | if(async_get_ctx()->blocked > 0) | |
466 | async_get_ctx()->blocked--; | |
467 | } |