LCOV - code coverage report
Current view: top level - spdk/test/nvme/doorbell_aers - doorbell_aers.c (source / functions) Hit Total Coverage
Test: Combined Lines: 96 157 61.1 %
Date: 2024-07-15 11:30:19 Functions: 13 17 76.5 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 28 128 21.9 %

           Branch data     Line data    Source code
       1                 :            : /*   SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  *   Copyright (C) 2023 Intel Corporation.
       3                 :            :  *   All rights reserved.
       4                 :            :  */
       5                 :            : 
       6                 :            : #include "spdk/stdinc.h"
       7                 :            : 
       8                 :            : #include "spdk/env.h"
       9                 :            : #include "spdk/nvme.h"
      10                 :            : #include "spdk/mmio.h"
      11                 :            : 
      12                 :            : #define IO_QUEUE_SIZE 32
      13                 :            : 
      14                 :            : static struct spdk_nvme_transport_id g_trid;
      15                 :            : static struct spdk_nvme_ctrlr *g_ctrlr;
      16                 :            : struct spdk_nvme_qpair *g_io_qpair;
      17                 :            : 
      18                 :            : uint32_t g_qpair_id;
      19                 :            : uint32_t *g_doorbell_base;
      20                 :            : uint32_t g_doorbell_stride_u32;
      21                 :            : 
      22                 :            : static union spdk_nvme_async_event_completion g_expected_event;
      23                 :            : static bool g_test_done;
      24                 :            : 
      25                 :            : static struct spdk_nvme_error_information_entry g_error_entries[256];
      26                 :            : 
      27                 :            : static bool g_exit;
      28                 :            : 
      29                 :            : static void
      30                 :          0 : usage(char *program_name)
      31                 :            : {
      32         [ #  # ]:          0 :         printf("%s options", program_name);
      33                 :          0 :         printf("\n");
      34         [ #  # ]:          0 :         printf("\t[-r <fmt> Transport ID for PCIe NVMe device]\n");
      35         [ #  # ]:          0 :         printf("\t Format: 'key:value [key:value] ...'\n");
      36         [ #  # ]:          0 :         printf("\t Keys:\n");
      37         [ #  # ]:          0 :         printf("\t  trtype      Transport type (PCIe)\n");
      38         [ #  # ]:          0 :         printf("\t  traddr      Transport address (e.g. 0000:db:00.0)\n");
      39         [ #  # ]:          0 :         printf("\t Example: -r 'trtype:PCIe traddr:0000:db:00.0'\n");
      40                 :          0 :         printf("\t");
      41                 :          0 : }
      42                 :            : 
      43                 :            : static int
      44                 :         10 : parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
      45                 :            : {
      46                 :            :         int op;
      47                 :            : 
      48   [ +  +  +  +  :         20 :         while ((op = getopt(argc, argv, "r:")) != -1) {
                   +  + ]
      49         [ +  - ]:         10 :                 switch (op) {
      50                 :         10 :                 case 'r':
      51         [ -  + ]:         10 :                         if (spdk_nvme_transport_id_parse(&g_trid, optarg) != 0) {
      52   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "Invalid transport ID format '%s'\n", optarg);
      53                 :          0 :                                 exit(1);
      54                 :            :                         }
      55                 :            : 
      56         [ -  + ]:         10 :                         if (g_trid.trtype != SPDK_NVME_TRANSPORT_PCIE) {
      57   [ #  #  #  # ]:          0 :                                 fprintf(stderr, "Invalid transport type, expected PCIe");
      58                 :          0 :                                 exit(1);
      59                 :            :                         }
      60                 :            : 
      61                 :         10 :                         break;
      62                 :          0 :                 default:
      63                 :          0 :                         usage(argv[0]);
      64                 :          0 :                         return 1;
      65                 :            :                 }
      66                 :            :         }
      67                 :            : 
      68                 :         10 :         return 0;
      69                 :            : }
      70                 :            : 
      71                 :            : static void
      72                 :         20 : sig_handler(int signo)
      73                 :            : {
      74                 :         20 :         g_exit = true;
      75                 :         20 : }
      76                 :            : 
      77                 :            : static void
      78                 :         10 : setup_sig_handlers(void)
      79                 :            : {
      80                 :         10 :         struct sigaction sigact = {};
      81                 :            :         int rc;
      82                 :            : 
      83                 :         10 :         sigemptyset(&sigact.sa_mask);
      84                 :         10 :         sigact.sa_handler = sig_handler;
      85                 :         10 :         rc = sigaction(SIGINT, &sigact, NULL);
      86         [ -  + ]:         10 :         if (rc < 0) {
      87         [ #  # ]:          0 :                 fprintf(stderr, "sigaction(SIGINT) failed, errno %d (%s)\n", errno, strerror(errno));
      88                 :          0 :                 exit(1);
      89                 :            :         }
      90                 :            : 
      91                 :         10 :         rc = sigaction(SIGTERM, &sigact, NULL);
      92         [ -  + ]:         10 :         if (rc < 0) {
      93         [ #  # ]:          0 :                 fprintf(stderr, "sigaction(SIGTERM) failed, errno %d (%s)\n", errno, strerror(errno));
      94                 :          0 :                 exit(1);
      95                 :            :         }
      96                 :         10 : }
      97                 :            : 
      98                 :            : static void
      99                 :          0 : get_error_log_page_completion(void *arg, const struct spdk_nvme_cpl *cpl)
     100                 :            : {
     101   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
     102   [ #  #  #  # ]:          0 :                 fprintf(stderr, "get error log page failed\n");
     103                 :          0 :                 exit(1);
     104                 :            :         }
     105                 :            : 
     106                 :            :         /* TODO: do handling (print?) of error log page */
     107         [ #  # ]:          0 :         printf("Error Informaton Log Page received.\n");
     108                 :          0 :         g_test_done = true;
     109                 :          0 : }
     110                 :            : 
     111                 :            : static void
     112                 :          0 : get_error_log_page(void)
     113                 :            : {
     114                 :            :         const struct spdk_nvme_ctrlr_data *cdata;
     115                 :            : 
     116                 :          0 :         cdata = spdk_nvme_ctrlr_get_data(g_ctrlr);
     117                 :            : 
     118         [ #  # ]:          0 :         if (spdk_nvme_ctrlr_cmd_get_log_page(g_ctrlr, SPDK_NVME_LOG_ERROR,
     119                 :            :                                              SPDK_NVME_GLOBAL_NS_TAG, g_error_entries,
     120                 :          0 :                                              sizeof(*g_error_entries) * (cdata->elpe + 1),
     121                 :            :                                              0,
     122                 :            :                                              get_error_log_page_completion, NULL)) {
     123   [ #  #  #  # ]:          0 :                 fprintf(stderr, "spdk_nvme_ctrlr_cmd_get_log_page() failed\n");
     124                 :          0 :                 exit(1);
     125                 :            :         }
     126                 :          0 : }
     127                 :            : 
     128                 :            : static void
     129                 :          0 : aer_cb(void *arg, const struct spdk_nvme_cpl *cpl)
     130                 :            : {
     131                 :            :         union spdk_nvme_async_event_completion event;
     132                 :            : 
     133                 :          0 :         event.raw = cpl->cdw0;
     134                 :            : 
     135         [ #  # ]:          0 :         printf("Asynchronous Event received.\n");
     136                 :            : 
     137   [ #  #  #  # ]:          0 :         if (spdk_nvme_cpl_is_error(cpl)) {
     138   [ #  #  #  # ]:          0 :                 fprintf(stderr, "aer failed\n");
     139                 :          0 :                 exit(1);
     140                 :            :         }
     141                 :            : 
     142         [ #  # ]:          0 :         if (event.bits.async_event_type != g_expected_event.bits.async_event_type) {
     143   [ #  #  #  # ]:          0 :                 fprintf(stderr, "unexpected async event type 0x%x\n", event.bits.async_event_type);
     144                 :          0 :                 exit(1);
     145                 :            :         }
     146                 :            : 
     147         [ #  # ]:          0 :         if (event.bits.async_event_info != g_expected_event.bits.async_event_info) {
     148   [ #  #  #  # ]:          0 :                 fprintf(stderr, "unexpected async event info 0x%x\n", event.bits.async_event_info);
     149                 :          0 :                 exit(1);
     150                 :            :         }
     151                 :            : 
     152         [ #  # ]:          0 :         if (event.bits.log_page_identifier != g_expected_event.bits.log_page_identifier) {
     153   [ #  #  #  # ]:          0 :                 fprintf(stderr, "unexpected async event log page 0x%x\n", event.bits.log_page_identifier);
     154                 :          0 :                 exit(1);
     155                 :            :         }
     156                 :            : 
     157                 :          0 :         get_error_log_page();
     158                 :          0 : }
     159                 :            : 
     160                 :            : static void
     161                 :         30 : wait_for_aer_and_log_page_cpl(void)
     162                 :            : {
     163   [ +  +  +  +  :   26866787 :         while (!g_exit && !g_test_done) {
             -  +  +  - ]
     164                 :   26866750 :                 spdk_nvme_ctrlr_process_admin_completions(g_ctrlr);
     165                 :            :         }
     166                 :         30 : }
     167                 :            : 
     168                 :            : static void
     169                 :         10 : create_ctrlr(void)
     170                 :            : {
     171                 :         10 :         g_ctrlr = spdk_nvme_connect(&g_trid, NULL, 0);
     172         [ -  + ]:         10 :         if (g_ctrlr == NULL) {
     173   [ #  #  #  # ]:          0 :                 fprintf(stderr, "spdk_nvme_connect() failed for transport address '%s'\n", g_trid.traddr);
     174                 :          0 :                 exit(1);
     175                 :            :         }
     176                 :         10 : }
     177                 :            : 
     178                 :            : static void
     179                 :         10 : create_io_qpair(void)
     180                 :            : {
     181                 :          6 :         struct spdk_nvme_io_qpair_opts opts;
     182                 :            : 
     183                 :            :         /* Override io_queue_size here, instead of doing it at connect time with
     184                 :            :          * the ctrlr_opts.  This is because stub app could be running, meaning
     185                 :            :          * that ctrlr opts were already set.
     186                 :            :          */
     187                 :         10 :         spdk_nvme_ctrlr_get_default_io_qpair_opts(g_ctrlr, &opts, sizeof(opts));
     188                 :         10 :         opts.io_queue_size = IO_QUEUE_SIZE;
     189                 :         10 :         opts.io_queue_requests = IO_QUEUE_SIZE;
     190                 :            : 
     191                 :         10 :         g_io_qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_ctrlr, &opts, sizeof(opts));
     192         [ -  + ]:         10 :         if (!g_io_qpair) {
     193   [ #  #  #  # ]:          0 :                 fprintf(stderr, "failed to spdk_nvme_ctrlr_alloc_io_qpair()");
     194                 :          0 :                 exit(1);
     195                 :            :         }
     196                 :            : 
     197                 :         10 :         g_qpair_id = spdk_nvme_qpair_get_id(g_io_qpair);
     198                 :         10 : }
     199                 :            : 
     200                 :            : static void
     201                 :         10 : set_doorbell_vars(void)
     202                 :            : {
     203                 :         10 :         volatile struct spdk_nvme_registers *regs = spdk_nvme_ctrlr_get_registers(g_ctrlr);
     204                 :            : 
     205         [ -  + ]:         10 :         g_doorbell_stride_u32 = 1 << regs->cap.bits.dstrd;
     206                 :         10 :         g_doorbell_base = (uint32_t *)&regs->doorbell[0].sq_tdbl;
     207                 :         10 : }
     208                 :            : 
     209                 :            : 
     210                 :            : static void
     211                 :         30 : pre_test(const char *test_name, enum spdk_nvme_async_event_info_error aec_info)
     212                 :            : {
     213         [ -  + ]:         30 :         printf("Executing: %s\n", test_name);
     214                 :            : 
     215                 :         30 :         g_test_done = false;
     216                 :            : 
     217                 :         30 :         g_expected_event.bits.async_event_type = SPDK_NVME_ASYNC_EVENT_TYPE_ERROR;
     218                 :         30 :         g_expected_event.bits.log_page_identifier = SPDK_NVME_LOG_ERROR;
     219                 :         30 :         g_expected_event.bits.async_event_info = aec_info;
     220                 :         30 : }
     221                 :            : 
     222                 :            : static void
     223                 :         30 : post_test(const char *test_name)
     224                 :            : {
     225         [ -  + ]:         30 :         printf("Waiting for AER completion...\n");
     226                 :         30 :         wait_for_aer_and_log_page_cpl();
     227   [ -  +  -  +  :         30 :         printf("%s: %s\n\n", g_test_done ? "Success" : "Failure", test_name);
                   -  + ]
     228                 :         30 : }
     229                 :            : 
     230                 :            : static void
     231                 :         10 : test_write_invalid_db(void)
     232                 :            : {
     233                 :            :         volatile uint32_t *wrong_db;
     234                 :            : 
     235                 :         10 :         pre_test(__func__, SPDK_NVME_ASYNC_EVENT_WRITE_INVALID_DB);
     236                 :            : 
     237                 :         10 :         spdk_wmb();
     238                 :            :         /* Write to invalid register (note g_qpair_id + 1). */
     239                 :         10 :         wrong_db = g_doorbell_base + (2 * (g_qpair_id + 1) + 0) * g_doorbell_stride_u32;
     240                 :         10 :         spdk_mmio_write_4(wrong_db, 0);
     241                 :            : 
     242                 :         10 :         post_test(__func__);
     243                 :         10 : }
     244                 :            : 
     245                 :            : static void
     246                 :         10 : test_invalid_db_write_overflow_sq(void)
     247                 :            : {
     248                 :            :         volatile uint32_t *good_db;
     249                 :            : 
     250                 :         10 :         pre_test(__func__, SPDK_NVME_ASYNC_EVENT_INVALID_DB_WRITE);
     251                 :            : 
     252                 :         10 :         spdk_wmb();
     253                 :         10 :         good_db = g_doorbell_base + (2 * g_qpair_id + 0) * g_doorbell_stride_u32;
     254                 :            :         /* Overflow SQ doorbell over queue size. */
     255                 :         10 :         spdk_mmio_write_4(good_db, IO_QUEUE_SIZE + 1);
     256                 :            : 
     257                 :         10 :         post_test(__func__);
     258                 :         10 : }
     259                 :            : 
     260                 :            : static void
     261                 :         10 : test_invalid_db_write_overflow_cq(void)
     262                 :            : {
     263                 :            :         volatile uint32_t *good_db;
     264                 :            : 
     265                 :         10 :         pre_test(__func__, SPDK_NVME_ASYNC_EVENT_INVALID_DB_WRITE);
     266                 :            : 
     267                 :         10 :         good_db = g_doorbell_base + (2 * g_qpair_id + 1) * g_doorbell_stride_u32;
     268                 :         10 :         spdk_wmb();
     269                 :            :         /* Overflow CQ doorbell over queue size. */
     270                 :         10 :         spdk_mmio_write_4(good_db, IO_QUEUE_SIZE + 1);
     271                 :            : 
     272                 :         10 :         post_test(__func__);
     273                 :         10 : }
     274                 :            : 
     275                 :            : int
     276                 :         10 : main(int argc, char **argv)
     277                 :            : {
     278                 :            :         int rc;
     279                 :          6 :         struct spdk_env_opts opts;
     280                 :         10 :         struct spdk_nvme_detach_ctx *detach_ctx = NULL;
     281                 :            : 
     282                 :         10 :         spdk_env_opts_init(&opts);
     283                 :         10 :         opts.name = "doorbell_aers";
     284                 :         10 :         opts.shm_id = 0;
     285                 :            : 
     286                 :         10 :         rc = parse_args(argc, argv, &opts);
     287         [ -  + ]:         10 :         if (rc != 0) {
     288                 :          0 :                 exit(1);
     289                 :            :         }
     290                 :            : 
     291         [ -  + ]:         10 :         if (spdk_env_init(&opts) < 0) {
     292   [ #  #  #  # ]:          0 :                 fprintf(stderr, "Unable to initialize SPDK env\n");
     293                 :          0 :                 exit(1);
     294                 :            :         }
     295                 :            : 
     296                 :         10 :         setup_sig_handlers();
     297                 :            : 
     298                 :         10 :         create_ctrlr();
     299                 :         10 :         create_io_qpair();
     300                 :            : 
     301                 :         10 :         set_doorbell_vars();
     302                 :            : 
     303                 :         10 :         spdk_nvme_ctrlr_register_aer_callback(g_ctrlr, aer_cb, NULL);
     304                 :            : 
     305                 :         10 :         test_write_invalid_db();
     306                 :         10 :         test_invalid_db_write_overflow_sq();
     307                 :         10 :         test_invalid_db_write_overflow_cq();
     308                 :            : 
     309                 :         10 :         spdk_nvme_detach_async(g_ctrlr, &detach_ctx);
     310                 :            : 
     311         [ -  + ]:         10 :         if (detach_ctx) {
     312                 :          0 :                 spdk_nvme_detach_poll(detach_ctx);
     313                 :            :         }
     314                 :            : 
     315                 :         10 :         spdk_env_fini();
     316                 :            : 
     317                 :         10 :         return 0;
     318                 :            : }

Generated by: LCOV version 1.14