LCOV - code coverage report
Current view: top level - app/test - test_lcores.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 117 164 71.3 %
Date: 2024-02-14 00:53:57 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 72 128 56.2 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright (c) 2020 Red Hat, Inc.
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <string.h>
       6                 :            : 
       7                 :            : #include <rte_common.h>
       8                 :            : #include <rte_errno.h>
       9                 :            : #include <rte_lcore.h>
      10                 :            : #include <rte_thread.h>
      11                 :            : 
      12                 :            : #include "test.h"
      13                 :            : 
      14                 :            : struct thread_context {
      15                 :            :         enum { Thread_INIT, Thread_ERROR, Thread_DONE } state;
      16                 :            :         bool lcore_id_any;
      17                 :            :         rte_thread_t id;
      18                 :            :         unsigned int *registered_count;
      19                 :            : };
      20                 :            : 
      21         [ -  + ]:        129 : static uint32_t thread_loop(void *arg)
      22                 :            : {
      23                 :            :         struct thread_context *t = arg;
      24                 :            :         unsigned int lcore_id;
      25                 :            : 
      26                 :            :         lcore_id = rte_lcore_id();
      27         [ -  + ]:        129 :         if (lcore_id != LCORE_ID_ANY) {
      28                 :            :                 printf("Error: incorrect lcore id for new thread %u\n", lcore_id);
      29                 :          0 :                 t->state = Thread_ERROR;
      30                 :            :         }
      31         [ +  + ]:        129 :         if (rte_thread_register() < 0)
      32                 :          2 :                 printf("Warning: could not register new thread (this might be expected during this test), reason %s\n",
      33                 :            :                         rte_strerror(rte_errno));
      34                 :            :         lcore_id = rte_lcore_id();
      35   [ +  +  +  -  :        129 :         if ((t->lcore_id_any && lcore_id != LCORE_ID_ANY) ||
                   +  + ]
      36         [ -  + ]:        127 :                         (!t->lcore_id_any && lcore_id == LCORE_ID_ANY)) {
      37         [ #  # ]:          0 :                 printf("Error: could not register new thread, got %u while %sexpecting %u\n",
      38                 :            :                         lcore_id, t->lcore_id_any ? "" : "not ", LCORE_ID_ANY);
      39                 :          0 :                 t->state = Thread_ERROR;
      40                 :            :         }
      41                 :            :         /* Report register happened to the control thread. */
      42                 :        129 :         __atomic_fetch_add(t->registered_count, 1, __ATOMIC_RELEASE);
      43                 :            : 
      44                 :            :         /* Wait for release from the control thread. */
      45         [ +  + ]:16299874392 :         while (__atomic_load_n(t->registered_count, __ATOMIC_ACQUIRE) != 0)
      46                 :            :                 ;
      47                 :        129 :         rte_thread_unregister();
      48                 :            :         lcore_id = rte_lcore_id();
      49         [ -  + ]:        129 :         if (lcore_id != LCORE_ID_ANY) {
      50                 :            :                 printf("Error: could not unregister new thread, %u still assigned\n",
      51                 :            :                         lcore_id);
      52                 :          0 :                 t->state = Thread_ERROR;
      53                 :            :         }
      54                 :            : 
      55         [ +  - ]:        129 :         if (t->state != Thread_ERROR)
      56                 :        129 :                 t->state = Thread_DONE;
      57                 :            : 
      58                 :        129 :         return 0;
      59                 :            : }
      60                 :            : 
      61                 :            : static int
      62                 :          1 : test_non_eal_lcores(unsigned int eal_threads_count)
      63                 :            : {
      64                 :            :         struct thread_context thread_contexts[RTE_MAX_LCORE];
      65                 :            :         unsigned int non_eal_threads_count;
      66                 :            :         unsigned int registered_count;
      67                 :            :         struct thread_context *t;
      68                 :            :         unsigned int i;
      69                 :            :         int ret;
      70                 :            : 
      71                 :            :         non_eal_threads_count = 0;
      72                 :          1 :         registered_count = 0;
      73                 :            : 
      74                 :            :         /* Try to create as many threads as possible. */
      75         [ +  + ]:        127 :         for (i = 0; i < RTE_MAX_LCORE - eal_threads_count; i++) {
      76                 :        126 :                 t = &thread_contexts[i];
      77                 :        126 :                 t->state = Thread_INIT;
      78                 :        126 :                 t->registered_count = &registered_count;
      79                 :        126 :                 t->lcore_id_any = false;
      80         [ +  - ]:        126 :                 if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
      81                 :            :                         break;
      82                 :        126 :                 non_eal_threads_count++;
      83                 :            :         }
      84                 :            :         printf("non-EAL threads count: %u\n", non_eal_threads_count);
      85                 :            :         /* Wait all non-EAL threads to register. */
      86         [ +  + ]:    8720679 :         while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
      87                 :            :                         non_eal_threads_count)
      88                 :            :                 ;
      89                 :            : 
      90                 :            :         /* We managed to create the max number of threads, let's try to create
      91                 :            :          * one more. This will allow one more check.
      92                 :            :          */
      93         [ -  + ]:          1 :         if (eal_threads_count + non_eal_threads_count < RTE_MAX_LCORE)
      94                 :          0 :                 goto skip_lcore_any;
      95                 :          1 :         t = &thread_contexts[non_eal_threads_count];
      96                 :          1 :         t->state = Thread_INIT;
      97                 :          1 :         t->registered_count = &registered_count;
      98                 :          1 :         t->lcore_id_any = true;
      99         [ +  - ]:          1 :         if (rte_thread_create(&t->id, NULL, thread_loop, t) == 0) {
     100                 :          1 :                 non_eal_threads_count++;
     101                 :            :                 printf("non-EAL threads count: %u\n", non_eal_threads_count);
     102         [ +  - ]:          1 :                 while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
     103                 :            :                                 non_eal_threads_count)
     104                 :            :                         ;
     105                 :            :         }
     106                 :            : 
     107                 :          1 : skip_lcore_any:
     108                 :            :         /* Release all threads, and check their states. */
     109                 :          1 :         __atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
     110                 :            :         ret = 0;
     111         [ +  + ]:        128 :         for (i = 0; i < non_eal_threads_count; i++) {
     112                 :        127 :                 t = &thread_contexts[i];
     113                 :        127 :                 rte_thread_join(t->id, NULL);
     114         [ -  + ]:        127 :                 if (t->state != Thread_DONE)
     115                 :            :                         ret = -1;
     116                 :            :         }
     117                 :            : 
     118                 :          1 :         return ret;
     119                 :            : }
     120                 :            : 
     121                 :            : struct limit_lcore_context {
     122                 :            :         unsigned int init;
     123                 :            :         unsigned int max;
     124                 :            :         unsigned int uninit;
     125                 :            : };
     126                 :            : 
     127                 :            : static int
     128                 :         12 : limit_lcores_init(unsigned int lcore_id __rte_unused, void *arg)
     129                 :            : {
     130                 :            :         struct limit_lcore_context *l = arg;
     131                 :            : 
     132                 :         12 :         l->init++;
     133         [ +  + ]:         12 :         if (l->init > l->max)
     134                 :          2 :                 return -1;
     135                 :            :         return 0;
     136                 :            : }
     137                 :            : 
     138                 :            : static void
     139                 :         10 : limit_lcores_uninit(unsigned int lcore_id __rte_unused, void *arg)
     140                 :            : {
     141                 :            :         struct limit_lcore_context *l = arg;
     142                 :            : 
     143                 :         10 :         l->uninit++;
     144                 :         10 : }
     145                 :            : 
     146                 :            : static int
     147                 :          1 : test_lcores_callback(unsigned int eal_threads_count)
     148                 :            : {
     149                 :            :         struct limit_lcore_context l;
     150                 :            :         void *handle;
     151                 :            : 
     152                 :            :         /* Refuse last lcore => callback register error. */
     153                 :            :         memset(&l, 0, sizeof(l));
     154                 :          1 :         l.max = eal_threads_count - 1;
     155                 :          1 :         handle = rte_lcore_callback_register("limit", limit_lcores_init,
     156                 :            :                 limit_lcores_uninit, &l);
     157         [ -  + ]:          1 :         if (handle != NULL) {
     158                 :            :                 printf("Error: lcore callback register should have failed\n");
     159                 :          0 :                 goto error;
     160                 :            :         }
     161                 :            :         /* Refusal happens at the n th call to the init callback.
     162                 :            :          * Besides, n - 1 were accepted, so we expect as many uninit calls when
     163                 :            :          * the rollback happens.
     164                 :            :          */
     165         [ -  + ]:          1 :         if (l.init != eal_threads_count) {
     166                 :            :                 printf("Error: lcore callback register failed but incorrect init calls, expected %u, got %u\n",
     167                 :            :                         eal_threads_count, l.init);
     168                 :          0 :                 goto error;
     169                 :            :         }
     170         [ -  + ]:          1 :         if (l.uninit != eal_threads_count - 1) {
     171                 :            :                 printf("Error: lcore callback register failed but incorrect uninit calls, expected %u, got %u\n",
     172                 :            :                         eal_threads_count - 1, l.uninit);
     173                 :          0 :                 goto error;
     174                 :            :         }
     175                 :            : 
     176                 :            :         /* Accept all lcore and unregister. */
     177                 :            :         memset(&l, 0, sizeof(l));
     178                 :          1 :         l.max = eal_threads_count;
     179                 :          1 :         handle = rte_lcore_callback_register("limit", limit_lcores_init,
     180                 :            :                 limit_lcores_uninit, &l);
     181         [ -  + ]:          1 :         if (handle == NULL) {
     182                 :            :                 printf("Error: lcore callback register failed\n");
     183                 :          0 :                 goto error;
     184                 :            :         }
     185         [ -  + ]:          1 :         if (l.uninit != 0) {
     186                 :            :                 printf("Error: lcore callback register succeeded but incorrect uninit calls, expected 0, got %u\n",
     187                 :            :                         l.uninit);
     188                 :          0 :                 goto error;
     189                 :            :         }
     190                 :          1 :         rte_lcore_callback_unregister(handle);
     191                 :            :         handle = NULL;
     192         [ -  + ]:          1 :         if (l.init != eal_threads_count) {
     193                 :            :                 printf("Error: lcore callback unregister done but incorrect init calls, expected %u, got %u\n",
     194                 :            :                         eal_threads_count, l.init);
     195                 :          0 :                 goto error;
     196                 :            :         }
     197         [ -  + ]:          1 :         if (l.uninit != eal_threads_count) {
     198                 :            :                 printf("Error: lcore callback unregister done but incorrect uninit calls, expected %u, got %u\n",
     199                 :            :                         eal_threads_count, l.uninit);
     200                 :          0 :                 goto error;
     201                 :            :         }
     202                 :            : 
     203                 :            :         return 0;
     204                 :            : 
     205                 :          0 : error:
     206         [ #  # ]:          0 :         if (handle != NULL)
     207                 :          0 :                 rte_lcore_callback_unregister(handle);
     208                 :            : 
     209                 :            :         return -1;
     210                 :            : }
     211                 :            : 
     212                 :            : static int
     213                 :          1 : test_non_eal_lcores_callback(unsigned int eal_threads_count)
     214                 :            : {
     215                 :            :         struct thread_context thread_contexts[2];
     216                 :            :         unsigned int non_eal_threads_count = 0;
     217                 :          1 :         struct limit_lcore_context l[2] = {};
     218                 :          1 :         unsigned int registered_count = 0;
     219                 :            :         struct thread_context *t;
     220                 :            :         void *handle[2] = {};
     221                 :            :         unsigned int i;
     222                 :            :         int ret;
     223                 :            : 
     224                 :            :         /* This test requires two empty slots to be sure lcore init refusal is
     225                 :            :          * because of callback execution.
     226                 :            :          */
     227         [ +  - ]:          1 :         if (eal_threads_count + 2 >= RTE_MAX_LCORE)
     228                 :            :                 return 0;
     229                 :            : 
     230                 :            :         /* Register two callbacks:
     231                 :            :          * - first one accepts any lcore,
     232                 :            :          * - second one accepts all EAL lcore + one more for the first non-EAL
     233                 :            :          *   thread, then refuses the next lcore.
     234                 :            :          */
     235                 :          1 :         l[0].max = UINT_MAX;
     236                 :          1 :         handle[0] = rte_lcore_callback_register("no_limit", limit_lcores_init,
     237                 :            :                 limit_lcores_uninit, &l[0]);
     238         [ -  + ]:          1 :         if (handle[0] == NULL) {
     239                 :            :                 printf("Error: lcore callback [0] register failed\n");
     240                 :          0 :                 goto error;
     241                 :            :         }
     242                 :          1 :         l[1].max = eal_threads_count + 1;
     243                 :          1 :         handle[1] = rte_lcore_callback_register("limit", limit_lcores_init,
     244                 :            :                 limit_lcores_uninit, &l[1]);
     245         [ -  + ]:          1 :         if (handle[1] == NULL) {
     246                 :            :                 printf("Error: lcore callback [1] register failed\n");
     247                 :          0 :                 goto error;
     248                 :            :         }
     249   [ +  -  -  + ]:          1 :         if (l[0].init != eal_threads_count || l[1].init != eal_threads_count) {
     250                 :          0 :                 printf("Error: lcore callbacks register succeeded but incorrect init calls, expected %u, %u, got %u, %u\n",
     251                 :            :                         eal_threads_count, eal_threads_count,
     252                 :            :                         l[0].init, l[1].init);
     253                 :          0 :                 goto error;
     254                 :            :         }
     255   [ +  -  -  + ]:          1 :         if (l[0].uninit != 0 || l[1].uninit != 0) {
     256                 :          0 :                 printf("Error: lcore callbacks register succeeded but incorrect uninit calls, expected 0, 1, got %u, %u\n",
     257                 :            :                         l[0].uninit, l[1].uninit);
     258                 :          0 :                 goto error;
     259                 :            :         }
     260                 :            :         /* First thread that expects a valid lcore id. */
     261                 :            :         t = &thread_contexts[0];
     262                 :          1 :         t->state = Thread_INIT;
     263                 :          1 :         t->registered_count = &registered_count;
     264                 :          1 :         t->lcore_id_any = false;
     265         [ -  + ]:          1 :         if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
     266                 :          0 :                 goto cleanup_threads;
     267                 :            :         non_eal_threads_count++;
     268         [ +  + ]:    9209676 :         while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
     269                 :            :                         non_eal_threads_count)
     270                 :            :                 ;
     271         [ +  - ]:          1 :         if (l[0].init != eal_threads_count + 1 ||
     272         [ -  + ]:          1 :                         l[1].init != eal_threads_count + 1) {
     273                 :          0 :                 printf("Error: incorrect init calls, expected %u, %u, got %u, %u\n",
     274                 :            :                         eal_threads_count + 1, eal_threads_count + 1,
     275                 :            :                         l[0].init, l[1].init);
     276                 :          0 :                 goto cleanup_threads;
     277                 :            :         }
     278   [ +  -  -  + ]:          1 :         if (l[0].uninit != 0 || l[1].uninit != 0) {
     279                 :          0 :                 printf("Error: incorrect uninit calls, expected 0, 0, got %u, %u\n",
     280                 :            :                         l[0].uninit, l[1].uninit);
     281                 :          0 :                 goto cleanup_threads;
     282                 :            :         }
     283                 :            :         /* Second thread, that expects LCORE_ID_ANY because of init refusal. */
     284                 :            :         t = &thread_contexts[1];
     285                 :          1 :         t->state = Thread_INIT;
     286                 :          1 :         t->registered_count = &registered_count;
     287                 :          1 :         t->lcore_id_any = true;
     288         [ -  + ]:          1 :         if (rte_thread_create(&t->id, NULL, thread_loop, t) != 0)
     289                 :          0 :                 goto cleanup_threads;
     290                 :            :         non_eal_threads_count++;
     291         [ +  + ]:    5802047 :         while (__atomic_load_n(&registered_count, __ATOMIC_ACQUIRE) !=
     292                 :            :                         non_eal_threads_count)
     293                 :            :                 ;
     294         [ +  - ]:          1 :         if (l[0].init != eal_threads_count + 2 ||
     295         [ -  + ]:          1 :                         l[1].init != eal_threads_count + 2) {
     296                 :          0 :                 printf("Error: incorrect init calls, expected %u, %u, got %u, %u\n",
     297                 :            :                         eal_threads_count + 2, eal_threads_count + 2,
     298                 :            :                         l[0].init, l[1].init);
     299                 :          0 :                 goto cleanup_threads;
     300                 :            :         }
     301   [ +  -  -  + ]:          1 :         if (l[0].uninit != 1 || l[1].uninit != 0) {
     302                 :          0 :                 printf("Error: incorrect uninit calls, expected 1, 0, got %u, %u\n",
     303                 :            :                         l[0].uninit, l[1].uninit);
     304                 :          0 :                 goto cleanup_threads;
     305                 :            :         }
     306                 :          1 :         rte_lcore_dump(stdout);
     307                 :            :         /* Release all threads, and check their states. */
     308                 :          1 :         __atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
     309                 :            :         ret = 0;
     310         [ +  + ]:          3 :         for (i = 0; i < non_eal_threads_count; i++) {
     311                 :          2 :                 t = &thread_contexts[i];
     312                 :          2 :                 rte_thread_join(t->id, NULL);
     313         [ -  + ]:          2 :                 if (t->state != Thread_DONE)
     314                 :            :                         ret = -1;
     315                 :            :         }
     316         [ -  + ]:          1 :         if (ret < 0)
     317                 :          0 :                 goto error;
     318                 :          1 :         rte_lcore_dump(stdout);
     319   [ +  -  -  + ]:          1 :         if (l[0].uninit != 2 || l[1].uninit != 1) {
     320                 :          0 :                 printf("Error: threads reported having successfully registered and unregistered, but incorrect uninit calls, expected 2, 1, got %u, %u\n",
     321                 :            :                         l[0].uninit, l[1].uninit);
     322                 :          0 :                 goto error;
     323                 :            :         }
     324                 :          1 :         rte_lcore_callback_unregister(handle[0]);
     325                 :          1 :         rte_lcore_callback_unregister(handle[1]);
     326                 :          1 :         return 0;
     327                 :            : 
     328                 :          0 : cleanup_threads:
     329                 :            :         /* Release all threads */
     330                 :          0 :         __atomic_store_n(&registered_count, 0, __ATOMIC_RELEASE);
     331         [ #  # ]:          0 :         for (i = 0; i < non_eal_threads_count; i++) {
     332                 :          0 :                 t = &thread_contexts[i];
     333                 :          0 :                 rte_thread_join(t->id, NULL);
     334                 :            :         }
     335                 :          0 : error:
     336         [ #  # ]:          0 :         if (handle[1] != NULL)
     337                 :          0 :                 rte_lcore_callback_unregister(handle[1]);
     338         [ #  # ]:          0 :         if (handle[0] != NULL)
     339                 :          0 :                 rte_lcore_callback_unregister(handle[0]);
     340                 :            :         return -1;
     341                 :            : }
     342                 :            : 
     343                 :          1 : static uint32_t ctrl_thread_loop(void *arg)
     344                 :            : {
     345                 :            :         struct thread_context *t = arg;
     346                 :            : 
     347                 :            :         printf("Control thread running successfully\n");
     348                 :            : 
     349                 :            :         /* Set the thread state to DONE */
     350                 :          1 :         t->state = Thread_DONE;
     351                 :            : 
     352                 :          1 :         return 0;
     353                 :            : }
     354                 :            : 
     355                 :            : static int
     356                 :          1 : test_ctrl_thread(void)
     357                 :            : {
     358                 :            :         struct thread_context ctrl_thread_context;
     359                 :            :         struct thread_context *t;
     360                 :            : 
     361                 :            :         /* Create one control thread */
     362                 :            :         t = &ctrl_thread_context;
     363                 :          1 :         t->state = Thread_INIT;
     364         [ +  - ]:          1 :         if (rte_thread_create_control(&t->id, "dpdk-test-ctrlt",
     365                 :            :                                 ctrl_thread_loop, t) != 0)
     366                 :            :                 return -1;
     367                 :            : 
     368                 :            :         /* Wait till the control thread exits.
     369                 :            :          * This also acts as the barrier such that the memory operations
     370                 :            :          * in control thread are visible to this thread.
     371                 :            :          */
     372                 :          1 :         rte_thread_join(t->id, NULL);
     373                 :            : 
     374                 :            :         /* Check if the control thread set the correct state */
     375         [ -  + ]:          1 :         if (t->state != Thread_DONE)
     376                 :          0 :                 return -1;
     377                 :            : 
     378                 :            :         return 0;
     379                 :            : }
     380                 :            : 
     381                 :            : static int
     382                 :          1 : test_lcores(void)
     383                 :            : {
     384                 :            :         unsigned int eal_threads_count = 0;
     385                 :            :         unsigned int i;
     386                 :            : 
     387         [ +  + ]:        129 :         for (i = 0; i < RTE_MAX_LCORE; i++) {
     388         [ +  + ]:        128 :                 if (!rte_lcore_has_role(i, ROLE_OFF))
     389                 :          2 :                         eal_threads_count++;
     390                 :            :         }
     391         [ -  + ]:          1 :         if (eal_threads_count == 0) {
     392                 :            :                 printf("Error: something is broken, no EAL thread detected.\n");
     393                 :          0 :                 return TEST_FAILED;
     394                 :            :         }
     395                 :            :         printf("EAL threads count: %u, RTE_MAX_LCORE=%u\n", eal_threads_count,
     396                 :            :                 RTE_MAX_LCORE);
     397                 :          1 :         rte_lcore_dump(stdout);
     398                 :            : 
     399         [ +  - ]:          1 :         if (test_non_eal_lcores(eal_threads_count) < 0)
     400                 :            :                 return TEST_FAILED;
     401                 :            : 
     402         [ +  - ]:          1 :         if (test_lcores_callback(eal_threads_count) < 0)
     403                 :            :                 return TEST_FAILED;
     404                 :            : 
     405         [ +  - ]:          1 :         if (test_non_eal_lcores_callback(eal_threads_count) < 0)
     406                 :            :                 return TEST_FAILED;
     407                 :            : 
     408         [ -  + ]:          1 :         if (test_ctrl_thread() < 0)
     409                 :          0 :                 return TEST_FAILED;
     410                 :            : 
     411                 :            :         return TEST_SUCCESS;
     412                 :            : }
     413                 :            : 
     414                 :        235 : REGISTER_FAST_TEST(lcores_autotest, true, true, test_lcores);

Generated by: LCOV version 1.14