Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2018 Intel Corporation. All rights reserved.
3 : : * Copyright (c) 2019 Mellanox Technologies LTD. All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/conf.h"
8 : : #include "spdk/env.h"
9 : : #include "spdk/event.h"
10 : : #include "spdk/util.h"
11 : : #include "spdk/string.h"
12 : : #include "spdk/nvme_spec.h"
13 : : #include "spdk/nvme.h"
14 : : #include "spdk/likely.h"
15 : : #include "spdk/json.h"
16 : : #include "fuzz_common.h"
17 : :
18 : : #define UNIQUE_OPCODES 256
19 : :
20 : : const char g_nvme_cmd_json_name[] = "struct spdk_nvme_cmd";
21 : : char *g_json_file = NULL;
22 : : uint64_t g_runtime_ticks;
23 : : unsigned int g_seed_value = 0;
24 : : int g_runtime;
25 : :
26 : : int g_num_active_threads = 0;
27 : : uint32_t g_admin_depth = 16;
28 : : uint32_t g_io_depth = 128;
29 : : bool g_check_iommu = true;
30 : :
31 : : bool g_valid_ns_only = false;
32 : : bool g_verbose_mode = false;
33 : : bool g_run_admin_commands = false;
34 : : bool g_run;
35 : :
36 : : struct spdk_poller *g_app_completion_poller;
37 : : bool g_successful_io_opcodes[UNIQUE_OPCODES] = {0};
38 : : bool g_successful_admin_opcodes[UNIQUE_OPCODES] = {0};
39 : :
40 : : struct spdk_nvme_cmd *g_cmd_array;
41 : : size_t g_cmd_array_size;
42 : :
43 : : /* I need context objects here because I need to keep track of all I/O that are in flight. */
44 : : struct nvme_fuzz_request {
45 : : struct spdk_nvme_cmd cmd;
46 : : struct nvme_fuzz_qp *qp;
47 : : TAILQ_ENTRY(nvme_fuzz_request) link;
48 : : };
49 : :
50 : : struct nvme_fuzz_trid {
51 : : struct spdk_nvme_transport_id trid;
52 : : TAILQ_ENTRY(nvme_fuzz_trid) tailq;
53 : : };
54 : :
55 : : struct nvme_fuzz_ctrlr {
56 : : struct spdk_nvme_ctrlr *ctrlr;
57 : : TAILQ_ENTRY(nvme_fuzz_ctrlr) tailq;
58 : : };
59 : :
60 : : struct nvme_fuzz_qp {
61 : : struct spdk_nvme_qpair *qpair;
62 : : /* array of context objects equal in length to the queue depth */
63 : : struct nvme_fuzz_request *req_ctx;
64 : : TAILQ_HEAD(, nvme_fuzz_request) free_ctx_objs;
65 : : TAILQ_HEAD(, nvme_fuzz_request) outstanding_ctx_objs;
66 : : unsigned int random_seed;
67 : : uint64_t completed_cmd_counter;
68 : : uint64_t submitted_cmd_counter;
69 : : uint64_t successful_completed_cmd_counter;
70 : : uint64_t timeout_tsc;
71 : : uint32_t num_cmds_outstanding;
72 : : bool timed_out;
73 : : bool is_admin;
74 : : };
75 : :
76 : : struct nvme_fuzz_ns {
77 : : struct spdk_nvme_ns *ns;
78 : : struct spdk_nvme_ctrlr *ctrlr;
79 : : struct spdk_thread *thread;
80 : : struct spdk_poller *req_poller;
81 : : struct nvme_fuzz_qp io_qp;
82 : : struct nvme_fuzz_qp a_qp;
83 : : uint32_t nsid;
84 : : TAILQ_ENTRY(nvme_fuzz_ns) tailq;
85 : : };
86 : :
87 : : static TAILQ_HEAD(, nvme_fuzz_ns) g_ns_list = TAILQ_HEAD_INITIALIZER(g_ns_list);
88 : : static TAILQ_HEAD(, nvme_fuzz_ctrlr) g_ctrlr_list = TAILQ_HEAD_INITIALIZER(g_ctrlr_list);
89 : : static TAILQ_HEAD(, nvme_fuzz_trid) g_trid_list = TAILQ_HEAD_INITIALIZER(g_trid_list);
90 : :
91 : : static bool
92 : 0 : parse_nvme_cmd_obj(void *item, struct spdk_json_val *value, size_t num_values)
93 : : {
94 : 0 : struct spdk_nvme_cmd *cmd = item;
95 : : struct spdk_json_val *next_val;
96 : 0 : uint64_t tmp_val;
97 : 0 : size_t i = 0;
98 : :
99 [ # # ]: 0 : while (i < num_values) {
100 [ # # ]: 0 : if (value->type == SPDK_JSON_VAL_NAME) {
101 : 0 : next_val = value + 1;
102 [ # # # # ]: 0 : if (!strncmp(value->start, "opc", value->len)) {
103 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
104 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UNSIGNED_8BIT_MAX, &tmp_val)) {
105 : 0 : goto invalid;
106 : : }
107 : 0 : cmd->opc = tmp_val;
108 : : }
109 [ # # # # ]: 0 : } else if (!strncmp(value->start, "fuse", value->len)) {
110 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
111 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UNSIGNED_2BIT_MAX, &tmp_val)) {
112 : 0 : goto invalid;
113 : : }
114 : 0 : cmd->fuse = tmp_val;
115 : : }
116 [ # # # # ]: 0 : } else if (!strncmp(value->start, "rsvd1", value->len)) {
117 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
118 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UNSIGNED_4BIT_MAX, &tmp_val)) {
119 : 0 : goto invalid;
120 : : }
121 : 0 : cmd->rsvd1 = tmp_val;
122 : : }
123 [ # # # # ]: 0 : } else if (!strncmp(value->start, "psdt", value->len)) {
124 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
125 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UNSIGNED_2BIT_MAX, &tmp_val)) {
126 : 0 : goto invalid;
127 : : }
128 : 0 : cmd->psdt = tmp_val;
129 : : }
130 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cid", value->len)) {
131 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
132 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT16_MAX, &tmp_val)) {
133 : 0 : goto invalid;
134 : : }
135 : 0 : cmd->cid = tmp_val;
136 : : }
137 [ # # # # ]: 0 : } else if (!strncmp(value->start, "nsid", value->len)) {
138 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
139 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
140 : 0 : goto invalid;
141 : : }
142 : 0 : cmd->nsid = tmp_val;
143 : : }
144 [ # # # # ]: 0 : } else if (!strncmp(value->start, "rsvd2", value->len)) {
145 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
146 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
147 : 0 : goto invalid;
148 : : }
149 : 0 : cmd->rsvd2 = tmp_val;
150 : : }
151 [ # # # # ]: 0 : } else if (!strncmp(value->start, "rsvd3", value->len)) {
152 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
153 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
154 : 0 : goto invalid;
155 : : }
156 : 0 : cmd->rsvd3 = tmp_val;
157 : : }
158 [ # # # # ]: 0 : } else if (!strncmp(value->start, "mptr", value->len)) {
159 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
160 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT64_MAX, &tmp_val)) {
161 : 0 : goto invalid;
162 : : }
163 : 0 : cmd->mptr = tmp_val;
164 : : }
165 [ # # # # ]: 0 : } else if (!strncmp(value->start, "dptr", value->len)) {
166 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_STRING) {
167 [ # # ]: 0 : if (fuzz_get_base_64_buffer_value(&cmd->dptr, sizeof(cmd->dptr), (char *)next_val->start,
168 : 0 : next_val->len)) {
169 : 0 : goto invalid;
170 : : }
171 : : }
172 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw10", value->len)) {
173 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
174 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
175 : 0 : goto invalid;
176 : : }
177 : 0 : cmd->cdw10 = tmp_val;
178 : : }
179 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw11", value->len)) {
180 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
181 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
182 : 0 : goto invalid;
183 : : }
184 : 0 : cmd->cdw11 = tmp_val;
185 : : }
186 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw12", value->len)) {
187 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
188 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
189 : 0 : goto invalid;
190 : : }
191 : 0 : cmd->cdw12 = tmp_val;
192 : : }
193 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw13", value->len)) {
194 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
195 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
196 : 0 : goto invalid;
197 : : }
198 : 0 : cmd->cdw13 = tmp_val;
199 : : }
200 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw14", value->len)) {
201 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
202 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
203 : 0 : goto invalid;
204 : : }
205 : 0 : cmd->cdw14 = tmp_val;
206 : : }
207 [ # # # # ]: 0 : } else if (!strncmp(value->start, "cdw15", value->len)) {
208 [ # # ]: 0 : if (next_val->type == SPDK_JSON_VAL_NUMBER) {
209 [ # # ]: 0 : if (fuzz_parse_json_num(next_val, UINT32_MAX, &tmp_val)) {
210 : 0 : goto invalid;
211 : : }
212 : 0 : cmd->cdw15 = tmp_val;
213 : : }
214 : : }
215 : : }
216 : 0 : i++;
217 : 0 : value++;
218 : : }
219 : 0 : return true;
220 : :
221 : 0 : invalid:
222 [ # # ]: 0 : fprintf(stderr, "Invalid value supplied for cmd->%.*s: %.*s\n", value->len, (char *)value->start,
223 : 0 : next_val->len, (char *)next_val->start);
224 : 0 : return false;
225 : : }
226 : :
227 : : static void
228 : 2 : report_successful_opcodes(bool *array, int length)
229 : : {
230 : : int i;
231 : :
232 [ + + ]: 514 : for (i = 0; i < length; i++) {
233 [ - + + + ]: 512 : if (array[i] == true) {
234 [ - + ]: 5 : printf("%d, ", i);
235 : : }
236 : : }
237 : 2 : printf("\n");
238 : 2 : }
239 : :
240 : : static int
241 : 5924 : print_nvme_cmd(void *cb_ctx, const void *data, size_t size)
242 : : {
243 [ - + - + ]: 5924 : fprintf(stderr, "%s\n", (const char *)data);
244 : 5924 : return 0;
245 : : }
246 : :
247 : : static void
248 : 5924 : json_dump_nvme_cmd(struct spdk_nvme_cmd *cmd)
249 : : {
250 : : struct spdk_json_write_ctx *w;
251 : : char *dptr_value;
252 : :
253 : 5924 : dptr_value = fuzz_get_value_base_64_buffer(&cmd->dptr, sizeof(cmd->dptr));
254 [ - + ]: 5924 : if (dptr_value == NULL) {
255 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate buffer context for printing command.\n");
256 : 0 : return;
257 : : }
258 : :
259 : 5924 : w = spdk_json_write_begin(print_nvme_cmd, cmd, SPDK_JSON_WRITE_FLAG_FORMATTED);
260 [ - + ]: 5924 : if (w == NULL) {
261 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate json context for printing command.\n");
262 : 0 : free(dptr_value);
263 : 0 : return;
264 : : }
265 : :
266 : 5924 : spdk_json_write_named_object_begin(w, g_nvme_cmd_json_name);
267 : 5924 : spdk_json_write_named_uint32(w, "opc", cmd->opc);
268 : 5924 : spdk_json_write_named_uint32(w, "fuse", cmd->fuse);
269 : 5924 : spdk_json_write_named_uint32(w, "rsvd1", cmd->rsvd1);
270 : 5924 : spdk_json_write_named_uint32(w, "psdt", cmd->psdt);
271 : 5924 : spdk_json_write_named_uint32(w, "cid", cmd->cid);
272 : 5924 : spdk_json_write_named_uint32(w, "nsid", cmd->nsid);
273 : 5924 : spdk_json_write_named_uint32(w, "rsvd2", cmd->rsvd2);
274 : 5924 : spdk_json_write_named_uint32(w, "rsvd3", cmd->rsvd3);
275 : 5924 : spdk_json_write_named_uint32(w, "mptr", cmd->mptr);
276 : 5924 : spdk_json_write_named_string(w, "dptr", dptr_value);
277 : 5924 : spdk_json_write_named_uint32(w, "cdw10", cmd->cdw10);
278 : 5924 : spdk_json_write_named_uint32(w, "cdw11", cmd->cdw11);
279 : 5924 : spdk_json_write_named_uint32(w, "cdw12", cmd->cdw12);
280 : 5924 : spdk_json_write_named_uint32(w, "cdw13", cmd->cdw13);
281 : 5924 : spdk_json_write_named_uint32(w, "cdw14", cmd->cdw14);
282 : 5924 : spdk_json_write_named_uint32(w, "cdw15", cmd->cdw15);
283 : 5924 : spdk_json_write_object_end(w);
284 : :
285 : 5924 : free(dptr_value);
286 : 5924 : spdk_json_write_end(w);
287 : : }
288 : :
289 : : static void
290 : 0 : json_dump_nvme_cmd_list(struct nvme_fuzz_qp *qp)
291 : : {
292 : : struct nvme_fuzz_request *ctx;
293 : :
294 [ # # ]: 0 : TAILQ_FOREACH(ctx, &qp->outstanding_ctx_objs, link) {
295 : 0 : json_dump_nvme_cmd(&ctx->cmd);
296 : : }
297 : 0 : }
298 : :
299 : : static void
300 : 0 : handle_timeout(struct nvme_fuzz_qp *qp, bool is_admin)
301 : : {
302 [ # # # # : 0 : fprintf(stderr, "An %s queue has timed out. Dumping all outstanding commands from that queue\n",
# # ]
303 : : is_admin ? "Admin" : "I/O");
304 : 0 : json_dump_nvme_cmd_list(qp);
305 : 0 : qp->timed_out = true;
306 : 0 : }
307 : :
308 : : static void submit_ns_cmds(struct nvme_fuzz_ns *ns_entry);
309 : :
310 : : static void
311 : 1249389 : nvme_fuzz_cpl_cb(void *cb_arg, const struct spdk_nvme_cpl *cpl)
312 : : {
313 : 1249389 : struct nvme_fuzz_request *ctx = cb_arg;
314 : 1249389 : struct nvme_fuzz_qp *qp = ctx->qp;
315 : :
316 : 1249389 : qp->completed_cmd_counter++;
317 [ + + ]: 1249389 : if (spdk_unlikely(cpl->status.sc == SPDK_NVME_SC_SUCCESS)) {
318 [ - + ]: 11848 : fprintf(stderr, "The following %s command (command num %" PRIu64 ") completed successfully\n",
319 [ - + + + ]: 5924 : qp->is_admin ? "Admin" : "I/O", qp->completed_cmd_counter);
320 : 5924 : qp->successful_completed_cmd_counter++;
321 : 5924 : json_dump_nvme_cmd(&ctx->cmd);
322 : :
323 [ - + + + ]: 5924 : if (qp->is_admin) {
324 : 2008 : __sync_bool_compare_and_swap(&g_successful_admin_opcodes[ctx->cmd.opc], false, true);
325 : : } else {
326 : 3916 : __sync_bool_compare_and_swap(&g_successful_io_opcodes[ctx->cmd.opc], false, true);
327 : : }
328 [ - + - + ]: 1243465 : } else if (g_verbose_mode == true) {
329 [ # # ]: 0 : fprintf(stderr, "The following %s command (command num %" PRIu64 ") failed as expected.\n",
330 [ # # # # ]: 0 : qp->is_admin ? "Admin" : "I/O", qp->completed_cmd_counter);
331 : 0 : json_dump_nvme_cmd(&ctx->cmd);
332 : : }
333 : :
334 : 1249389 : qp->timeout_tsc = fuzz_refresh_timeout();
335 [ + + ]: 1249389 : TAILQ_REMOVE(&qp->outstanding_ctx_objs, ctx, link);
336 [ + + ]: 1249389 : TAILQ_INSERT_HEAD(&qp->free_ctx_objs, ctx, link);
337 [ - + ]: 1249389 : assert(qp->num_cmds_outstanding > 0);
338 : 1249389 : qp->num_cmds_outstanding--;
339 : 1249389 : }
340 : :
341 : : static int
342 : 141646 : poll_for_completions(void *arg)
343 : : {
344 : 141646 : struct nvme_fuzz_ns *ns_entry = arg;
345 : 141646 : uint64_t current_ticks = spdk_get_ticks();
346 : : uint64_t *counter;
347 [ - + + - ]: 141646 : if (!ns_entry->io_qp.timed_out) {
348 : 141646 : spdk_nvme_qpair_process_completions(ns_entry->io_qp.qpair, 0);
349 : : /* SAlways have to process admin completions for the purposes of keep alive. */
350 : 141646 : spdk_nvme_ctrlr_process_admin_completions(ns_entry->ctrlr);
351 : : }
352 : :
353 [ - + ]: 141646 : if (g_cmd_array) {
354 [ # # # # ]: 0 : if (g_run_admin_commands) {
355 : 0 : counter = &ns_entry->a_qp.submitted_cmd_counter;
356 : : } else {
357 : 0 : counter = &ns_entry->io_qp.submitted_cmd_counter;
358 : : }
359 : :
360 [ # # ]: 0 : if (*counter >= g_cmd_array_size) {
361 : 0 : g_run = false;
362 : : }
363 : : } else {
364 [ + + ]: 141646 : if (current_ticks > g_runtime_ticks) {
365 : 2 : g_run = false;
366 : : }
367 : : }
368 : :
369 [ - + - - : 141646 : if (ns_entry->a_qp.timeout_tsc < current_ticks && !ns_entry->a_qp.timed_out &&
- - ]
370 [ # # ]: 0 : ns_entry->a_qp.num_cmds_outstanding > 0) {
371 : 0 : handle_timeout(&ns_entry->a_qp, true);
372 : : }
373 : :
374 [ - + - - : 141646 : if (ns_entry->io_qp.timeout_tsc < current_ticks && !ns_entry->io_qp.timed_out &&
- - ]
375 [ # # ]: 0 : ns_entry->io_qp.num_cmds_outstanding > 0) {
376 : 0 : handle_timeout(&ns_entry->io_qp, false);
377 : : }
378 : :
379 : 141646 : submit_ns_cmds(ns_entry);
380 : :
381 [ - + + + ]: 141646 : if (g_run) {
382 : 141644 : return 0;
383 : : }
384 : : /*
385 : : * We either processed all I/O properly and can shut down normally, or we
386 : : * had a qp time out and we need to exit without reducing the values to 0.
387 : : */
388 [ + + ]: 2 : if (ns_entry->io_qp.num_cmds_outstanding == 0 &&
389 [ + - ]: 1 : ns_entry->a_qp.num_cmds_outstanding == 0) {
390 : 1 : goto exit_handler;
391 [ - + - + : 1 : } else if (ns_entry->io_qp.timed_out && (!g_run_admin_commands || ns_entry->a_qp.timed_out)) {
- - - - -
- - - ]
392 : 0 : goto exit_handler;
393 : : } else {
394 : 1 : return 0;
395 : : }
396 : :
397 : 1 : exit_handler:
398 : 1 : spdk_poller_unregister(&ns_entry->req_poller);
399 : 1 : __sync_sub_and_fetch(&g_num_active_threads, 1);
400 : 1 : spdk_thread_exit(ns_entry->thread);
401 : 1 : return 0;
402 : : }
403 : :
404 : : static void
405 : 1250368 : prep_nvme_cmd(struct nvme_fuzz_ns *ns_entry, struct nvme_fuzz_qp *qp, struct nvme_fuzz_request *ctx)
406 : : {
407 [ - + ]: 1250368 : if (g_cmd_array) {
408 [ # # # # ]: 0 : memcpy(&ctx->cmd, &g_cmd_array[qp->submitted_cmd_counter], sizeof(ctx->cmd));
409 : : } else {
410 : 1250368 : fuzz_fill_random_bytes((char *)&ctx->cmd, sizeof(ctx->cmd), &qp->random_seed);
411 : :
412 [ - + + - ]: 1250368 : if (g_valid_ns_only) {
413 : 1250368 : ctx->cmd.nsid = ns_entry->nsid;
414 : : }
415 : : }
416 : :
417 : : /* Fuzzing test not support sequential FUSE commands. */
418 : 1250368 : ctx->cmd.fuse = 0;
419 : 1250368 : }
420 : :
421 : : static int
422 : 283290 : submit_qp_cmds(struct nvme_fuzz_ns *ns, struct nvme_fuzz_qp *qp)
423 : : {
424 : : struct nvme_fuzz_request *ctx;
425 : : int rc;
426 : :
427 [ - + - + ]: 283290 : if (qp->timed_out) {
428 : 0 : return 0;
429 : : }
430 : : /* If we are reading from an array, we need to stop after the last one. */
431 [ + - + - ]: 1532679 : while ((qp->submitted_cmd_counter < g_cmd_array_size || g_cmd_array_size == 0) &&
432 [ + + ]: 1532679 : !TAILQ_EMPTY(&qp->free_ctx_objs)) {
433 : 1249389 : ctx = TAILQ_FIRST(&qp->free_ctx_objs);
434 : : do {
435 : 1250368 : prep_nvme_cmd(ns, qp, ctx);
436 [ - + + + : 1250368 : } while (qp->is_admin && ctx->cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST);
+ + ]
437 : :
438 [ + + ]: 1249389 : TAILQ_REMOVE(&qp->free_ctx_objs, ctx, link);
439 [ + + ]: 1249389 : TAILQ_INSERT_HEAD(&qp->outstanding_ctx_objs, ctx, link);
440 : 1249389 : qp->num_cmds_outstanding++;
441 : 1249389 : qp->submitted_cmd_counter++;
442 [ - + + + ]: 1249389 : if (qp->is_admin) {
443 : 248354 : rc = spdk_nvme_ctrlr_cmd_admin_raw(ns->ctrlr, &ctx->cmd, NULL, 0, nvme_fuzz_cpl_cb, ctx);
444 : : } else {
445 : 1001035 : rc = spdk_nvme_ctrlr_cmd_io_raw(ns->ctrlr, qp->qpair, &ctx->cmd, NULL, 0, nvme_fuzz_cpl_cb, ctx);
446 : : }
447 [ - + ]: 1249389 : if (rc) {
448 : 0 : return rc;
449 : : }
450 : : }
451 : 283290 : return 0;
452 : : }
453 : :
454 : : static void
455 : 141647 : submit_ns_cmds(struct nvme_fuzz_ns *ns_entry)
456 : : {
457 : : int rc;
458 : :
459 [ - + + + ]: 141647 : if (!g_run) {
460 : 2 : return;
461 : : }
462 : :
463 [ - + + - ]: 141645 : if (g_run_admin_commands) {
464 : 141645 : rc = submit_qp_cmds(ns_entry, &ns_entry->a_qp);
465 [ - + ]: 141645 : if (rc) {
466 : 0 : goto err_exit;
467 : : }
468 : : }
469 : :
470 [ - + - - : 141645 : if (g_cmd_array == NULL || !g_run_admin_commands) {
- - ]
471 : 141645 : rc = submit_qp_cmds(ns_entry, &ns_entry->io_qp);
472 : : }
473 : 0 : err_exit:
474 [ - + ]: 141645 : if (rc) {
475 : : /*
476 : : * I see the prospect of having a broken qpair on one ns as interesting
477 : : * enough to recommend stopping the application.
478 : : */
479 [ # # # # ]: 0 : fprintf(stderr, "Unable to submit command with rc %d\n", rc);
480 : 0 : g_run = false;
481 : : }
482 : : }
483 : :
484 : : static void
485 : 1 : free_namespaces(void)
486 : : {
487 : : struct nvme_fuzz_ns *ns, *tmp;
488 : :
489 [ + + ]: 2 : TAILQ_FOREACH_SAFE(ns, &g_ns_list, tailq, tmp) {
490 [ - + ]: 1 : printf("NS: %p I/O qp, Total commands completed: %" PRIu64 ", total successful commands: %" PRIu64
491 : : ", random_seed: %u\n",
492 : : ns->ns,
493 : : ns->io_qp.completed_cmd_counter, ns->io_qp.successful_completed_cmd_counter, ns->io_qp.random_seed);
494 [ - + ]: 1 : printf("NS: %p admin qp, Total commands completed: %" PRIu64 ", total successful commands: %" PRIu64
495 : : ", random_seed: %u\n",
496 : : ns->ns,
497 : : ns->a_qp.completed_cmd_counter, ns->a_qp.successful_completed_cmd_counter, ns->a_qp.random_seed);
498 : :
499 [ - + ]: 1 : TAILQ_REMOVE(&g_ns_list, ns, tailq);
500 [ + - ]: 1 : if (ns->io_qp.qpair) {
501 : 1 : spdk_nvme_ctrlr_free_io_qpair(ns->io_qp.qpair);
502 : : }
503 [ + - ]: 1 : if (ns->io_qp.req_ctx) {
504 : 1 : free(ns->io_qp.req_ctx);
505 : : }
506 [ + - ]: 1 : if (ns->a_qp.req_ctx) {
507 : 1 : free(ns->a_qp.req_ctx);
508 : : }
509 : 1 : free(ns);
510 : : }
511 : 1 : }
512 : :
513 : : static void
514 : 1 : free_controllers(void)
515 : : {
516 : : struct nvme_fuzz_ctrlr *ctrlr, *tmp;
517 : 1 : struct spdk_nvme_detach_ctx *detach_ctx = NULL;
518 : :
519 [ + + ]: 2 : TAILQ_FOREACH_SAFE(ctrlr, &g_ctrlr_list, tailq, tmp) {
520 [ - + ]: 1 : TAILQ_REMOVE(&g_ctrlr_list, ctrlr, tailq);
521 : 1 : spdk_nvme_detach_async(ctrlr->ctrlr, &detach_ctx);
522 : 1 : free(ctrlr);
523 : : }
524 : :
525 [ + - ]: 1 : if (detach_ctx) {
526 : 1 : spdk_nvme_detach_poll(detach_ctx);
527 : : }
528 : 1 : }
529 : :
530 : : static void
531 : 1 : free_trids(void)
532 : : {
533 : : struct nvme_fuzz_trid *trid, *tmp;
534 : :
535 [ + + ]: 2 : TAILQ_FOREACH_SAFE(trid, &g_trid_list, tailq, tmp) {
536 [ - + ]: 1 : TAILQ_REMOVE(&g_trid_list, trid, tailq);
537 : 1 : free(trid);
538 : : }
539 : 1 : }
540 : :
541 : : static void
542 : 1 : register_ns(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns, uint32_t nsid)
543 : : {
544 : : struct nvme_fuzz_ns *ns_entry;
545 : :
546 : 1 : ns_entry = calloc(1, sizeof(struct nvme_fuzz_ns));
547 [ - + ]: 1 : if (ns_entry == NULL) {
548 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate an entry for a namespace\n");
549 : 0 : return;
550 : : }
551 : :
552 : 1 : ns_entry->ns = ns;
553 : 1 : ns_entry->ctrlr = ctrlr;
554 : 1 : ns_entry->nsid = nsid;
555 : :
556 : 1 : TAILQ_INIT(&ns_entry->io_qp.free_ctx_objs);
557 : 1 : TAILQ_INIT(&ns_entry->io_qp.outstanding_ctx_objs);
558 : 1 : TAILQ_INIT(&ns_entry->a_qp.free_ctx_objs);
559 : 1 : TAILQ_INIT(&ns_entry->a_qp.outstanding_ctx_objs);
560 : 1 : TAILQ_INSERT_TAIL(&g_ns_list, ns_entry, tailq);
561 : : }
562 : :
563 : : static void
564 : 1 : register_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
565 : : {
566 : : struct nvme_fuzz_ctrlr *ctrlr_entry;
567 : : uint32_t nsid;
568 : : struct spdk_nvme_ns *ns;
569 : :
570 : 1 : ctrlr_entry = calloc(1, sizeof(struct nvme_fuzz_ctrlr));
571 [ - + ]: 1 : if (ctrlr_entry == NULL) {
572 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate an entry for a controller\n");
573 : 0 : return;
574 : : }
575 : :
576 : 1 : ctrlr_entry->ctrlr = ctrlr;
577 : 1 : TAILQ_INSERT_TAIL(&g_ctrlr_list, ctrlr_entry, tailq);
578 : :
579 [ + + ]: 2 : for (nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr); nsid != 0;
580 : 1 : nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr, nsid)) {
581 : 1 : ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
582 [ - + ]: 1 : if (ns == NULL) {
583 : 0 : continue;
584 : : }
585 : 1 : register_ns(ctrlr, ns, nsid);
586 : : }
587 : : }
588 : :
589 : : static int
590 : 2 : prep_qpair(struct nvme_fuzz_ns *ns, struct nvme_fuzz_qp *qp, uint32_t max_qdepth)
591 : : {
592 : : uint32_t i;
593 : :
594 : : /* ensure that each qpair gets a unique random seed for maximum command dispersion. */
595 : :
596 [ + - ]: 2 : if (g_seed_value != 0) {
597 : 2 : qp->random_seed = g_seed_value;
598 : : } else {
599 : : /* Take the low 32 bits of spdk_get_ticks. This should be more granular than time(). */
600 : 0 : qp->random_seed = spdk_get_ticks();
601 : : }
602 : :
603 : 2 : qp->timeout_tsc = fuzz_refresh_timeout();
604 : :
605 : 2 : qp->req_ctx = calloc(max_qdepth, sizeof(struct nvme_fuzz_request));
606 [ - + ]: 2 : if (qp->req_ctx == NULL) {
607 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate I/O contexts for I/O qpair.\n");
608 : 0 : return -1;
609 : : }
610 : :
611 [ + + ]: 146 : for (i = 0; i < max_qdepth; i++) {
612 : 144 : qp->req_ctx[i].qp = qp;
613 [ + + ]: 144 : TAILQ_INSERT_HEAD(&qp->free_ctx_objs, &qp->req_ctx[i], link);
614 : : }
615 : :
616 : 2 : return 0;
617 : : }
618 : :
619 : : static int
620 : 1 : prepare_qpairs(void)
621 : : {
622 : 0 : struct spdk_nvme_io_qpair_opts opts;
623 : : struct nvme_fuzz_ns *ns_entry;
624 : :
625 [ + + ]: 2 : TAILQ_FOREACH(ns_entry, &g_ns_list, tailq) {
626 : 1 : spdk_nvme_ctrlr_get_default_io_qpair_opts(ns_entry->ctrlr, &opts, sizeof(opts));
627 : 1 : ns_entry->io_qp.qpair = spdk_nvme_ctrlr_alloc_io_qpair(ns_entry->ctrlr, &opts, sizeof(opts));
628 [ - + ]: 1 : if (ns_entry->io_qp.qpair == NULL) {
629 [ # # # # ]: 0 : fprintf(stderr, "Unable to create a qpair for a namespace\n");
630 : 0 : return -1;
631 : : }
632 : :
633 : 1 : ns_entry->io_qp.is_admin = false;
634 [ - + ]: 1 : if (prep_qpair(ns_entry, &ns_entry->io_qp, g_io_depth) != 0) {
635 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate request contexts for I/O qpair.\n");
636 : 0 : return -1;
637 : : }
638 : :
639 [ - + + - ]: 1 : if (g_run_admin_commands) {
640 : 1 : ns_entry->a_qp.is_admin = true;
641 [ - + ]: 1 : if (prep_qpair(ns_entry, &ns_entry->a_qp, g_admin_depth) != 0) {
642 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate request contexts for admin qpair.\n");
643 : 0 : return -1;
644 : : }
645 : : }
646 : : }
647 : 1 : return 0;
648 : : }
649 : :
650 : : static void
651 : 1 : start_ns_poller(void *ctx)
652 : : {
653 : 1 : struct nvme_fuzz_ns *ns_entry = ctx;
654 : :
655 : 1 : ns_entry->req_poller = SPDK_POLLER_REGISTER(poll_for_completions, ns_entry, 0);
656 : 1 : submit_ns_cmds(ns_entry);
657 : 1 : }
658 : :
659 : : static int
660 : 30 : check_app_completion(void *ctx)
661 : : {
662 : :
663 [ + + ]: 30 : if (g_num_active_threads <= 0) {
664 : 1 : spdk_poller_unregister(&g_app_completion_poller);
665 [ - + ]: 1 : if (g_cmd_array) {
666 : 0 : free(g_cmd_array);
667 : : }
668 [ - + ]: 1 : printf("Fuzzing completed. Shutting down the fuzz application\n\n");
669 [ - + ]: 1 : printf("Dumping successful admin opcodes:\n");
670 : 1 : report_successful_opcodes(g_successful_admin_opcodes, UNIQUE_OPCODES);
671 [ - + ]: 1 : printf("Dumping successful io opcodes:\n");
672 : 1 : report_successful_opcodes(g_successful_io_opcodes, UNIQUE_OPCODES);
673 : 1 : free_namespaces();
674 : 1 : free_controllers();
675 : 1 : free_trids();
676 : 1 : spdk_app_stop(0);
677 : : }
678 : 30 : return 0;
679 : : }
680 : :
681 : : static void
682 : 1 : begin_fuzz(void *ctx)
683 : : {
684 : : struct nvme_fuzz_ns *ns_entry;
685 : : struct nvme_fuzz_trid *trid;
686 : : struct spdk_nvme_ctrlr *ctrlr;
687 : : int rc;
688 : :
689 [ - + + - : 1 : if (g_check_iommu && !spdk_iommu_is_enabled()) {
- + ]
690 : : /* Don't set rc to an error code here. We don't want to fail an automated test based on this. */
691 [ # # # # ]: 0 : fprintf(stderr, "The IOMMU must be enabled to run this program to avoid unsafe memory accesses.\n");
692 : 0 : rc = 0;
693 : 0 : goto out;
694 : : }
695 : :
696 [ + + ]: 2 : TAILQ_FOREACH(trid, &g_trid_list, tailq) {
697 : 1 : ctrlr = spdk_nvme_connect(&trid->trid, NULL, 0);
698 [ - + ]: 1 : if (ctrlr == NULL) {
699 [ # # ]: 0 : fprintf(stderr, "spdk_nvme_connect() failed for transport address '%s'\n",
700 [ # # ]: 0 : trid->trid.traddr);
701 : 0 : rc = -1;
702 : 0 : goto out;
703 : : }
704 : 1 : register_ctrlr(ctrlr);
705 : : }
706 : :
707 [ - + ]: 1 : if (TAILQ_EMPTY(&g_ns_list)) {
708 [ # # # # ]: 0 : fprintf(stderr, "No valid NVMe Namespaces to fuzz\n");
709 : 0 : rc = -EINVAL;
710 : 0 : goto out;
711 : : }
712 : :
713 : 1 : rc = prepare_qpairs();
714 : :
715 [ - + ]: 1 : if (rc < 0) {
716 [ # # # # ]: 0 : fprintf(stderr, "Unable to prepare the qpairs\n");
717 : 0 : goto out;
718 : : }
719 : :
720 : 1 : g_runtime_ticks = spdk_get_ticks() + g_runtime * spdk_get_ticks_hz();
721 : :
722 : : /* Assigning all of the threads and then starting them makes cleanup easier. */
723 [ + + ]: 2 : TAILQ_FOREACH(ns_entry, &g_ns_list, tailq) {
724 : 1 : ns_entry->thread = spdk_thread_create(NULL, NULL);
725 [ - + ]: 1 : if (ns_entry->thread == NULL) {
726 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate thread for namespace.\n");
727 : 0 : goto out;
728 : : }
729 : : }
730 : :
731 [ + + ]: 2 : TAILQ_FOREACH(ns_entry, &g_ns_list, tailq) {
732 : 1 : spdk_thread_send_msg(ns_entry->thread, start_ns_poller, ns_entry);
733 : 1 : __sync_add_and_fetch(&g_num_active_threads, 1);
734 : : }
735 : :
736 : 1 : g_app_completion_poller = SPDK_POLLER_REGISTER(check_app_completion, NULL, 1000000);
737 : 1 : return;
738 : 0 : out:
739 [ # # ]: 0 : printf("Shutting down the fuzz application\n");
740 : 0 : free_namespaces();
741 : 0 : free_controllers();
742 : 0 : free_trids();
743 : 0 : spdk_app_stop(rc);
744 : : }
745 : :
746 : : static void
747 : 0 : nvme_fuzz_usage(void)
748 : : {
749 [ # # # # ]: 0 : fprintf(stderr, " -a Perform admin commands. if -j is specified, \
750 : : only admin commands will run. Otherwise they will be run in tandem with I/O commands.\n");
751 [ # # # # ]: 0 : fprintf(stderr, " -F Transport ID for subsystem that should be fuzzed.\n");
752 [ # # # # ]: 0 : fprintf(stderr,
753 : : " -j <path> Path to a json file containing named objects of type spdk_nvme_cmd. If this option is specified, -t will be ignored.\n");
754 [ # # # # ]: 0 : fprintf(stderr, " -N Target only valid namespace with commands. \
755 : : This helps dig deeper into other errors besides invalid namespace.\n");
756 [ # # # # ]: 0 : fprintf(stderr, " -S <integer> Seed value for test.\n");
757 [ # # # # ]: 0 : fprintf(stderr,
758 : : " -t <integer> Time in seconds to run the fuzz test. Only valid if -j is not specified.\n");
759 [ # # # # ]: 0 : fprintf(stderr, " -U Do not check if IOMMU is enabled.\n");
760 [ # # # # ]: 0 : fprintf(stderr, " -V Enable logging of each submitted command.\n");
761 : 0 : }
762 : :
763 : : static int
764 : 5 : nvme_fuzz_parse(int ch, char *arg)
765 : : {
766 : : struct nvme_fuzz_trid *trid;
767 : : int64_t error_test;
768 : : int rc;
769 : :
770 [ + + - + : 5 : switch (ch) {
+ + - -
- ]
771 : 1 : case 'a':
772 : 1 : g_run_admin_commands = true;
773 : 1 : break;
774 : 1 : case 'F':
775 : 1 : trid = malloc(sizeof(*trid));
776 [ - + ]: 1 : if (!trid) {
777 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate memory for transport ID\n");
778 : 0 : return -1;
779 : : }
780 : 1 : rc = spdk_nvme_transport_id_parse(&trid->trid, optarg);
781 [ - + ]: 1 : if (rc < 0) {
782 [ # # # # ]: 0 : fprintf(stderr, "failed to parse transport ID: %s\n", optarg);
783 : 0 : free(trid);
784 : 0 : return -1;
785 : : }
786 : 1 : TAILQ_INSERT_TAIL(&g_trid_list, trid, tailq);
787 : 1 : break;
788 : 0 : case 'j':
789 : 0 : g_json_file = optarg;
790 : 0 : break;
791 : 1 : case 'N':
792 : 1 : g_valid_ns_only = true;
793 : 1 : break;
794 : 1 : case 'S':
795 : 1 : error_test = spdk_strtol(arg, 10);
796 [ - + ]: 1 : if (error_test < 0) {
797 [ # # # # ]: 0 : fprintf(stderr, "Invalid value supplied for the random seed.\n");
798 : 0 : return -1;
799 : : } else {
800 : 1 : g_seed_value = error_test;
801 : : }
802 : 1 : break;
803 : 1 : case 't':
804 : 1 : g_runtime = spdk_strtol(optarg, 10);
805 [ + - - + ]: 1 : if (g_runtime < 0 || g_runtime > MAX_RUNTIME_S) {
806 [ # # # # ]: 0 : fprintf(stderr, "You must supply a positive runtime value less than 86401.\n");
807 : 0 : return -1;
808 : : }
809 : 1 : break;
810 : 0 : case 'U':
811 : 0 : g_check_iommu = false;
812 : 0 : break;
813 : 0 : case 'V':
814 : 0 : g_verbose_mode = true;
815 : 0 : break;
816 : 0 : case '?':
817 : : default:
818 : 0 : return -EINVAL;
819 : : }
820 : 5 : return 0;
821 : : }
822 : :
823 : : int
824 : 1 : main(int argc, char **argv)
825 : : {
826 : 1 : struct spdk_app_opts opts = {};
827 : : int rc;
828 : :
829 : 1 : spdk_app_opts_init(&opts, sizeof(opts));
830 : 1 : opts.name = "nvme_fuzz";
831 : 1 : opts.rpc_addr = NULL;
832 : :
833 : 1 : g_runtime = DEFAULT_RUNTIME;
834 : 1 : g_run = true;
835 : :
836 : 1 : if ((rc = spdk_app_parse_args(argc, argv, &opts, "aF:j:NS:t:UV", NULL, nvme_fuzz_parse,
837 [ - + ]: 1 : nvme_fuzz_usage) != SPDK_APP_PARSE_ARGS_SUCCESS)) {
838 : 0 : return rc;
839 : : }
840 : :
841 [ - + ]: 1 : if (g_json_file != NULL) {
842 : 0 : g_cmd_array_size = fuzz_parse_args_into_array(g_json_file, (void **)&g_cmd_array,
843 : : sizeof(struct spdk_nvme_cmd), g_nvme_cmd_json_name, parse_nvme_cmd_obj);
844 [ # # ]: 0 : if (g_cmd_array_size == 0) {
845 [ # # # # ]: 0 : fprintf(stderr, "The provided json file did not contain any valid commands. Exiting.");
846 : 0 : return -EINVAL;
847 : : }
848 : : }
849 : :
850 : 1 : rc = spdk_app_start(&opts, begin_fuzz, NULL);
851 : :
852 : 1 : spdk_app_fini();
853 : 1 : return rc;
854 : : }
|