5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2008 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* #define SILC_SOFTACC_DEBUG_ON 1 */
22 #include "silccrypto.h"
24 #include "softacc_i.h"
25 #include "aes_internal.h"
29 /* Cipher accelerator accelerates ciphers using counter mode by precomputing
30 the CTR key stream in threads. Encryption and decryption uses the
31 precomputed key stream and gets significant speed improvement in the
32 process. The threads are reserved from the thread pool and they remain
33 reserved as long as the cipher is accelerated.
35 As a queue we use SilcThreadQueue from SRT which handles locking and
36 waiting automatically and supports multiple pipes for multiple key
37 streams, so it makes this whole thing very simple.
39 This can accelerate any cipher but AES is especially optimized.
43 To get the absolutely maximum performance out one must assign lots of
49 Benchmarks (version 1.0):
51 4-core: 2 x dual-core Xeon 5160 3GHz (Woodcrest), 4 GB RAM
52 -----------------------------------------------------------------------
53 cipher_threads = 4, cipher_blocks = 65536, cipher_streams = 32:
54 aes-128-ctr: 728042.34 KB 710.98 MB 5687.83 Mbit / sec
55 aes-192-ctr: 634662.85 KB 619.79 MB 4958.30 Mbit / sec
56 aes-256-ctr: 555215.22 KB 542.20 MB 4337.62 Mbit / sec
58 default settings, cipher_threads = 4:
59 aes-128-ctr: 625568.94 KB 610.91 MB 4887.26 Mbit / sec
60 aes-192-ctr: 572719.08 KB 559.30 MB 4474.37 Mbit / sec
61 aes-256-ctr: 506930.88 KB 495.05 MB 3960.40 Mbit / sec
63 8-core: 2 x quad-core Xeon E5345 2.33GHz (Clovertown), 4 GB RAM
64 -----------------------------------------------------------------------
65 cipher_threads = 8, cipher_blocks = 65536, cipher_streams = 64:
66 aes-128-ctr: 1162373.93 KB 1135.13 MB 9081.05 Mbit / sec
67 aes-192-ctr: 994808.64 KB 971.49 MB 7771.94 Mbit / sec
68 aes-256-ctr: 874370.93 KB 853.88 MB 6831.02 Mbit / sec
70 default settings, cipher_threads = 8:
71 aes-128-ctr: 805157.74 KB 786.29 MB 6290.29 Mbit / sec
72 aes-192-ctr: 733164.28 KB 715.98 MB 5727.85 Mbit / sec
73 aes-256-ctr: 664677.98 KB 649.10 MB 5192.80 Mbit / sec
79 - nice -n -20 lib/silcacc/tests/test_softacc_cipher
83 /************************** Types and definitions ***************************/
85 /* Software accelerator cipher operations */
86 const SilcCipherObject softacc_cipher[] =
91 silc_softacc_cipher_aes_set_key,
92 silc_softacc_cipher_aes_set_iv,
93 silc_softacc_cipher_aes_encrypt,
94 silc_softacc_cipher_aes_encrypt,
95 silc_softacc_cipher_init,
96 silc_softacc_cipher_uninit,
98 SILC_CIPHER_MODE_CTR, /* Only CTR mode can be accelerated */
101 /* All other ciphers */
104 silc_softacc_cipher_set_key,
105 silc_softacc_cipher_set_iv,
106 silc_softacc_cipher_encrypt,
107 silc_softacc_cipher_encrypt,
108 silc_softacc_cipher_init,
109 silc_softacc_cipher_uninit,
111 SILC_CIPHER_MODE_CTR, /* Only CTR mode can be accelerated */
115 NULL, NULL, NULL, NULL, NULL,
116 NULL, NULL, 0, 0, 0, 0,
121 #define SILC_KEYSTREAM_BLOCK SILC_CIPHER_MAX_IV_SIZE
123 /* Thread stop signal */
124 #define SILC_KEYSTREAM_STOP (void *)0x01
126 /* Key stream context */
128 SilcUInt32 key_index; /* Key index in queue */
129 unsigned char ctr[SILC_CIPHER_MAX_IV_SIZE]; /* Counter */
130 unsigned char key[0]; /* Key stream begins here */
131 } *SilcSoftaccCipherKeyStream;
133 /* Accelerator cipher context */
134 typedef struct SilcSoftaccCipherStruct {
136 AesContext aes; /* AES */
137 SilcCipher ecb; /* Other ciphers in ECB mode */
140 SilcThreadQueue queue; /* Key stream queue */
141 unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current counter */
142 SilcSoftaccCipherKeyStream *key_stream; /* Key streams */
143 SilcSoftaccCipherKeyStream cur; /* Current key stream */
144 SilcUInt32 cur_block; /* Current block in key stream */
145 SilcUInt32 cur_index; /* Current key stream index */
146 SilcUInt32 pad; /* Partial block offset */
147 SilcUInt32 num_key_stream; /* Number of key streams */
148 SilcUInt32 cipher_blocks; /* Number of cipher blocks */
149 unsigned int cipher_threads : 31; /* Number of cipher threads */
150 unsigned int key_set : 1; /* Set when key is set */
151 } *SilcSoftaccCipher;
153 /************************** Static utility functions ************************/
155 /* Add value to MSB ordered counter. */
158 void silc_softacc_add_ctr(unsigned char *ctr, SilcUInt32 block_len,
167 for (i = block_len - 1; i >= 0; i--) {
168 q += ctr[i] + (val & 0xff);
177 /*********************************** AES ************************************/
179 #define SILC_AES_BLOCK 16
181 /* Thread destructor */
183 static SILC_TASK_CALLBACK(silc_softacc_cipher_aes_completion)
185 SilcSoftaccCipher c = context;
188 /* Disconnect from key stream queue */
189 if (silc_thread_queue_disconnect(c->queue))
192 for (i = 0; i < c->num_key_stream; i++)
193 silc_free(c->key_stream[i]);
194 silc_free(c->key_stream);
195 memset(c, 0, sizeof(*c));
199 /* Key stream computation thread */
201 void silc_softacc_cipher_aes_thread(SilcSchedule schedule, void *context)
203 SilcSoftaccCipher c = context;
204 SilcThreadQueue queue = c->queue;
205 SilcSoftaccCipherKeyStream key;
206 SilcUInt32 i, num_key_stream = c->num_key_stream;
207 SilcUInt32 cipher_blocks = c->cipher_blocks;
209 unsigned char *enc_ctr;
211 SILC_SOFTACC_DEBUG(("Start CTR precomputation thread"));
213 /* Connect to the key stream queue */
214 silc_thread_queue_connect(queue);
216 /* Process key streams. We wait for empty key streams to come from the
217 last pipe in the queue. Here we precompute the key stream and put them
218 back to the queue. */
220 key = silc_thread_queue_pop(queue, num_key_stream, TRUE);
221 if (key == SILC_KEYSTREAM_STOP)
224 SILC_SOFTACC_DEBUG(("Precompute key stream %p, index %d", key,
229 for (i = 0; i < cipher_blocks; i++) {
230 for (k = SILC_AES_BLOCK - 1; k >= 0; k--)
233 aes_encrypt(key->ctr, enc_ctr, &c->c.aes.u.enc);
234 enc_ctr += SILC_AES_BLOCK;
237 SILC_SOFTACC_DEBUG(("Precomputed key stream %p, index %d", key,
241 silc_softacc_add_ctr(key->ctr, SILC_AES_BLOCK,
242 (num_key_stream - 1) * cipher_blocks);
244 /* Put it back to queue */
245 silc_thread_queue_push(queue, key->key_index, key, FALSE);
248 SILC_SOFTACC_DEBUG(("End CTR precomputation thread"));
251 /* Set IV. Also, reset current block, discarding any remaining unused bits in
252 the current key block. */
254 SILC_CIPHER_API_SET_IV(softacc_cipher_aes)
256 SilcSoftaccCipher c = context;
257 SilcSoftaccCipherKeyStream key;
260 /* If IV is NULL we start new block */
262 SILC_SOFTACC_DEBUG(("Start new block"));
264 if (c->pad < SILC_AES_BLOCK) {
265 c->pad = SILC_AES_BLOCK;
267 /* Start new block */
268 if (++c->cur_block == c->cipher_blocks) {
269 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
270 c->cur, c->cur->key_index));
271 silc_thread_queue_push(c->queue, c->num_key_stream, c->cur, FALSE);
272 c->cur_index = (c->cur_index + 1) % c->cipher_blocks;
279 SILC_SOFTACC_DEBUG(("Start new counter"));
281 memcpy(c->iv, iv, SILC_AES_BLOCK);
286 /* Push current key stream back to queue. We need all of them there
289 silc_thread_queue_push(c->queue, c->cur->key_index, c->cur, FALSE);
291 /* We must get all key streams and update them */
292 for (i = 0; i < c->num_key_stream; i++) {
293 key = silc_thread_queue_pop(c->queue, i, TRUE);
294 memcpy(key->ctr, c->iv, SILC_AES_BLOCK);
295 silc_softacc_add_ctr(key->ctr, SILC_AES_BLOCK, i * c->cipher_blocks);
296 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
302 c->pad = SILC_AES_BLOCK;
306 /* Accelerate cipher */
308 SILC_CIPHER_API_SET_KEY(softacc_cipher_aes)
310 SilcSoftaccCipher c = context;
314 /* If key is present set it. If it is NULL this is initialization call. */
316 SILC_SOFTACC_DEBUG(("Set key for accelerator %s %p", ops->alg_name, c));
318 aes_encrypt_key(key, keylen, &c->c.aes.u.enc);
321 /* Set the counters for each key stream and push them to the queue for
323 for (i = 0; i < c->num_key_stream; i++) {
324 memcpy(c->key_stream[i]->ctr, c->iv, SILC_AES_BLOCK);
325 silc_softacc_add_ctr(c->key_stream[i]->ctr, SILC_AES_BLOCK,
326 i * c->cipher_blocks);
327 silc_thread_queue_push(c->queue, c->num_key_stream, c->key_stream[i],
334 /* Initialize the accelerator for this cipher */
335 SILC_LOG_DEBUG(("Initialize accelerator for %s %p", ops->alg_name, c));
337 sa = silc_global_get_var("softacc", FALSE);
339 SILC_LOG_ERROR(("Software accelerator not initialized"));
343 /* Start the queue with sa->cipher_blocks many key streams. One extra pipe
344 in the queue is used as a return pipe for empty key streams. */
345 c->cipher_blocks = sa->cipher_blocks;
346 c->cipher_threads = sa->cipher_threads;
347 c->num_key_stream = sa->cipher_streams;
348 c->key_stream = silc_calloc(c->num_key_stream, sizeof(*c->key_stream));
351 for (i = 0; i < c->num_key_stream; i++) {
352 c->key_stream[i] = silc_malloc(sizeof(**c->key_stream) +
353 (c->cipher_blocks * SILC_AES_BLOCK));
354 if (!c->key_stream[i])
356 c->key_stream[i]->key_index = i;
358 c->queue = silc_thread_queue_alloc(c->num_key_stream + 1, TRUE);
362 /* Start the threads. If thread starting fails, we can't accelerate the
363 cipher. The uninit operation will clean up any started threads. */
364 for (i = 0; i < sa->cipher_threads; i++)
365 if (!silc_thread_pool_run(sa->tp, FALSE, NULL,
366 silc_softacc_cipher_aes_thread,
367 c, silc_softacc_cipher_aes_completion, c))
373 /* Accelerated encryption/decryption in CTR mode */
375 SILC_CIPHER_API_ENCRYPT(softacc_cipher_aes)
377 SilcSoftaccCipher c = context;
378 SilcSoftaccCipherKeyStream key;
379 SilcUInt32 pad = c->pad, block = c->cur_block;
380 SilcUInt32 blocks, cipher_blocks = c->cipher_blocks;
381 unsigned char *enc_ctr;
385 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
386 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
389 enc_ctr = key->key + (block << 4);
391 /* Compute partial block */
392 if (pad < SILC_AES_BLOCK) {
394 *dst++ = *src++ ^ enc_ctr[pad++];
395 if (pad == SILC_AES_BLOCK) {
396 enc_ctr += SILC_AES_BLOCK;
397 if (++block == cipher_blocks) {
398 /* Push the used up key stream back to the queue */
399 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
400 key, key->key_index));
401 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
403 /* Get new key stream from queue */
404 c->cur_index = (c->cur_index + 1) % c->num_key_stream;
405 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
406 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
416 /* Compute full blocks */
418 len -= (blocks << 4);
421 #ifndef WORDS_BIGENDIAN
422 *(SilcUInt64 *)dst = (*(SilcUInt64 *)src ^
423 *(SilcUInt64 *)enc_ctr);
424 *(SilcUInt64 *)(dst + 8) = (*(SilcUInt64 *)(src + 8) ^
425 *(SilcUInt64 *)(enc_ctr + 8));
427 SilcUInt64 dst_tmp, src_tmp, enc_ctr_tmp;
429 SILC_GET64_MSB(src_tmp, src);
430 SILC_GET64_MSB(enc_ctr_tmp, enc_ctr);
431 dst_tmp = src_tmp ^ enc_ctr_tmp;
432 SILC_PUT64_MSB(dst_tmp, dst);
434 SILC_GET64_MSB(src_tmp, src + 8);
435 SILC_GET64_MSB(enc_ctr_tmp, enc_ctr + 8);
436 dst_tmp = src_tmp ^ enc_ctr_tmp;
437 SILC_PUT64_MSB(dst_tmp, dst + 8);
438 #endif /* !WORDS_BIGENDIAN */
440 src += SILC_AES_BLOCK;
441 dst += SILC_AES_BLOCK;
442 enc_ctr += SILC_AES_BLOCK;
444 if (++block == cipher_blocks) {
445 /* Push the used up key stream back to the queue */
446 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
447 key, key->key_index));
448 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
450 /* Get new key stream from queue */
451 c->cur_index = (c->cur_index + 1) % c->num_key_stream;
452 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
453 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
459 /* Compute partial block */
463 *dst++ = *src++ ^ enc_ctr[pad++];
466 c->cur_block = block;
472 /****************************** Other ciphers *******************************/
474 /* Thread destructor */
476 static SILC_TASK_CALLBACK(silc_softacc_cipher_completion)
478 SilcSoftaccCipher c = context;
481 /* Disconnect from key stream queue */
482 if (silc_thread_queue_disconnect(c->queue))
485 silc_cipher_free(c->c.ecb);
486 for (i = 0; i < c->num_key_stream; i++)
487 silc_free(c->key_stream[i]);
488 silc_free(c->key_stream);
489 memset(c, 0, sizeof(*c));
493 /* Key stream computation thread */
495 void silc_softacc_cipher_thread(SilcSchedule schedule, void *context)
497 SilcSoftaccCipher c = context;
498 SilcThreadQueue queue = c->queue;
499 SilcSoftaccCipherKeyStream key = NULL;
500 SilcUInt32 i, block_len, num_key_stream = c->num_key_stream;
501 SilcUInt32 cipher_blocks = c->cipher_blocks;
503 unsigned char *enc_ctr;
505 SILC_SOFTACC_DEBUG(("Start CTR precomputation thread"));
507 block_len = silc_cipher_get_block_len(c->c.ecb);
509 /* Connect to the key stream queue */
510 silc_thread_queue_connect(queue);
512 /* Process key streams. We wait for empty key streams to come from the
513 last pipe in the queue. Here we precompute the key stream and put them
514 back to the queue. */
516 key = silc_thread_queue_pop(queue, num_key_stream, TRUE);
517 if (key == SILC_KEYSTREAM_STOP)
520 SILC_SOFTACC_DEBUG(("Precompute key stream %p, index %d", key,
525 for (i = 0; i < cipher_blocks; i++) {
526 for (k = block_len - 1; k >= 0; k--)
529 c->c.ecb->cipher->encrypt(c->c.ecb, c->c.ecb->cipher, c->c.ecb->context,
530 key->ctr, enc_ctr, block_len, NULL);
531 enc_ctr += block_len;
534 SILC_SOFTACC_DEBUG(("Precomputed key stream %p, index %d", key,
538 silc_softacc_add_ctr(key->ctr, block_len,
539 (num_key_stream - 1) * cipher_blocks);
541 /* Put it back to queue */
542 silc_thread_queue_push(queue, key->key_index, key, FALSE);
545 SILC_SOFTACC_DEBUG(("End CTR precomputation thread"));
548 /* Accelerate cipher */
550 SILC_CIPHER_API_SET_KEY(softacc_cipher)
552 SilcSoftaccCipher c = context;
556 /* If key is present set it. If it is NULL this is initialization call. */
558 SILC_SOFTACC_DEBUG(("Set key for accelerator %s %p", ops->alg_name, c));
560 SILC_VERIFY(c->c.ecb && c->queue);
562 if (!silc_cipher_set_key(c->c.ecb, key, keylen, TRUE))
566 /* Set the counters for each key stream and push them to the queue for
568 for (i = 0; i < c->num_key_stream; i++) {
569 memcpy(c->key_stream[i]->ctr, c->iv, silc_cipher_get_iv_len(c->c.ecb));
570 silc_softacc_add_ctr(c->key_stream[i]->ctr,
571 silc_cipher_get_block_len(c->c.ecb),
572 i * c->cipher_blocks);
573 silc_thread_queue_push(c->queue, c->num_key_stream, c->key_stream[i],
580 /* Initialize the accelerator for this cipher */
581 SILC_LOG_DEBUG(("Initialize accelerator for %s %p", ops->alg_name, c));
583 sa = silc_global_get_var("softacc", FALSE);
585 SILC_LOG_ERROR(("Software accelerator not initialized"));
589 /* Allocate cipher in ECB mode. It is used to encrypt the key stream. */
590 if (!silc_cipher_alloc_full(ops->alg_name, ops->key_len,
591 SILC_CIPHER_MODE_ECB, &c->c.ecb))
594 /* Start the queue with sa->cipher_blocks many key streams. One extra pipe
595 in the queue is used as a return pipe for empty key streams. */
596 c->cipher_blocks = sa->cipher_blocks;
597 c->cipher_threads = sa->cipher_threads;
598 c->num_key_stream = sa->cipher_streams;
599 c->key_stream = silc_calloc(c->num_key_stream, sizeof(*c->key_stream));
602 for (i = 0; i < c->num_key_stream; i++) {
603 c->key_stream[i] = silc_malloc(sizeof(**c->key_stream) +
605 silc_cipher_get_block_len(c->c.ecb)));
606 if (!c->key_stream[i])
608 c->key_stream[i]->key_index = i;
610 c->queue = silc_thread_queue_alloc(c->num_key_stream + 1, TRUE);
614 /* Start the threads. If thread starting fails, we can't accelerate the
615 cipher. The uninit operation will clean up any started threads. */
616 for (i = 0; i < sa->cipher_threads; i++)
617 if (!silc_thread_pool_run(sa->tp, FALSE, NULL, silc_softacc_cipher_thread,
618 c, silc_softacc_cipher_completion, c))
624 /* Set IV. Also, reset current block, discarding any remaining unused bits in
625 the current key block. */
627 SILC_CIPHER_API_SET_IV(softacc_cipher)
629 SilcSoftaccCipher c = context;
630 SilcSoftaccCipherKeyStream key;
631 SilcUInt32 i, block_len, iv_len;
633 block_len = silc_cipher_get_block_len(c->c.ecb);
634 iv_len = silc_cipher_get_iv_len(c->c.ecb);
636 if (c->pad > block_len)
639 /* If IV is NULL we start new block */
641 SILC_SOFTACC_DEBUG(("Start new block"));
643 if (c->pad < block_len) {
646 /* Start new block */
647 if (++c->cur_block == c->cipher_blocks) {
648 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
649 c->cur, c->cur->key_index));
650 silc_thread_queue_push(c->queue, c->num_key_stream, c->cur, FALSE);
651 c->cur_index = (c->cur_index + 1) % c->cipher_blocks;
658 SILC_SOFTACC_DEBUG(("Start new counter"));
660 memcpy(c->iv, iv, iv_len);
665 /* Push current key stream back to queue. We need all of them there
668 silc_thread_queue_push(c->queue, c->cur->key_index, c->cur, FALSE);
670 /* We must get all key streams and update them. */
671 for (i = 0; i < c->num_key_stream; i++) {
672 key = silc_thread_queue_pop(c->queue, i, TRUE);
673 memcpy(key->ctr, c->iv, iv_len);
674 silc_softacc_add_ctr(key->ctr, iv_len, i * c->cipher_blocks);
675 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
685 SILC_CIPHER_API_ENCRYPT(softacc_cipher)
687 SilcSoftaccCipher c = context;
688 SilcSoftaccCipherKeyStream key;
689 SilcUInt32 pad = c->pad, block = c->cur_block;
690 SilcUInt32 cipher_blocks = c->cipher_blocks;
691 SilcUInt32 blocks, block_len, i;
692 unsigned char *enc_ctr;
696 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
697 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key, key->key_index));
700 block_len = c->c.ecb->cipher->block_len;
701 enc_ctr = key->key + (block * block_len);
703 /* Compute partial block */
704 if (pad < block_len) {
706 *dst++ = *src++ ^ enc_ctr[pad++];
707 if (pad == block_len) {
708 enc_ctr += block_len;
709 if (++block == cipher_blocks) {
710 /* Push the used up key stream back to the queue */
711 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
712 key, key->key_index));
713 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
715 /* Get new key stream from queue */
716 c->cur_index = (c->cur_index + 1) % c->num_key_stream;
717 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
718 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
728 /* Compute full blocks */
729 blocks = (len / block_len);
730 len -= (blocks * block_len);
733 #ifndef WORDS_BIGENDIAN
734 for (i = 0; i < block_len / sizeof(SilcUInt64); i++)
735 *(SilcUInt64 *)(dst + (i * sizeof(SilcUInt64))) =
736 *(SilcUInt64 *)(src + (i * sizeof(SilcUInt64))) ^
737 *(SilcUInt64 *)(enc_ctr + (i * sizeof(SilcUInt64)));
739 SilcUInt64 dst_tmp, src_tmp, enc_ctr_tmp;
741 for (i = 0; i < block_len / sizeof(SilcUInt64); i++) {
742 SILC_GET64_MSB(src_tmp, src + (i * sizeof(SilcUInt64)));
743 SILC_GET64_MSB(enc_ctr_tmp, enc_ctr + (i * sizeof(SilcUInt64)));
744 dst_tmp = src_tmp ^ enc_ctr_tmp;
745 SILC_PUT64_MSB(dst_tmp, dst + (i * sizeof(SilcUInt64)));
747 #endif /* !WORDS_BIGENDIAN */
751 enc_ctr += block_len;
753 if (++block == cipher_blocks) {
754 /* Push the used up key stream back to the queue */
755 SILC_SOFTACC_DEBUG(("Push empty key stream %p index %d back to queue",
756 key, key->key_index));
757 silc_thread_queue_push(c->queue, c->num_key_stream, key, FALSE);
759 /* Get new key stream from queue */
760 c->cur_index = (c->cur_index + 1) % c->num_key_stream;
761 c->cur = key = silc_thread_queue_pop(c->queue, c->cur_index, TRUE);
762 SILC_SOFTACC_DEBUG(("Got key stream %p, index %d", key,
769 /* Compute partial block */
773 *dst++ = *src++ ^ enc_ctr[pad++];
776 c->cur_block = block;
782 /* Return accelerator cipher context */
784 SILC_CIPHER_API_INIT(softacc_cipher)
786 SilcSoftaccCipher c = silc_calloc(1, sizeof(*c));
796 /* Uninitialize the cipher accelerator */
798 SILC_CIPHER_API_UNINIT(softacc_cipher)
800 SilcSoftaccCipher c = context;
805 for (i = 0; i < c->cipher_threads; i++)
806 silc_thread_queue_push(c->queue, c->num_key_stream,
807 SILC_KEYSTREAM_STOP, FALSE);
809 /* Disconnect from key stream queue */
810 if (silc_thread_queue_disconnect(c->queue))
814 silc_free(c->key_stream);
815 memset(c, 0, sizeof(*c));