26e78ff43acc0a0ec8b685874ada263aa328c421
[dpdk.git] / app / test-crypto-perf / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016-2017 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <unistd.h>
7
8 #include <rte_malloc.h>
9 #include <rte_random.h>
10 #include <rte_eal.h>
11 #include <rte_cryptodev.h>
12 #ifdef RTE_LIBRTE_PMD_CRYPTO_SCHEDULER
13 #include <rte_cryptodev_scheduler.h>
14 #endif
15
16 #include "cperf.h"
17 #include "cperf_options.h"
18 #include "cperf_test_vector_parsing.h"
19 #include "cperf_test_throughput.h"
20 #include "cperf_test_latency.h"
21 #include "cperf_test_verify.h"
22 #include "cperf_test_pmd_cyclecount.h"
23
24 #define NUM_SESSIONS 2048
25 #define SESS_MEMPOOL_CACHE_SIZE 64
26
27 const char *cperf_test_type_strs[] = {
28         [CPERF_TEST_TYPE_THROUGHPUT] = "throughput",
29         [CPERF_TEST_TYPE_LATENCY] = "latency",
30         [CPERF_TEST_TYPE_VERIFY] = "verify",
31         [CPERF_TEST_TYPE_PMDCC] = "pmd-cyclecount"
32 };
33
34 const char *cperf_op_type_strs[] = {
35         [CPERF_CIPHER_ONLY] = "cipher-only",
36         [CPERF_AUTH_ONLY] = "auth-only",
37         [CPERF_CIPHER_THEN_AUTH] = "cipher-then-auth",
38         [CPERF_AUTH_THEN_CIPHER] = "auth-then-cipher",
39         [CPERF_AEAD] = "aead"
40 };
41
42 const struct cperf_test cperf_testmap[] = {
43                 [CPERF_TEST_TYPE_THROUGHPUT] = {
44                                 cperf_throughput_test_constructor,
45                                 cperf_throughput_test_runner,
46                                 cperf_throughput_test_destructor
47                 },
48                 [CPERF_TEST_TYPE_LATENCY] = {
49                                 cperf_latency_test_constructor,
50                                 cperf_latency_test_runner,
51                                 cperf_latency_test_destructor
52                 },
53                 [CPERF_TEST_TYPE_VERIFY] = {
54                                 cperf_verify_test_constructor,
55                                 cperf_verify_test_runner,
56                                 cperf_verify_test_destructor
57                 },
58                 [CPERF_TEST_TYPE_PMDCC] = {
59                                 cperf_pmd_cyclecount_test_constructor,
60                                 cperf_pmd_cyclecount_test_runner,
61                                 cperf_pmd_cyclecount_test_destructor
62                 }
63 };
64
65 static int
66 cperf_initialize_cryptodev(struct cperf_options *opts, uint8_t *enabled_cdevs,
67                         struct rte_mempool *session_pool_socket[])
68 {
69         uint8_t enabled_cdev_count = 0, nb_lcores, cdev_id;
70         unsigned int i, j;
71         int ret;
72
73         enabled_cdev_count = rte_cryptodev_devices_get(opts->device_type,
74                         enabled_cdevs, RTE_CRYPTO_MAX_DEVS);
75         if (enabled_cdev_count == 0) {
76                 printf("No crypto devices type %s available\n",
77                                 opts->device_type);
78                 return -EINVAL;
79         }
80
81         nb_lcores = rte_lcore_count() - 1;
82
83         if (nb_lcores < 1) {
84                 RTE_LOG(ERR, USER1,
85                         "Number of enabled cores need to be higher than 1\n");
86                 return -EINVAL;
87         }
88
89         /*
90          * Use less number of devices,
91          * if there are more available than cores.
92          */
93         if (enabled_cdev_count > nb_lcores)
94                 enabled_cdev_count = nb_lcores;
95
96         /* Create a mempool shared by all the devices */
97         uint32_t max_sess_size = 0, sess_size;
98
99         for (cdev_id = 0; cdev_id < rte_cryptodev_count(); cdev_id++) {
100                 sess_size = rte_cryptodev_get_private_session_size(cdev_id);
101                 if (sess_size > max_sess_size)
102                         max_sess_size = sess_size;
103         }
104
105         /*
106          * Calculate number of needed queue pairs, based on the amount
107          * of available number of logical cores and crypto devices.
108          * For instance, if there are 4 cores and 2 crypto devices,
109          * 2 queue pairs will be set up per device.
110          */
111         opts->nb_qps = (nb_lcores % enabled_cdev_count) ?
112                                 (nb_lcores / enabled_cdev_count) + 1 :
113                                 nb_lcores / enabled_cdev_count;
114
115         for (i = 0; i < enabled_cdev_count &&
116                         i < RTE_CRYPTO_MAX_DEVS; i++) {
117                 cdev_id = enabled_cdevs[i];
118 #ifdef RTE_LIBRTE_PMD_CRYPTO_SCHEDULER
119                 /*
120                  * If multi-core scheduler is used, limit the number
121                  * of queue pairs to 1, as there is no way to know
122                  * how many cores are being used by the PMD, and
123                  * how many will be available for the application.
124                  */
125                 if (!strcmp((const char *)opts->device_type, "crypto_scheduler") &&
126                                 rte_cryptodev_scheduler_mode_get(cdev_id) ==
127                                 CDEV_SCHED_MODE_MULTICORE)
128                         opts->nb_qps = 1;
129 #endif
130
131                 struct rte_cryptodev_info cdev_info;
132                 uint8_t socket_id = rte_cryptodev_socket_id(cdev_id);
133
134                 rte_cryptodev_info_get(cdev_id, &cdev_info);
135                 if (opts->nb_qps > cdev_info.max_nb_queue_pairs) {
136                         printf("Number of needed queue pairs is higher "
137                                 "than the maximum number of queue pairs "
138                                 "per device.\n");
139                         printf("Lower the number of cores or increase "
140                                 "the number of crypto devices\n");
141                         return -EINVAL;
142                 }
143                 struct rte_cryptodev_config conf = {
144                         .nb_queue_pairs = opts->nb_qps,
145                         .socket_id = socket_id
146                 };
147
148                 struct rte_cryptodev_qp_conf qp_conf = {
149                         .nb_descriptors = opts->nb_descriptors
150                 };
151
152                 if (session_pool_socket[socket_id] == NULL) {
153                         char mp_name[RTE_MEMPOOL_NAMESIZE];
154                         struct rte_mempool *sess_mp;
155
156                         snprintf(mp_name, RTE_MEMPOOL_NAMESIZE,
157                                 "sess_mp_%u", socket_id);
158
159                         sess_mp = rte_mempool_create(mp_name,
160                                                 NUM_SESSIONS,
161                                                 max_sess_size,
162                                                 SESS_MEMPOOL_CACHE_SIZE,
163                                                 0, NULL, NULL, NULL,
164                                                 NULL, socket_id,
165                                                 0);
166
167                         if (sess_mp == NULL) {
168                                 printf("Cannot create session pool on socket %d\n",
169                                         socket_id);
170                                 return -ENOMEM;
171                         }
172
173                         printf("Allocated session pool on socket %d\n", socket_id);
174                         session_pool_socket[socket_id] = sess_mp;
175                 }
176
177                 ret = rte_cryptodev_configure(cdev_id, &conf);
178                 if (ret < 0) {
179                         printf("Failed to configure cryptodev %u", cdev_id);
180                         return -EINVAL;
181                 }
182
183                 for (j = 0; j < opts->nb_qps; j++) {
184                         ret = rte_cryptodev_queue_pair_setup(cdev_id, j,
185                                 &qp_conf, socket_id,
186                                 session_pool_socket[socket_id]);
187                         if (ret < 0) {
188                                 printf("Failed to setup queue pair %u on "
189                                         "cryptodev %u", j, cdev_id);
190                                 return -EINVAL;
191                         }
192                 }
193
194                 ret = rte_cryptodev_start(cdev_id);
195                 if (ret < 0) {
196                         printf("Failed to start device %u: error %d\n",
197                                         cdev_id, ret);
198                         return -EPERM;
199                 }
200         }
201
202         return enabled_cdev_count;
203 }
204
205 static int
206 cperf_verify_devices_capabilities(struct cperf_options *opts,
207                 uint8_t *enabled_cdevs, uint8_t nb_cryptodevs)
208 {
209         struct rte_cryptodev_sym_capability_idx cap_idx;
210         const struct rte_cryptodev_symmetric_capability *capability;
211
212         uint8_t i, cdev_id;
213         int ret;
214
215         for (i = 0; i < nb_cryptodevs; i++) {
216
217                 cdev_id = enabled_cdevs[i];
218
219                 if (opts->op_type == CPERF_AUTH_ONLY ||
220                                 opts->op_type == CPERF_CIPHER_THEN_AUTH ||
221                                 opts->op_type == CPERF_AUTH_THEN_CIPHER) {
222
223                         cap_idx.type = RTE_CRYPTO_SYM_XFORM_AUTH;
224                         cap_idx.algo.auth = opts->auth_algo;
225
226                         capability = rte_cryptodev_sym_capability_get(cdev_id,
227                                         &cap_idx);
228                         if (capability == NULL)
229                                 return -1;
230
231                         ret = rte_cryptodev_sym_capability_check_auth(
232                                         capability,
233                                         opts->auth_key_sz,
234                                         opts->digest_sz,
235                                         opts->auth_iv_sz);
236                         if (ret != 0)
237                                 return ret;
238                 }
239
240                 if (opts->op_type == CPERF_CIPHER_ONLY ||
241                                 opts->op_type == CPERF_CIPHER_THEN_AUTH ||
242                                 opts->op_type == CPERF_AUTH_THEN_CIPHER) {
243
244                         cap_idx.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
245                         cap_idx.algo.cipher = opts->cipher_algo;
246
247                         capability = rte_cryptodev_sym_capability_get(cdev_id,
248                                         &cap_idx);
249                         if (capability == NULL)
250                                 return -1;
251
252                         ret = rte_cryptodev_sym_capability_check_cipher(
253                                         capability,
254                                         opts->cipher_key_sz,
255                                         opts->cipher_iv_sz);
256                         if (ret != 0)
257                                 return ret;
258                 }
259
260                 if (opts->op_type == CPERF_AEAD) {
261
262                         cap_idx.type = RTE_CRYPTO_SYM_XFORM_AEAD;
263                         cap_idx.algo.aead = opts->aead_algo;
264
265                         capability = rte_cryptodev_sym_capability_get(cdev_id,
266                                         &cap_idx);
267                         if (capability == NULL)
268                                 return -1;
269
270                         ret = rte_cryptodev_sym_capability_check_aead(
271                                         capability,
272                                         opts->aead_key_sz,
273                                         opts->digest_sz,
274                                         opts->aead_aad_sz,
275                                         opts->aead_iv_sz);
276                         if (ret != 0)
277                                 return ret;
278                 }
279         }
280
281         return 0;
282 }
283
284 static int
285 cperf_check_test_vector(struct cperf_options *opts,
286                 struct cperf_test_vector *test_vec)
287 {
288         if (opts->op_type == CPERF_CIPHER_ONLY) {
289                 if (opts->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
290                         if (test_vec->plaintext.data == NULL)
291                                 return -1;
292                 } else if (opts->cipher_algo != RTE_CRYPTO_CIPHER_NULL) {
293                         if (test_vec->plaintext.data == NULL)
294                                 return -1;
295                         if (test_vec->plaintext.length < opts->max_buffer_size)
296                                 return -1;
297                         if (test_vec->ciphertext.data == NULL)
298                                 return -1;
299                         if (test_vec->ciphertext.length < opts->max_buffer_size)
300                                 return -1;
301                         if (test_vec->cipher_iv.data == NULL)
302                                 return -1;
303                         if (test_vec->cipher_iv.length != opts->cipher_iv_sz)
304                                 return -1;
305                         if (test_vec->cipher_key.data == NULL)
306                                 return -1;
307                         if (test_vec->cipher_key.length != opts->cipher_key_sz)
308                                 return -1;
309                 }
310         } else if (opts->op_type == CPERF_AUTH_ONLY) {
311                 if (opts->auth_algo != RTE_CRYPTO_AUTH_NULL) {
312                         if (test_vec->plaintext.data == NULL)
313                                 return -1;
314                         if (test_vec->plaintext.length < opts->max_buffer_size)
315                                 return -1;
316                         if (test_vec->auth_key.data == NULL)
317                                 return -1;
318                         if (test_vec->auth_key.length != opts->auth_key_sz)
319                                 return -1;
320                         if (test_vec->auth_iv.length != opts->auth_iv_sz)
321                                 return -1;
322                         /* Auth IV is only required for some algorithms */
323                         if (opts->auth_iv_sz && test_vec->auth_iv.data == NULL)
324                                 return -1;
325                         if (test_vec->digest.data == NULL)
326                                 return -1;
327                         if (test_vec->digest.length < opts->digest_sz)
328                                 return -1;
329                 }
330
331         } else if (opts->op_type == CPERF_CIPHER_THEN_AUTH ||
332                         opts->op_type == CPERF_AUTH_THEN_CIPHER) {
333                 if (opts->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
334                         if (test_vec->plaintext.data == NULL)
335                                 return -1;
336                         if (test_vec->plaintext.length < opts->max_buffer_size)
337                                 return -1;
338                 } else if (opts->cipher_algo != RTE_CRYPTO_CIPHER_NULL) {
339                         if (test_vec->plaintext.data == NULL)
340                                 return -1;
341                         if (test_vec->plaintext.length < opts->max_buffer_size)
342                                 return -1;
343                         if (test_vec->ciphertext.data == NULL)
344                                 return -1;
345                         if (test_vec->ciphertext.length < opts->max_buffer_size)
346                                 return -1;
347                         if (test_vec->cipher_iv.data == NULL)
348                                 return -1;
349                         if (test_vec->cipher_iv.length != opts->cipher_iv_sz)
350                                 return -1;
351                         if (test_vec->cipher_key.data == NULL)
352                                 return -1;
353                         if (test_vec->cipher_key.length != opts->cipher_key_sz)
354                                 return -1;
355                 }
356                 if (opts->auth_algo != RTE_CRYPTO_AUTH_NULL) {
357                         if (test_vec->auth_key.data == NULL)
358                                 return -1;
359                         if (test_vec->auth_key.length != opts->auth_key_sz)
360                                 return -1;
361                         if (test_vec->auth_iv.length != opts->auth_iv_sz)
362                                 return -1;
363                         /* Auth IV is only required for some algorithms */
364                         if (opts->auth_iv_sz && test_vec->auth_iv.data == NULL)
365                                 return -1;
366                         if (test_vec->digest.data == NULL)
367                                 return -1;
368                         if (test_vec->digest.length < opts->digest_sz)
369                                 return -1;
370                 }
371         } else if (opts->op_type == CPERF_AEAD) {
372                 if (test_vec->plaintext.data == NULL)
373                         return -1;
374                 if (test_vec->plaintext.length < opts->max_buffer_size)
375                         return -1;
376                 if (test_vec->ciphertext.data == NULL)
377                         return -1;
378                 if (test_vec->ciphertext.length < opts->max_buffer_size)
379                         return -1;
380                 if (test_vec->aead_iv.data == NULL)
381                         return -1;
382                 if (test_vec->aead_iv.length != opts->aead_iv_sz)
383                         return -1;
384                 if (test_vec->aad.data == NULL)
385                         return -1;
386                 if (test_vec->aad.length != opts->aead_aad_sz)
387                         return -1;
388                 if (test_vec->digest.data == NULL)
389                         return -1;
390                 if (test_vec->digest.length < opts->digest_sz)
391                         return -1;
392         }
393         return 0;
394 }
395
396 int
397 main(int argc, char **argv)
398 {
399         struct cperf_options opts = {0};
400         struct cperf_test_vector *t_vec = NULL;
401         struct cperf_op_fns op_fns;
402
403         void *ctx[RTE_MAX_LCORE] = { };
404         struct rte_mempool *session_pool_socket[RTE_MAX_NUMA_NODES] = { 0 };
405
406         int nb_cryptodevs = 0;
407         uint16_t total_nb_qps = 0;
408         uint8_t cdev_id, i;
409         uint8_t enabled_cdevs[RTE_CRYPTO_MAX_DEVS] = { 0 };
410
411         uint8_t buffer_size_idx = 0;
412
413         int ret;
414         uint32_t lcore_id;
415
416         /* Initialise DPDK EAL */
417         ret = rte_eal_init(argc, argv);
418         if (ret < 0)
419                 rte_exit(EXIT_FAILURE, "Invalid EAL arguments!\n");
420         argc -= ret;
421         argv += ret;
422
423         cperf_options_default(&opts);
424
425         ret = cperf_options_parse(&opts, argc, argv);
426         if (ret) {
427                 RTE_LOG(ERR, USER1, "Parsing on or more user options failed\n");
428                 goto err;
429         }
430
431         ret = cperf_options_check(&opts);
432         if (ret) {
433                 RTE_LOG(ERR, USER1,
434                                 "Checking on or more user options failed\n");
435                 goto err;
436         }
437
438         nb_cryptodevs = cperf_initialize_cryptodev(&opts, enabled_cdevs,
439                         session_pool_socket);
440
441         if (!opts.silent)
442                 cperf_options_dump(&opts);
443
444         if (nb_cryptodevs < 1) {
445                 RTE_LOG(ERR, USER1, "Failed to initialise requested crypto "
446                                 "device type\n");
447                 nb_cryptodevs = 0;
448                 goto err;
449         }
450
451         ret = cperf_verify_devices_capabilities(&opts, enabled_cdevs,
452                         nb_cryptodevs);
453         if (ret) {
454                 RTE_LOG(ERR, USER1, "Crypto device type does not support "
455                                 "capabilities requested\n");
456                 goto err;
457         }
458
459         if (opts.test_file != NULL) {
460                 t_vec = cperf_test_vector_get_from_file(&opts);
461                 if (t_vec == NULL) {
462                         RTE_LOG(ERR, USER1,
463                                         "Failed to create test vector for"
464                                         " specified file\n");
465                         goto err;
466                 }
467
468                 if (cperf_check_test_vector(&opts, t_vec)) {
469                         RTE_LOG(ERR, USER1, "Incomplete necessary test vectors"
470                                         "\n");
471                         goto err;
472                 }
473         } else {
474                 t_vec = cperf_test_vector_get_dummy(&opts);
475                 if (t_vec == NULL) {
476                         RTE_LOG(ERR, USER1,
477                                         "Failed to create test vector for"
478                                         " specified algorithms\n");
479                         goto err;
480                 }
481         }
482
483         ret = cperf_get_op_functions(&opts, &op_fns);
484         if (ret) {
485                 RTE_LOG(ERR, USER1, "Failed to find function ops set for "
486                                 "specified algorithms combination\n");
487                 goto err;
488         }
489
490         if (!opts.silent)
491                 show_test_vector(t_vec);
492
493         total_nb_qps = nb_cryptodevs * opts.nb_qps;
494
495         i = 0;
496         uint8_t qp_id = 0, cdev_index = 0;
497         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
498
499                 if (i == total_nb_qps)
500                         break;
501
502                 cdev_id = enabled_cdevs[cdev_index];
503
504                 uint8_t socket_id = rte_cryptodev_socket_id(cdev_id);
505
506                 ctx[i] = cperf_testmap[opts.test].constructor(
507                                 session_pool_socket[socket_id], cdev_id, qp_id,
508                                 &opts, t_vec, &op_fns);
509                 if (ctx[i] == NULL) {
510                         RTE_LOG(ERR, USER1, "Test run constructor failed\n");
511                         goto err;
512                 }
513                 qp_id = (qp_id + 1) % opts.nb_qps;
514                 if (qp_id == 0)
515                         cdev_index++;
516                 i++;
517         }
518
519         if (opts.imix_distribution_count != 0) {
520                 uint8_t buffer_size_count = opts.buffer_size_count;
521                 uint16_t distribution_total[buffer_size_count];
522                 uint32_t op_idx;
523                 uint32_t test_average_size = 0;
524                 const uint32_t *buffer_size_list = opts.buffer_size_list;
525                 const uint32_t *imix_distribution_list = opts.imix_distribution_list;
526
527                 opts.imix_buffer_sizes = rte_malloc(NULL,
528                                         sizeof(uint32_t) * opts.pool_sz,
529                                         0);
530                 /*
531                  * Calculate accumulated distribution of
532                  * probabilities per packet size
533                  */
534                 distribution_total[0] = imix_distribution_list[0];
535                 for (i = 1; i < buffer_size_count; i++)
536                         distribution_total[i] = imix_distribution_list[i] +
537                                 distribution_total[i-1];
538
539                 /* Calculate a random sequence of packet sizes, based on distribution */
540                 for (op_idx = 0; op_idx < opts.pool_sz; op_idx++) {
541                         uint16_t random_number = rte_rand() %
542                                 distribution_total[buffer_size_count - 1];
543                         for (i = 0; i < buffer_size_count; i++)
544                                 if (random_number < distribution_total[i])
545                                         break;
546
547                         opts.imix_buffer_sizes[op_idx] = buffer_size_list[i];
548                 }
549
550                 /* Calculate average buffer size for the IMIX distribution */
551                 for (i = 0; i < buffer_size_count; i++)
552                         test_average_size += buffer_size_list[i] *
553                                 imix_distribution_list[i];
554
555                 opts.test_buffer_size = test_average_size /
556                                 distribution_total[buffer_size_count - 1];
557
558                 i = 0;
559                 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
560
561                         if (i == total_nb_qps)
562                                 break;
563
564                         rte_eal_remote_launch(cperf_testmap[opts.test].runner,
565                                 ctx[i], lcore_id);
566                         i++;
567                 }
568                 i = 0;
569                 RTE_LCORE_FOREACH_SLAVE(lcore_id) {
570
571                         if (i == total_nb_qps)
572                                 break;
573                         rte_eal_wait_lcore(lcore_id);
574                         i++;
575                 }
576         } else {
577
578                 /* Get next size from range or list */
579                 if (opts.inc_buffer_size != 0)
580                         opts.test_buffer_size = opts.min_buffer_size;
581                 else
582                         opts.test_buffer_size = opts.buffer_size_list[0];
583
584                 while (opts.test_buffer_size <= opts.max_buffer_size) {
585                         i = 0;
586                         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
587
588                                 if (i == total_nb_qps)
589                                         break;
590
591                                 rte_eal_remote_launch(cperf_testmap[opts.test].runner,
592                                         ctx[i], lcore_id);
593                                 i++;
594                         }
595                         i = 0;
596                         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
597
598                                 if (i == total_nb_qps)
599                                         break;
600                                 rte_eal_wait_lcore(lcore_id);
601                                 i++;
602                         }
603
604                         /* Get next size from range or list */
605                         if (opts.inc_buffer_size != 0)
606                                 opts.test_buffer_size += opts.inc_buffer_size;
607                         else {
608                                 if (++buffer_size_idx == opts.buffer_size_count)
609                                         break;
610                                 opts.test_buffer_size =
611                                         opts.buffer_size_list[buffer_size_idx];
612                         }
613                 }
614         }
615
616         i = 0;
617         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
618
619                 if (i == total_nb_qps)
620                         break;
621
622                 cperf_testmap[opts.test].destructor(ctx[i]);
623                 i++;
624         }
625
626         for (i = 0; i < nb_cryptodevs &&
627                         i < RTE_CRYPTO_MAX_DEVS; i++)
628                 rte_cryptodev_stop(enabled_cdevs[i]);
629
630         free_test_vector(t_vec, &opts);
631
632         printf("\n");
633         return EXIT_SUCCESS;
634
635 err:
636         i = 0;
637         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
638                 if (i == total_nb_qps)
639                         break;
640
641                 if (ctx[i] && cperf_testmap[opts.test].destructor)
642                         cperf_testmap[opts.test].destructor(ctx[i]);
643                 i++;
644         }
645
646         for (i = 0; i < nb_cryptodevs &&
647                         i < RTE_CRYPTO_MAX_DEVS; i++)
648                 rte_cryptodev_stop(enabled_cdevs[i]);
649         rte_free(opts.imix_buffer_sizes);
650         free_test_vector(t_vec, &opts);
651
652         printf("\n");
653         return EXIT_FAILURE;
654 }