LCOV - code coverage report
Current view: top level - spdk/test/event/scheduler - scheduler.c (source / functions) Hit Total Coverage
Test: Combined Lines: 153 197 77.7 %
Date: 2024-07-12 04:40:00 Functions: 21 22 95.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60 98 61.2 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2021 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : 
       8                 :            : #include "spdk/env.h"
       9                 :            : #include "spdk/event.h"
      10                 :            : #include "spdk/likely.h"
      11                 :            : #include "spdk/json.h"
      12                 :            : #include "spdk/jsonrpc.h"
      13                 :            : #include "spdk/rpc.h"
      14                 :            : #include "spdk/string.h"
      15                 :            : #include "spdk/thread.h"
      16                 :            : #include "spdk/util.h"
      17                 :            : 
      18                 :            : #include "spdk_internal/event.h"
      19                 :            : 
      20                 :            : static bool g_is_running = true;
      21                 :            : pthread_mutex_t g_sched_list_mutex = PTHREAD_MUTEX_INITIALIZER;
      22                 :            : #define TIMESLICE_US 100 * 1000
      23                 :            : static bool g_for_each_reactor = false;
      24                 :            : 
      25                 :            : struct sched_thread {
      26                 :            :         struct spdk_thread *thread;
      27                 :            :         struct spdk_poller *poller;
      28                 :            :         struct spdk_poller *idle_poller;
      29                 :            :         int active_percent;
      30                 :            :         struct spdk_jsonrpc_request *request;
      31                 :            :         TAILQ_ENTRY(sched_thread) link;
      32                 :            : };
      33                 :            : 
      34                 :            : static TAILQ_HEAD(, sched_thread) g_sched_threads = TAILQ_HEAD_INITIALIZER(g_sched_threads);
      35                 :            : 
      36                 :            : struct rpc_thread_create {
      37                 :            :         int active_percent;
      38                 :            :         char *name;
      39                 :            :         char *cpu_mask;
      40                 :            : };
      41                 :            : 
      42                 :            : static void
      43                 :        212 : free_rpc_thread_create(struct rpc_thread_create *req)
      44                 :            : {
      45                 :        212 :         free(req->name);
      46                 :        212 :         free(req->cpu_mask);
      47                 :        212 : }
      48                 :            : 
      49                 :            : static const struct spdk_json_object_decoder rpc_thread_create_decoders[] = {
      50                 :            :         {"active", offsetof(struct rpc_thread_create, active_percent), spdk_json_decode_uint64},
      51                 :            :         {"name", offsetof(struct rpc_thread_create, name), spdk_json_decode_string, true},
      52                 :            :         {"cpu_mask", offsetof(struct rpc_thread_create, cpu_mask), spdk_json_decode_string, true},
      53                 :            : };
      54                 :            : 
      55                 :            : static void
      56                 :        212 : rpc_scheduler_thread_create_cb(struct spdk_jsonrpc_request *request, uint64_t thread_id)
      57                 :            : {
      58                 :            :         struct spdk_json_write_ctx *w;
      59                 :            : 
      60                 :        212 :         w = spdk_jsonrpc_begin_result(request);
      61                 :        212 :         spdk_json_write_uint64(w, thread_id);
      62                 :        212 :         spdk_jsonrpc_end_result(request, w);
      63                 :        212 : }
      64                 :            : 
      65                 :            : static void
      66                 :        212 : thread_delete(struct sched_thread *sched_thread)
      67                 :            : {
      68                 :        212 :         spdk_poller_unregister(&sched_thread->poller);
      69                 :        212 :         spdk_poller_unregister(&sched_thread->idle_poller);
      70                 :        212 :         spdk_thread_exit(sched_thread->thread);
      71                 :            : 
      72         [ +  + ]:        212 :         TAILQ_REMOVE(&g_sched_threads, sched_thread, link);
      73                 :        212 :         free(sched_thread);
      74                 :            : 
      75   [ +  +  +  +  :        212 :         if (!g_is_running && TAILQ_EMPTY(&g_sched_threads)) {
                   +  + ]
      76                 :         19 :                 spdk_app_stop(0);
      77                 :            :         }
      78                 :        212 : }
      79                 :            : 
      80                 :            : static int
      81                 :       1620 : poller_run_busy(void *arg)
      82                 :            : {
      83                 :       1620 :         struct sched_thread *sched_thread = arg;
      84                 :            : 
      85   [ -  +  -  + ]:       1620 :         if (spdk_unlikely(!g_is_running)) {
      86                 :          0 :                 pthread_mutex_lock(&g_sched_list_mutex);
      87                 :          0 :                 thread_delete(sched_thread);
      88                 :          0 :                 pthread_mutex_unlock(&g_sched_list_mutex);
      89                 :          0 :                 return SPDK_POLLER_IDLE;
      90                 :            :         }
      91                 :            : 
      92                 :       1620 :         spdk_delay_us(TIMESLICE_US * sched_thread->active_percent / 100);
      93                 :       1620 :         return SPDK_POLLER_BUSY;
      94                 :            : }
      95                 :            : 
      96                 :            : static int
      97                 :    1911166 : poller_run_idle(void *arg)
      98                 :            : {
      99                 :    1911166 :         struct sched_thread *sched_thread = arg;
     100                 :            : 
     101   [ +  +  +  + ]:    1911166 :         if (spdk_unlikely(!g_is_running)) {
     102                 :        190 :                 pthread_mutex_lock(&g_sched_list_mutex);
     103                 :        190 :                 thread_delete(sched_thread);
     104                 :        190 :                 pthread_mutex_unlock(&g_sched_list_mutex);
     105                 :        190 :                 return SPDK_POLLER_IDLE;
     106                 :            :         }
     107                 :            : 
     108                 :    1910976 :         spdk_delay_us(10);
     109                 :    1910976 :         return SPDK_POLLER_IDLE;
     110                 :            : }
     111                 :            : 
     112                 :            : static void
     113                 :        234 : update_pollers(struct sched_thread *sched_thread)
     114                 :            : {
     115                 :        234 :         spdk_poller_unregister(&sched_thread->poller);
     116         [ +  + ]:        234 :         if (sched_thread->active_percent > 0) {
     117                 :        136 :                 sched_thread->poller = spdk_poller_register_named(poller_run_busy, sched_thread, TIMESLICE_US,
     118                 :        136 :                                        spdk_thread_get_name(sched_thread->thread));
     119         [ -  + ]:        136 :                 assert(sched_thread->poller != NULL);
     120                 :            :         }
     121         [ +  + ]:        234 :         if (sched_thread->idle_poller == NULL) {
     122                 :        212 :                 sched_thread->idle_poller = spdk_poller_register_named(poller_run_idle, sched_thread, 0,
     123                 :            :                                             "idle_poller");
     124         [ -  + ]:        212 :                 assert(sched_thread->idle_poller != NULL);
     125                 :            :         }
     126                 :        234 : }
     127                 :            : 
     128                 :            : static void
     129                 :        212 : rpc_register_poller(void *arg)
     130                 :            : {
     131                 :        212 :         struct sched_thread *sched_thread = arg;
     132                 :            : 
     133                 :        212 :         update_pollers(sched_thread);
     134                 :            : 
     135         [ +  - ]:        212 :         if (sched_thread->request != NULL) {
     136                 :        212 :                 rpc_scheduler_thread_create_cb(sched_thread->request, spdk_thread_get_id(sched_thread->thread));
     137                 :        212 :                 sched_thread->request = NULL;
     138                 :            :         }
     139                 :        212 : }
     140                 :            : 
     141                 :            : static void
     142                 :        212 : rpc_scheduler_thread_create(struct spdk_jsonrpc_request *request,
     143                 :            :                             const struct spdk_json_val *params)
     144                 :            : {
     145                 :            :         struct sched_thread *sched_thread;
     146                 :        212 :         struct rpc_thread_create req = {0};
     147                 :        212 :         struct spdk_cpuset *cpu_set = NULL;
     148                 :        212 :         int rc = 0;
     149                 :            : 
     150         [ -  + ]:        212 :         if (spdk_json_decode_object(params, rpc_thread_create_decoders,
     151                 :            :                                     SPDK_COUNTOF(rpc_thread_create_decoders),
     152                 :            :                                     &req)) {
     153                 :          0 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     154                 :            :                                                  "Invalid parameters provided");
     155                 :          0 :                 return;
     156                 :            :         }
     157                 :            : 
     158   [ +  -  -  + ]:        212 :         if (req.active_percent < 0 || req.active_percent > 100) {
     159                 :          0 :                 SPDK_ERRLOG("invalid percent value %d\n", req.active_percent);
     160                 :          0 :                 spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
     161                 :          0 :                 free_rpc_thread_create(&req);
     162                 :          0 :                 return;
     163                 :            :         }
     164                 :            : 
     165         [ +  + ]:        212 :         if (req.cpu_mask != NULL) {
     166                 :        155 :                 cpu_set = calloc(1, sizeof(*cpu_set));
     167         [ -  + ]:        155 :                 assert(cpu_set != NULL);
     168                 :        155 :                 rc = spdk_cpuset_parse(cpu_set, req.cpu_mask);
     169         [ -  + ]:        155 :                 if (rc < 0) {
     170                 :          0 :                         SPDK_ERRLOG("invalid cpumask %s\n", req.cpu_mask);
     171                 :          0 :                         spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
     172                 :          0 :                         free_rpc_thread_create(&req);
     173                 :          0 :                         free(cpu_set);
     174                 :          0 :                         return;
     175                 :            :                 }
     176                 :            :         }
     177                 :            : 
     178                 :        212 :         sched_thread = calloc(1, sizeof(*sched_thread));
     179         [ -  + ]:        212 :         assert(sched_thread != NULL);
     180                 :            : 
     181                 :        212 :         sched_thread->thread = spdk_thread_create(req.name, cpu_set);
     182         [ -  + ]:        212 :         assert(sched_thread->thread != NULL);
     183                 :        212 :         free(cpu_set);
     184                 :            : 
     185                 :        212 :         sched_thread->request = request;
     186                 :        212 :         sched_thread->active_percent = req.active_percent;
     187                 :            : 
     188                 :        212 :         spdk_thread_send_msg(sched_thread->thread, rpc_register_poller, sched_thread);
     189                 :            : 
     190                 :        212 :         free_rpc_thread_create(&req);
     191                 :            : 
     192         [ -  + ]:        212 :         pthread_mutex_lock(&g_sched_list_mutex);
     193                 :        212 :         TAILQ_INSERT_TAIL(&g_sched_threads, sched_thread, link);
     194         [ -  + ]:        212 :         pthread_mutex_unlock(&g_sched_list_mutex);
     195                 :            : 
     196                 :        212 :         return;
     197                 :            : }
     198                 :            : 
     199                 :         20 : SPDK_RPC_REGISTER("scheduler_thread_create", rpc_scheduler_thread_create, SPDK_RPC_RUNTIME)
     200                 :            : 
     201                 :            : struct rpc_thread_set_active_ctx {
     202                 :            :         int active_percent;
     203                 :            :         struct spdk_jsonrpc_request *request;
     204                 :            : };
     205                 :            : 
     206                 :            : struct rpc_thread_set_active {
     207                 :            :         uint64_t thread_id;
     208                 :            :         int active_percent;
     209                 :            : };
     210                 :            : 
     211                 :            : static const struct spdk_json_object_decoder rpc_thread_set_active_decoders[] = {
     212                 :            :         {"thread_id", offsetof(struct rpc_thread_set_active, thread_id), spdk_json_decode_uint64},
     213                 :            :         {"active", offsetof(struct rpc_thread_set_active, active_percent), spdk_json_decode_uint64},
     214                 :            : };
     215                 :            : 
     216                 :            : static void
     217                 :         22 : rpc_scheduler_thread_set_active_cb(void *arg)
     218                 :            : {
     219                 :         22 :         struct rpc_thread_set_active_ctx *ctx = arg;
     220                 :            :         uint64_t thread_id;
     221                 :            :         struct sched_thread *sched_thread;
     222                 :            : 
     223                 :         22 :         thread_id = spdk_thread_get_id(spdk_get_thread());
     224                 :            : 
     225         [ -  + ]:         22 :         pthread_mutex_lock(&g_sched_list_mutex);
     226         [ +  - ]:        196 :         TAILQ_FOREACH(sched_thread, &g_sched_threads, link) {
     227         [ +  + ]:        196 :                 if (spdk_thread_get_id(sched_thread->thread) == thread_id) {
     228                 :         22 :                         sched_thread->active_percent = ctx->active_percent;
     229                 :         22 :                         update_pollers(sched_thread);
     230         [ -  + ]:         22 :                         pthread_mutex_unlock(&g_sched_list_mutex);
     231                 :         22 :                         spdk_jsonrpc_send_bool_response(ctx->request, true);
     232                 :         22 :                         free(ctx);
     233                 :         22 :                         return;
     234                 :            :                 }
     235                 :            :         }
     236         [ #  # ]:          0 :         pthread_mutex_unlock(&g_sched_list_mutex);
     237                 :            : 
     238                 :          0 :         spdk_jsonrpc_send_error_response(ctx->request, -ENOENT, spdk_strerror(ENOENT));
     239                 :          0 :         free(ctx);
     240                 :          0 :         return;
     241                 :            : }
     242                 :            : 
     243                 :            : static void
     244                 :         22 : rpc_scheduler_thread_set_active(struct spdk_jsonrpc_request *request,
     245                 :            :                                 const struct spdk_json_val *params)
     246                 :            : {
     247                 :            :         struct spdk_thread *thread;
     248                 :         22 :         struct rpc_thread_set_active req = {0};
     249                 :            :         struct rpc_thread_set_active_ctx *ctx;
     250                 :            : 
     251         [ -  + ]:         22 :         if (spdk_json_decode_object(params, rpc_thread_set_active_decoders,
     252                 :            :                                     SPDK_COUNTOF(rpc_thread_set_active_decoders),
     253                 :            :                                     &req)) {
     254                 :          0 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     255                 :            :                                                  "Invalid parameters provided");
     256                 :          0 :                 return;
     257                 :            :         }
     258                 :            : 
     259   [ +  -  -  + ]:         22 :         if (req.active_percent < 0 || req.active_percent > 100) {
     260                 :          0 :                 SPDK_ERRLOG("invalid percent value %d\n", req.active_percent);
     261                 :          0 :                 spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
     262                 :          0 :                 return;
     263                 :            :         }
     264                 :            : 
     265                 :         22 :         thread = spdk_thread_get_by_id(req.thread_id);
     266         [ -  + ]:         22 :         if (thread == NULL) {
     267                 :          0 :                 spdk_jsonrpc_send_error_response(request, -ENOENT, spdk_strerror(ENOENT));
     268                 :          0 :                 return;
     269                 :            :         }
     270                 :            : 
     271                 :         22 :         ctx = calloc(1, sizeof(*ctx));
     272         [ -  + ]:         22 :         if (ctx == NULL) {
     273                 :          0 :                 spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(-ENOMEM));
     274                 :          0 :                 return;
     275                 :            :         }
     276                 :         22 :         ctx->request = request;
     277                 :         22 :         ctx->active_percent = req.active_percent;
     278                 :            : 
     279                 :         22 :         spdk_thread_send_msg(thread, rpc_scheduler_thread_set_active_cb, ctx);
     280                 :            : }
     281                 :            : 
     282                 :         20 : SPDK_RPC_REGISTER("scheduler_thread_set_active", rpc_scheduler_thread_set_active, SPDK_RPC_RUNTIME)
     283                 :            : 
     284                 :            : struct rpc_thread_delete_ctx {
     285                 :            :         struct spdk_jsonrpc_request *request;
     286                 :            : };
     287                 :            : 
     288                 :            : struct rpc_thread_delete {
     289                 :            :         uint64_t thread_id;
     290                 :            : };
     291                 :            : 
     292                 :            : static const struct spdk_json_object_decoder rpc_thread_delete_decoders[] = {
     293                 :            :         {"thread_id", offsetof(struct rpc_thread_delete, thread_id), spdk_json_decode_uint64},
     294                 :            : };
     295                 :            : 
     296                 :            : static void
     297                 :         22 : rpc_scheduler_thread_delete_cb(void *arg)
     298                 :            : {
     299                 :         22 :         struct rpc_thread_delete_ctx *ctx = arg;
     300                 :            :         struct sched_thread *sched_thread;
     301                 :            :         uint64_t thread_id;
     302                 :            : 
     303                 :         22 :         thread_id = spdk_thread_get_id(spdk_get_thread());
     304                 :            : 
     305         [ -  + ]:         22 :         pthread_mutex_lock(&g_sched_list_mutex);
     306         [ +  - ]:        212 :         TAILQ_FOREACH(sched_thread, &g_sched_threads, link) {
     307         [ +  + ]:        212 :                 if (spdk_thread_get_id(sched_thread->thread) == thread_id) {
     308                 :         22 :                         thread_delete(sched_thread);
     309         [ -  + ]:         22 :                         pthread_mutex_unlock(&g_sched_list_mutex);
     310                 :         22 :                         spdk_jsonrpc_send_bool_response(ctx->request, true);
     311                 :         22 :                         free(ctx);
     312                 :         22 :                         return;
     313                 :            :                 }
     314                 :            :         }
     315         [ #  # ]:          0 :         pthread_mutex_unlock(&g_sched_list_mutex);
     316                 :            : 
     317                 :          0 :         spdk_jsonrpc_send_error_response(ctx->request, -ENOENT, spdk_strerror(ENOENT));
     318                 :          0 :         free(ctx);
     319                 :          0 :         return;
     320                 :            : }
     321                 :            : 
     322                 :            : static void
     323                 :         22 : rpc_scheduler_thread_delete(struct spdk_jsonrpc_request *request,
     324                 :            :                             const struct spdk_json_val *params)
     325                 :            : {
     326                 :            :         struct spdk_thread *thread;
     327                 :         22 :         struct rpc_thread_delete req = {0};
     328                 :            :         struct rpc_thread_delete_ctx *ctx;
     329                 :            : 
     330         [ -  + ]:         22 :         if (spdk_json_decode_object(params, rpc_thread_delete_decoders,
     331                 :            :                                     SPDK_COUNTOF(rpc_thread_delete_decoders),
     332                 :            :                                     &req)) {
     333                 :          0 :                 spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
     334                 :            :                                                  "Invalid parameters provided");
     335                 :          0 :                 return;
     336                 :            :         }
     337                 :            : 
     338                 :         22 :         thread = spdk_thread_get_by_id(req.thread_id);
     339         [ -  + ]:         22 :         if (thread == NULL) {
     340                 :          0 :                 spdk_jsonrpc_send_error_response(request, -ENOENT, spdk_strerror(ENOENT));
     341                 :          0 :                 return;
     342                 :            :         }
     343                 :            : 
     344                 :         22 :         ctx = calloc(1, sizeof(*ctx));
     345         [ -  + ]:         22 :         if (ctx == NULL) {
     346                 :          0 :                 spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(-ENOMEM));
     347                 :          0 :                 return;
     348                 :            :         }
     349                 :         22 :         ctx->request = request;
     350                 :            : 
     351                 :         22 :         spdk_thread_send_msg(thread, rpc_scheduler_thread_delete_cb, ctx);
     352                 :            : }
     353                 :            : 
     354                 :         20 : SPDK_RPC_REGISTER("scheduler_thread_delete", rpc_scheduler_thread_delete, SPDK_RPC_RUNTIME)
     355                 :            : 
     356                 :            : static void
     357                 :         20 : test_shutdown(void)
     358                 :            : {
     359                 :         20 :         g_is_running = false;
     360                 :         20 :         SPDK_NOTICELOG("Scheduler test application stopped.\n");
     361         [ -  + ]:         20 :         pthread_mutex_lock(&g_sched_list_mutex);
     362         [ +  + ]:         20 :         if (TAILQ_EMPTY(&g_sched_threads)) {
     363                 :          1 :                 spdk_app_stop(0);
     364                 :            :         }
     365         [ -  + ]:         20 :         pthread_mutex_unlock(&g_sched_list_mutex);
     366                 :         20 : }
     367                 :            : 
     368                 :            : static void
     369                 :      75160 : for_each_nop(void *arg1, void *arg2)
     370                 :            : {
     371                 :      75160 : }
     372                 :            : 
     373                 :            : static void
     374                 :      18809 : for_each_reactor_start(void *arg1, void *arg2)
     375                 :            : {
     376                 :      18809 :         spdk_for_each_reactor(for_each_nop, NULL, NULL, for_each_reactor_start);
     377                 :      18809 : }
     378                 :            : 
     379                 :            : static void
     380                 :         20 : test_start(void *arg1)
     381                 :            : {
     382                 :         20 :         SPDK_NOTICELOG("Scheduler test application started.\n");
     383                 :            :         /* Start an spdk_for_each_reactor operation that just keeps
     384                 :            :          * running over and over again until the app exits.  This
     385                 :            :          * serves as a regression test for SPDK issue #2206, ensuring
     386                 :            :          * that any pending spdk_for_each_reactor operations are
     387                 :            :          * completed before reactors are shut down.
     388                 :            :          */
     389   [ +  +  +  + ]:         20 :         if (g_for_each_reactor) {
     390                 :         19 :                 for_each_reactor_start(NULL, NULL);
     391                 :            :         }
     392                 :         20 : }
     393                 :            : 
     394                 :            : static void
     395                 :          0 : scheduler_usage(void)
     396                 :            : {
     397         [ #  # ]:          0 :         printf(" -f                        Enable spdk_for_each_reactor regression test\n");
     398                 :          0 : }
     399                 :            : 
     400                 :            : static int
     401                 :         19 : scheduler_parse_arg(int ch, char *arg)
     402                 :            : {
     403         [ +  - ]:         19 :         switch (ch) {
     404                 :         19 :         case 'f':
     405                 :         19 :                 g_for_each_reactor = true;
     406                 :         19 :                 break;
     407                 :          0 :         default:
     408                 :          0 :                 return -EINVAL;
     409                 :            :         }
     410                 :         19 :         return 0;
     411                 :            : }
     412                 :            : 
     413                 :            : int
     414                 :         20 : main(int argc, char **argv)
     415                 :            : {
     416                 :          8 :         struct spdk_app_opts opts;
     417                 :         20 :         int rc = 0;
     418                 :            : 
     419                 :         20 :         spdk_app_opts_init(&opts, sizeof(opts));
     420                 :         20 :         opts.name = "scheduler";
     421                 :         20 :         opts.shutdown_cb = test_shutdown;
     422                 :            : 
     423         [ -  + ]:         20 :         if ((rc = spdk_app_parse_args(argc, argv, &opts,
     424                 :            :                                       "f", NULL, scheduler_parse_arg, scheduler_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
     425                 :          0 :                 return rc;
     426                 :            :         }
     427                 :            : 
     428                 :         20 :         rc = spdk_app_start(&opts, test_start, NULL);
     429                 :            : 
     430                 :         20 :         spdk_app_fini();
     431                 :            : 
     432                 :         20 :         return rc;
     433                 :            : }

Generated by: LCOV version 1.14