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