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 [ + + + + : 27059301 : while (!g_exit && !g_test_done) {
- + + - ]
164 : 27059264 : 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 *)®s->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 : : }
|