LCOV - code coverage report
Current view: top level - app/test - test_lcores.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 122 169 72.2 %
Date: 2024-04-01 19:00:53 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 73 128 57.0 %

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

Generated by: LCOV version 1.14