Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2019 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/env.h"
8 : : #include "spdk/json.h"
9 : : #include "spdk/event.h"
10 : : #include "spdk/likely.h"
11 : : #include "spdk/util.h"
12 : : #include "spdk/string.h"
13 : : #include "spdk_internal/virtio.h"
14 : : #include "spdk_internal/vhost_user.h"
15 : :
16 : : #include "fuzz_common.h"
17 : : #include "vhost_fuzz.h"
18 : :
19 : : #include <linux/virtio_blk.h>
20 : : #include <linux/virtio_scsi.h>
21 : :
22 : : /* Features desired/implemented by virtio blk. */
23 : : #define VIRTIO_BLK_DEV_SUPPORTED_FEATURES \
24 : : (1ULL << VIRTIO_BLK_F_BLK_SIZE | \
25 : : 1ULL << VIRTIO_BLK_F_TOPOLOGY | \
26 : : 1ULL << VIRTIO_BLK_F_MQ | \
27 : : 1ULL << VIRTIO_BLK_F_RO | \
28 : : 1ULL << VIRTIO_BLK_F_DISCARD | \
29 : : 1ULL << VIRTIO_RING_F_EVENT_IDX | \
30 : : 1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
31 : :
32 : : /* Features desired/implemented by virtio scsi. */
33 : : #define VIRTIO_SCSI_DEV_SUPPORTED_FEATURES \
34 : : (1ULL << VIRTIO_SCSI_F_INOUT | \
35 : : 1ULL << VIRTIO_SCSI_F_HOTPLUG | \
36 : : 1ULL << VIRTIO_RING_F_EVENT_IDX | \
37 : : 1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
38 : :
39 : : #define VIRTIO_DEV_FIXED_QUEUES 2
40 : : #define VIRTIO_SCSI_CONTROLQ 0
41 : : #define VIRTIO_SCSI_EVENTQ 1
42 : : #define VIRTIO_REQUESTQ 2
43 : : #define FUZZ_MAX_QUEUES 3
44 : :
45 : : #define FUZZ_QUEUE_DEPTH 128
46 : :
47 : : #define BLK_IO_NAME "vhost_blk_cmd"
48 : : #define SCSI_IO_NAME "vhost_scsi_cmd"
49 : : #define SCSI_MGMT_NAME "vhost_scsi_mgmt_cmd"
50 : :
51 : : struct fuzz_vhost_iov_ctx {
52 : : struct iovec iov_req;
53 : : struct iovec iov_data;
54 : : struct iovec iov_resp;
55 : : };
56 : :
57 : : struct fuzz_vhost_io_ctx {
58 : : struct fuzz_vhost_iov_ctx iovs;
59 : : union {
60 : : struct virtio_blk_outhdr blk_req;
61 : : struct virtio_scsi_cmd_req scsi_req;
62 : : struct virtio_scsi_ctrl_tmf_req scsi_tmf_req;
63 : : } req;
64 : : union {
65 : : uint8_t blk_resp;
66 : : struct virtio_scsi_cmd_resp scsi_resp;
67 : : union {
68 : : struct virtio_scsi_ctrl_tmf_resp scsi_tmf_resp;
69 : : struct virtio_scsi_ctrl_an_resp an_resp;
70 : : } scsi_tmf_resp;
71 : : } resp;
72 : :
73 : : TAILQ_ENTRY(fuzz_vhost_io_ctx) link;
74 : : };
75 : :
76 : : struct fuzz_vhost_dev_ctx {
77 : : struct virtio_dev virtio_dev;
78 : : struct spdk_thread *thread;
79 : : struct spdk_poller *poller;
80 : :
81 : : struct fuzz_vhost_io_ctx *io_ctx_array;
82 : : TAILQ_HEAD(, fuzz_vhost_io_ctx) free_io_ctx;
83 : : TAILQ_HEAD(, fuzz_vhost_io_ctx) outstanding_io_ctx;
84 : :
85 : : unsigned int random_seed;
86 : :
87 : : uint64_t submitted_io;
88 : : uint64_t completed_io;
89 : : uint64_t successful_io;
90 : : uint64_t timeout_tsc;
91 : :
92 : : bool socket_is_blk;
93 : : bool test_scsi_tmf;
94 : : bool valid_lun;
95 : : bool use_bogus_buffer;
96 : : bool use_valid_buffer;
97 : : bool timed_out;
98 : :
99 : : TAILQ_ENTRY(fuzz_vhost_dev_ctx) link;
100 : : };
101 : :
102 : : /* Global run state */
103 : : uint64_t g_runtime_ticks;
104 : : int g_runtime;
105 : : int g_num_active_threads;
106 : : bool g_run = true;
107 : : bool g_verbose_mode = false;
108 : :
109 : : /* Global resources */
110 : : TAILQ_HEAD(, fuzz_vhost_dev_ctx) g_dev_list = TAILQ_HEAD_INITIALIZER(g_dev_list);
111 : : struct spdk_poller *g_run_poller;
112 : : void *g_valid_buffer;
113 : : unsigned int g_random_seed;
114 : :
115 : :
116 : : /* Global parameters and resources for parsed commands */
117 : : bool g_keep_iov_pointers = false;
118 : : char *g_json_file = NULL;
119 : : struct fuzz_vhost_io_ctx *g_blk_cmd_array = NULL;
120 : : struct fuzz_vhost_io_ctx *g_scsi_cmd_array = NULL;
121 : : struct fuzz_vhost_io_ctx *g_scsi_mgmt_cmd_array = NULL;
122 : :
123 : : size_t g_blk_cmd_array_size;
124 : : size_t g_scsi_cmd_array_size;
125 : : size_t g_scsi_mgmt_cmd_array_size;
126 : :
127 : : static void
128 : 2 : cleanup(void)
129 : : {
130 : : struct fuzz_vhost_dev_ctx *dev_ctx, *tmp;
131 [ - + ]: 2 : printf("Fuzzing completed.\n");
132 [ + + ]: 8 : TAILQ_FOREACH_SAFE(dev_ctx, &g_dev_list, link, tmp) {
133 [ - + ]: 6 : printf("device %p stats: Completed I/O: %lu, Successful I/O: %lu\n", dev_ctx,
134 : : dev_ctx->completed_io, dev_ctx->successful_io);
135 : 6 : virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_REQUESTQ);
136 [ - + + + ]: 6 : if (!dev_ctx->socket_is_blk) {
137 : 4 : virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_EVENTQ);
138 : 4 : virtio_dev_release_queue(&dev_ctx->virtio_dev, VIRTIO_SCSI_CONTROLQ);
139 : : }
140 : 6 : virtio_dev_stop(&dev_ctx->virtio_dev);
141 : 6 : virtio_dev_destruct(&dev_ctx->virtio_dev);
142 [ + - ]: 6 : if (dev_ctx->io_ctx_array) {
143 : 6 : spdk_free(dev_ctx->io_ctx_array);
144 : : }
145 : 6 : free(dev_ctx);
146 : : }
147 : :
148 : 2 : spdk_free(g_valid_buffer);
149 : :
150 [ + + ]: 2 : if (g_blk_cmd_array) {
151 : 1 : free(g_blk_cmd_array);
152 : : }
153 [ + + ]: 2 : if (g_scsi_cmd_array) {
154 : 1 : free(g_scsi_cmd_array);
155 : : }
156 [ + + ]: 2 : if (g_scsi_mgmt_cmd_array) {
157 : 1 : free(g_scsi_mgmt_cmd_array);
158 : : }
159 : 2 : }
160 : :
161 : : /* Get a memory address that is random and not located in our hugepage memory. */
162 : : static void *
163 : 0 : get_invalid_mem_address(uint64_t length)
164 : : {
165 : 0 : uint64_t chosen_address = 0x0;
166 : :
167 : : while (true) {
168 : 0 : chosen_address = rand();
169 : 0 : chosen_address = (chosen_address << 32) | rand();
170 [ # # ]: 0 : if (spdk_vtophys((void *)chosen_address, &length) == SPDK_VTOPHYS_ERROR) {
171 : 0 : return (void *)chosen_address;
172 : : }
173 : : }
174 : : return NULL;
175 : : }
176 : :
177 : : /* dev initialization code begin. */
178 : : static int
179 : 6 : virtio_dev_init(struct virtio_dev *vdev, const char *socket_path, uint64_t flags,
180 : : uint16_t max_queues)
181 : : {
182 : : int rc;
183 : :
184 : 6 : rc = virtio_user_dev_init(vdev, "dev_ctx", socket_path, 1024);
185 [ - + ]: 6 : if (rc != 0) {
186 [ # # # # ]: 0 : fprintf(stderr, "Failed to initialize virtual bdev\n");
187 : 0 : return rc;
188 : : }
189 : :
190 : 6 : rc = virtio_dev_reset(vdev, flags);
191 [ - + ]: 6 : if (rc != 0) {
192 : 0 : return rc;
193 : : }
194 : :
195 : 6 : rc = virtio_dev_start(vdev, max_queues, VIRTIO_DEV_FIXED_QUEUES);
196 [ - + ]: 6 : if (rc != 0) {
197 : 0 : return rc;
198 : : }
199 : :
200 : 6 : rc = virtio_dev_acquire_queue(vdev, VIRTIO_REQUESTQ);
201 [ - + ]: 6 : if (rc < 0) {
202 [ # # # # ]: 0 : fprintf(stderr, "Couldn't get an unused queue for the io_channel.\n");
203 : 0 : virtio_dev_stop(vdev);
204 : 0 : return rc;
205 : : }
206 : 6 : return 0;
207 : : }
208 : :
209 : : static int
210 : 2 : blk_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
211 : : {
212 : 2 : return virtio_dev_init(vdev, socket_path, VIRTIO_BLK_DEV_SUPPORTED_FEATURES, max_queues);
213 : : }
214 : :
215 : : static int
216 : 4 : scsi_dev_init(struct virtio_dev *vdev, const char *socket_path, uint16_t max_queues)
217 : : {
218 : : int rc;
219 : :
220 : 4 : rc = virtio_dev_init(vdev, socket_path, VIRTIO_SCSI_DEV_SUPPORTED_FEATURES, max_queues);
221 [ - + ]: 4 : if (rc != 0) {
222 : 0 : return rc;
223 : : }
224 : :
225 : 4 : rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_CONTROLQ);
226 [ - + ]: 4 : if (rc != 0) {
227 : 0 : SPDK_ERRLOG("Failed to acquire the controlq.\n");
228 : 0 : return rc;
229 : : }
230 : :
231 : 4 : rc = virtio_dev_acquire_queue(vdev, VIRTIO_SCSI_EVENTQ);
232 [ - + ]: 4 : if (rc != 0) {
233 : 0 : SPDK_ERRLOG("Failed to acquire the eventq.\n");
234 : 0 : virtio_dev_release_queue(vdev, VIRTIO_SCSI_CONTROLQ);
235 : 0 : return rc;
236 : : }
237 : :
238 : 4 : return 0;
239 : : }
240 : :
241 : : int
242 : 6 : fuzz_vhost_dev_init(const char *socket_path, bool is_blk_dev, bool use_bogus_buffer,
243 : : bool use_valid_buffer, bool valid_lun, bool test_scsi_tmf)
244 : : {
245 : : struct fuzz_vhost_dev_ctx *dev_ctx;
246 : 6 : int rc = 0, i;
247 : :
248 : 6 : dev_ctx = calloc(1, sizeof(*dev_ctx));
249 [ - + ]: 6 : if (dev_ctx == NULL) {
250 : 0 : return -ENOMEM;
251 : : }
252 : :
253 : 6 : dev_ctx->socket_is_blk = is_blk_dev;
254 : 6 : dev_ctx->use_bogus_buffer = use_bogus_buffer;
255 : 6 : dev_ctx->use_valid_buffer = use_valid_buffer;
256 : 6 : dev_ctx->valid_lun = valid_lun;
257 : 6 : dev_ctx->test_scsi_tmf = test_scsi_tmf;
258 : :
259 : 6 : TAILQ_INIT(&dev_ctx->free_io_ctx);
260 : 6 : TAILQ_INIT(&dev_ctx->outstanding_io_ctx);
261 : :
262 : : assert(sizeof(*dev_ctx->io_ctx_array) <= UINT64_MAX / FUZZ_QUEUE_DEPTH);
263 : 6 : dev_ctx->io_ctx_array = spdk_malloc(sizeof(*dev_ctx->io_ctx_array) * FUZZ_QUEUE_DEPTH, 0x0, NULL,
264 : : SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
265 [ - + ]: 6 : if (dev_ctx->io_ctx_array == NULL) {
266 : 0 : free(dev_ctx);
267 : 0 : return -ENOMEM;
268 : : }
269 : :
270 [ + + ]: 774 : for (i = 0; i < FUZZ_QUEUE_DEPTH; i++) {
271 [ + + ]: 768 : TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, &dev_ctx->io_ctx_array[i], link);
272 : : }
273 : :
274 : 6 : dev_ctx->thread = spdk_thread_create(NULL, NULL);
275 [ - + ]: 6 : if (dev_ctx->thread == NULL) {
276 [ # # # # ]: 0 : fprintf(stderr, "Unable to allocate a thread for a fuzz device.\n");
277 : 0 : rc = -ENOMEM;
278 : 0 : goto error_out;
279 : : }
280 : :
281 [ + + ]: 6 : if (is_blk_dev) {
282 : 2 : rc = blk_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
283 : : } else {
284 : 4 : rc = scsi_dev_init(&dev_ctx->virtio_dev, socket_path, FUZZ_MAX_QUEUES);
285 : : }
286 : :
287 [ - + ]: 6 : if (rc) {
288 [ # # # # ]: 0 : fprintf(stderr, "Unable to prepare the device to perform I/O.\n");
289 : 0 : goto error_out;
290 : : }
291 : :
292 : 6 : TAILQ_INSERT_TAIL(&g_dev_list, dev_ctx, link);
293 : 6 : return 0;
294 : :
295 : 0 : error_out:
296 : 0 : spdk_free(dev_ctx->io_ctx_array);
297 : 0 : free(dev_ctx);
298 : 0 : return rc;
299 : : }
300 : : /* dev initialization code end */
301 : :
302 : : /* data dumping functions begin */
303 : : static int
304 : 637 : dump_virtio_cmd(void *ctx, const void *data, size_t size)
305 : : {
306 [ - + - + ]: 637 : fprintf(stderr, "%s\n", (const char *)data);
307 : 637 : return 0;
308 : : }
309 : :
310 : : static void
311 : 1 : print_blk_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
312 : : {
313 : 1 : spdk_json_write_named_uint32(w, "type", io_ctx->req.blk_req.type);
314 : 1 : spdk_json_write_named_uint32(w, "ioprio", io_ctx->req.blk_req.ioprio);
315 : 1 : spdk_json_write_named_uint64(w, "sector", io_ctx->req.blk_req.sector);
316 : 1 : }
317 : :
318 : : static void
319 : 41 : print_scsi_tmf_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
320 : : {
321 : : char *lun_data;
322 : :
323 : 41 : lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_tmf_req.lun,
324 : : sizeof(io_ctx->req.scsi_tmf_req.lun));
325 : :
326 : 41 : spdk_json_write_named_uint32(w, "type", io_ctx->req.scsi_tmf_req.type);
327 : 41 : spdk_json_write_named_uint32(w, "subtype", io_ctx->req.scsi_tmf_req.subtype);
328 : 41 : spdk_json_write_named_string(w, "lun", lun_data);
329 : 41 : spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_tmf_req.tag);
330 : :
331 : 41 : free(lun_data);
332 : 41 : }
333 : :
334 : : static void
335 : 595 : print_scsi_io_data(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
336 : : {
337 : : char *lun_data;
338 : : char *cdb_data;
339 : :
340 : 595 : lun_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.lun,
341 : : sizeof(io_ctx->req.scsi_req.lun));
342 : 595 : cdb_data = fuzz_get_value_base_64_buffer(io_ctx->req.scsi_req.cdb,
343 : : sizeof(io_ctx->req.scsi_req.cdb));
344 : :
345 : 595 : spdk_json_write_named_string(w, "lun", lun_data);
346 : 595 : spdk_json_write_named_uint64(w, "tag", io_ctx->req.scsi_req.tag);
347 : 595 : spdk_json_write_named_uint32(w, "task_attr", io_ctx->req.scsi_req.task_attr);
348 : 595 : spdk_json_write_named_uint32(w, "prio", io_ctx->req.scsi_req.prio);
349 : 595 : spdk_json_write_named_uint32(w, "crn", io_ctx->req.scsi_req.crn);
350 : 595 : spdk_json_write_named_string(w, "cdb", cdb_data);
351 : :
352 : 595 : free(lun_data);
353 : 595 : free(cdb_data);
354 : 595 : }
355 : :
356 : : static void
357 : 1911 : print_iov_obj(struct spdk_json_write_ctx *w, const char *iov_name, struct iovec *iov)
358 : : {
359 : : /* "0x" + up to 16 digits + null terminator */
360 : 0 : char hex_addr[19];
361 : : int rc;
362 : :
363 [ - + ]: 1911 : rc = snprintf(hex_addr, 19, "%lx", (uintptr_t)iov->iov_base);
364 : :
365 : : /* default to 0. */
366 [ + - - + ]: 1911 : if (rc < 0 || rc >= 19) {
367 : 0 : hex_addr[0] = '0';
368 : 0 : hex_addr[1] = '\0';
369 : : }
370 : :
371 : 1911 : spdk_json_write_named_object_begin(w, iov_name);
372 : 1911 : spdk_json_write_named_string(w, "iov_base", hex_addr);
373 : 1911 : spdk_json_write_named_uint64(w, "iov_len", iov->iov_len);
374 : 1911 : spdk_json_write_object_end(w);
375 : 1911 : }
376 : :
377 : : static void
378 : 637 : print_iovs(struct spdk_json_write_ctx *w, struct fuzz_vhost_io_ctx *io_ctx)
379 : : {
380 : 637 : print_iov_obj(w, "req_iov", &io_ctx->iovs.iov_req);
381 : 637 : print_iov_obj(w, "data_iov", &io_ctx->iovs.iov_data);
382 : 637 : print_iov_obj(w, "resp_iov", &io_ctx->iovs.iov_resp);
383 : 637 : }
384 : :
385 : : static void
386 : 637 : print_req_obj(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
387 : : {
388 : :
389 : : struct spdk_json_write_ctx *w;
390 : :
391 : 637 : w = spdk_json_write_begin(dump_virtio_cmd, NULL, SPDK_JSON_WRITE_FLAG_FORMATTED);
392 : :
393 [ - + + + ]: 637 : if (dev_ctx->socket_is_blk) {
394 : 1 : spdk_json_write_named_object_begin(w, BLK_IO_NAME);
395 : 1 : print_iovs(w, io_ctx);
396 : 1 : print_blk_io_data(w, io_ctx);
397 [ - + + + ]: 636 : } else if (dev_ctx->test_scsi_tmf) {
398 : 41 : spdk_json_write_named_object_begin(w, SCSI_MGMT_NAME);
399 : 41 : print_iovs(w, io_ctx);
400 : 41 : print_scsi_tmf_io_data(w, io_ctx);
401 : : } else {
402 : 595 : spdk_json_write_named_object_begin(w, SCSI_IO_NAME);
403 : 595 : print_iovs(w, io_ctx);
404 : 595 : print_scsi_io_data(w, io_ctx);
405 : : }
406 : 637 : spdk_json_write_object_end(w);
407 : 637 : spdk_json_write_end(w);
408 : 637 : }
409 : :
410 : : static void
411 : 0 : dump_outstanding_io(struct fuzz_vhost_dev_ctx *dev_ctx)
412 : : {
413 : : struct fuzz_vhost_io_ctx *io_ctx, *tmp;
414 : :
415 [ # # ]: 0 : TAILQ_FOREACH_SAFE(io_ctx, &dev_ctx->outstanding_io_ctx, link, tmp) {
416 : 0 : print_req_obj(dev_ctx, io_ctx);
417 [ # # ]: 0 : TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
418 : 0 : TAILQ_INSERT_TAIL(&dev_ctx->free_io_ctx, io_ctx, link);
419 : : }
420 : 0 : }
421 : : /* data dumping functions end */
422 : :
423 : : /* data parsing functions begin */
424 : : static int
425 : 180 : hex_value(uint8_t c)
426 : : {
427 : : #define V(x, y) [x] = y + 1
428 : : static const int8_t val[256] = {
429 : : V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4),
430 : : V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9),
431 : : V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF),
432 : : V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF),
433 : : };
434 : : #undef V
435 : :
436 : 180 : return val[c] - 1;
437 : : }
438 : :
439 : : static int
440 : 15 : fuzz_json_decode_hex_uint64(const struct spdk_json_val *val, void *out)
441 : : {
442 : 15 : uint64_t *out_val = out;
443 : : size_t i;
444 : 15 : char *val_pointer = val->start;
445 : : int current_val;
446 : :
447 [ - + ]: 15 : if (val->len > 16) {
448 : 0 : return -EINVAL;
449 : : }
450 : :
451 : 15 : *out_val = 0;
452 [ + + ]: 195 : for (i = 0; i < val->len; i++) {
453 : 180 : *out_val = *out_val << 4;
454 : 180 : current_val = hex_value(*val_pointer);
455 [ - + ]: 180 : if (current_val < 0) {
456 : 0 : return -EINVAL;
457 : : }
458 : 180 : *out_val += current_val;
459 : 180 : val_pointer++;
460 : : }
461 : :
462 : 15 : return 0;
463 : : }
464 : :
465 : : static const struct spdk_json_object_decoder fuzz_vhost_iov_decoders[] = {
466 : : {"iov_base", offsetof(struct iovec, iov_base), fuzz_json_decode_hex_uint64},
467 : : {"iov_len", offsetof(struct iovec, iov_len), spdk_json_decode_uint64},
468 : : };
469 : :
470 : : static size_t
471 : 15 : parse_iov_struct(struct iovec *iovec, struct spdk_json_val *value)
472 : : {
473 : : int rc;
474 : :
475 [ - + ]: 15 : if (value->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
476 : 0 : return -1;
477 : : }
478 : :
479 : 15 : rc = spdk_json_decode_object(value,
480 : : fuzz_vhost_iov_decoders,
481 : : SPDK_COUNTOF(fuzz_vhost_iov_decoders),
482 : : iovec);
483 [ - + ]: 15 : if (rc) {
484 : 0 : return -1;
485 : : }
486 : :
487 [ + + ]: 90 : while (value->type != SPDK_JSON_VAL_OBJECT_END) {
488 : 75 : value++;
489 : 75 : rc++;
490 : : }
491 : :
492 : : /* The +1 instructs the calling function to skip over the OBJECT_END function. */
493 : 15 : rc += 1;
494 : 15 : return rc;
495 : : }
496 : :
497 : : static bool
498 : 1 : parse_vhost_blk_cmds(void *item, struct spdk_json_val *value, size_t num_values)
499 : : {
500 : 1 : struct fuzz_vhost_io_ctx *io_ctx = item;
501 : : struct spdk_json_val *prev_value;
502 : : int nested_object_size;
503 : 0 : uint64_t tmp_val;
504 : 1 : size_t i = 0;
505 : :
506 [ + + ]: 9 : while (i < num_values) {
507 : 8 : nested_object_size = 1;
508 [ + + ]: 8 : if (value->type == SPDK_JSON_VAL_NAME) {
509 : 6 : prev_value = value;
510 : 6 : value++;
511 : 6 : i++;
512 [ - + + + ]: 6 : if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
513 : 1 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
514 [ - + + + ]: 5 : } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
515 : 1 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
516 [ - + + + ]: 4 : } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
517 : 1 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
518 [ - + + + ]: 3 : } else if (!strncmp(prev_value->start, "type", prev_value->len)) {
519 [ - + ]: 1 : if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
520 : 0 : nested_object_size = -1;
521 : : } else {
522 : 1 : io_ctx->req.blk_req.type = tmp_val;
523 : : }
524 [ - + + + ]: 2 : } else if (!strncmp(prev_value->start, "ioprio", prev_value->len)) {
525 [ - + ]: 1 : if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
526 : 0 : nested_object_size = -1;
527 : : } else {
528 : 1 : io_ctx->req.blk_req.ioprio = tmp_val;
529 : : }
530 [ - + + - ]: 1 : } else if (!strncmp(prev_value->start, "sector", prev_value->len)) {
531 [ - + ]: 1 : if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
532 : 0 : nested_object_size = -1;
533 : : } else {
534 : 1 : io_ctx->req.blk_req.sector = tmp_val;
535 : : }
536 : : }
537 : : }
538 [ - + ]: 8 : if (nested_object_size < 0) {
539 [ # # ]: 0 : fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
540 : 0 : (char *)prev_value->start, value->len, (char *)value->start);
541 : 0 : return false;
542 : : }
543 : 8 : value += nested_object_size;
544 : 8 : i += nested_object_size;
545 : : }
546 : 1 : return true;
547 : : }
548 : :
549 : : static bool
550 : 2 : parse_vhost_scsi_cmds(void *item, struct spdk_json_val *value, size_t num_values)
551 : : {
552 : 2 : struct fuzz_vhost_io_ctx *io_ctx = item;
553 : : struct spdk_json_val *prev_value;
554 : : int nested_object_size;
555 : 0 : uint64_t tmp_val;
556 : 2 : size_t i = 0;
557 : :
558 [ + + ]: 20 : while (i < num_values) {
559 : 18 : nested_object_size = 1;
560 [ + + ]: 18 : if (value->type == SPDK_JSON_VAL_NAME) {
561 : 14 : prev_value = value;
562 : 14 : value++;
563 : 14 : i++;
564 [ - + + + ]: 14 : if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
565 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
566 [ - + + + ]: 12 : } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
567 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
568 [ - + + + ]: 10 : } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
569 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
570 [ - + + + ]: 8 : } else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
571 [ - + ]: 2 : if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.lun,
572 : : sizeof(io_ctx->req.scsi_req.lun),
573 : 2 : (char *)value->start,
574 : 2 : value->len)) {
575 : 0 : nested_object_size = -1;
576 : : }
577 [ - + + + ]: 6 : } else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
578 [ - + ]: 2 : if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
579 : 0 : nested_object_size = -1;
580 : : } else {
581 : 2 : io_ctx->req.scsi_req.tag = tmp_val;
582 : : }
583 [ - + - + ]: 4 : } else if (!strncmp(prev_value->start, "task_attr", prev_value->len)) {
584 [ # # ]: 0 : if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
585 : 0 : nested_object_size = -1;
586 : : } else {
587 : 0 : io_ctx->req.scsi_req.task_attr = tmp_val;
588 : : }
589 [ - + - + ]: 4 : } else if (!strncmp(prev_value->start, "prio", prev_value->len)) {
590 [ # # ]: 0 : if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
591 : 0 : nested_object_size = -1;
592 : : } else {
593 : 0 : io_ctx->req.scsi_req.prio = tmp_val;
594 : : }
595 [ - + - + ]: 4 : } else if (!strncmp(prev_value->start, "crn", prev_value->len)) {
596 [ # # ]: 0 : if (fuzz_parse_json_num(value, UINT8_MAX, &tmp_val)) {
597 : 0 : nested_object_size = -1;
598 : : } else {
599 : 0 : io_ctx->req.scsi_req.crn = tmp_val;
600 : : }
601 [ - + - + ]: 4 : } else if (!strncmp(prev_value->start, "cdb", prev_value->len)) {
602 [ # # ]: 0 : if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_req.cdb,
603 : : sizeof(io_ctx->req.scsi_req.cdb),
604 : 0 : (char *)value->start,
605 : 0 : value->len)) {
606 : 0 : nested_object_size = -1;
607 : : }
608 : : }
609 : : }
610 [ - + ]: 18 : if (nested_object_size < 0) {
611 [ # # ]: 0 : fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
612 : 0 : (char *)prev_value->start, value->len, (char *)value->start);
613 : 0 : return false;
614 : : }
615 : 18 : value += nested_object_size;
616 : 18 : i += nested_object_size;
617 : : }
618 : 2 : return true;
619 : :
620 : : }
621 : :
622 : : static bool
623 : 2 : parse_vhost_scsi_mgmt_cmds(void *item, struct spdk_json_val *value, size_t num_values)
624 : : {
625 : 2 : struct fuzz_vhost_io_ctx *io_ctx = item;
626 : : struct spdk_json_val *prev_value;
627 : : int nested_object_size;
628 : 0 : uint64_t tmp_val;
629 : 2 : size_t i = 0;
630 : :
631 [ + + ]: 20 : while (i < num_values) {
632 : 18 : nested_object_size = 1;
633 [ + + ]: 18 : if (value->type == SPDK_JSON_VAL_NAME) {
634 : 14 : prev_value = value;
635 : 14 : value++;
636 : 14 : i++;
637 [ - + + + ]: 14 : if (!strncmp(prev_value->start, "req_iov", prev_value->len)) {
638 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_req, value);
639 [ - + + + ]: 12 : } else if (!strncmp(prev_value->start, "data_iov", prev_value->len)) {
640 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
641 [ - + + + ]: 10 : } else if (!strncmp(prev_value->start, "resp_iov", prev_value->len)) {
642 : 2 : nested_object_size = parse_iov_struct(&io_ctx->iovs.iov_data, value);
643 [ - + + + ]: 8 : } else if (!strncmp(prev_value->start, "type", prev_value->len)) {
644 [ - + ]: 2 : if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
645 : 0 : nested_object_size = -1;
646 : : } else {
647 : 2 : io_ctx->req.scsi_tmf_req.type = tmp_val;
648 : : }
649 [ - + + + ]: 6 : } else if (!strncmp(prev_value->start, "subtype", prev_value->len)) {
650 [ - + ]: 2 : if (fuzz_parse_json_num(value, UINT32_MAX, &tmp_val)) {
651 : 0 : nested_object_size = -1;
652 : : } else {
653 : 2 : io_ctx->req.scsi_tmf_req.subtype = tmp_val;
654 : : }
655 [ - + + + ]: 4 : } else if (!strncmp(prev_value->start, "lun", prev_value->len)) {
656 [ - + ]: 2 : if (fuzz_get_base_64_buffer_value(&io_ctx->req.scsi_tmf_req.lun,
657 : : sizeof(io_ctx->req.scsi_tmf_req.lun),
658 : 2 : (char *)value->start,
659 : 2 : value->len)) {
660 : 0 : nested_object_size = -1;
661 : : }
662 [ - + + - ]: 2 : } else if (!strncmp(prev_value->start, "tag", prev_value->len)) {
663 [ - + ]: 2 : if (fuzz_parse_json_num(value, UINT64_MAX, &tmp_val)) {
664 : 0 : nested_object_size = -1;
665 : : } else {
666 : 2 : io_ctx->req.scsi_tmf_req.tag = tmp_val;
667 : : }
668 : : }
669 : : }
670 [ - + ]: 18 : if (nested_object_size < 0) {
671 [ # # ]: 0 : fprintf(stderr, "Invalid value supplied for io_ctx->%.*s: %.*s\n", prev_value->len,
672 : 0 : (char *)prev_value->start, value->len, (char *)value->start);
673 : 0 : return false;
674 : : }
675 : 18 : value += nested_object_size;
676 : 18 : i += nested_object_size;
677 : : }
678 : 2 : return true;
679 : : }
680 : : /* data parsing functions end */
681 : :
682 : : /* build requests begin */
683 : : static void
684 : 5 : craft_io_from_array(struct fuzz_vhost_io_ctx *src_ctx, struct fuzz_vhost_io_ctx *dest_ctx)
685 : : {
686 [ - + - + ]: 5 : if (g_keep_iov_pointers) {
687 : 0 : dest_ctx->iovs = src_ctx->iovs;
688 : : }
689 : 5 : dest_ctx->req = src_ctx->req;
690 : 5 : }
691 : :
692 : : static void
693 : 143648 : craft_virtio_scsi_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
694 : : {
695 : 143648 : io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_req);
696 : 143648 : io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_resp);
697 : 143648 : fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_req, sizeof(io_ctx->req.scsi_req),
698 : : &dev_ctx->random_seed);
699 : : /* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
700 [ - + + - ]: 143648 : if (dev_ctx->valid_lun) {
701 : 143648 : io_ctx->req.scsi_req.lun[0] = 1;
702 : 143648 : io_ctx->req.scsi_req.lun[1] = 0;
703 : : }
704 : 143648 : }
705 : :
706 : : static void
707 : 48896 : craft_virtio_scsi_tmf_req(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
708 : : {
709 : 48896 : io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.scsi_tmf_req);
710 : 48896 : io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.scsi_tmf_resp);
711 : 48896 : fuzz_fill_random_bytes((char *)&io_ctx->req.scsi_tmf_req, sizeof(io_ctx->req.scsi_tmf_req),
712 : : &dev_ctx->random_seed);
713 : : /* TODO: set up the logic to find all luns on the target. Right now we are just assuming the first is OK. */
714 [ - + - + ]: 48896 : if (dev_ctx->valid_lun) {
715 : 0 : io_ctx->req.scsi_tmf_req.lun[0] = 1;
716 : 0 : io_ctx->req.scsi_tmf_req.lun[1] = 0;
717 : : }
718 : :
719 : : /* Valid controlq commands have to be of type 0, 1, or 2. Any others just return immediately from the target. */
720 : : /* Try to only test the opcodes that will exercise extra paths in the target side. But allow for at least one invalid value. */
721 : 48896 : io_ctx->req.scsi_tmf_req.type = rand() % 4;
722 : 48896 : }
723 : :
724 : : static void
725 : 143662 : craft_virtio_blk_req(struct fuzz_vhost_io_ctx *io_ctx)
726 : : {
727 : 143662 : io_ctx->iovs.iov_req.iov_len = sizeof(io_ctx->req.blk_req);
728 : 143662 : io_ctx->iovs.iov_resp.iov_len = sizeof(io_ctx->resp.blk_resp);
729 : 143662 : io_ctx->req.blk_req.type = rand();
730 : 143662 : io_ctx->req.blk_req.sector = rand();
731 : 143662 : }
732 : :
733 : : static void
734 : 336211 : craft_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
735 : : {
736 : 336211 : struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
737 : :
738 : : /*
739 : : * Always set these buffer values up front.
740 : : * If the user wants to override this with the json values,
741 : : * they can specify -k when starting the app. */
742 : 336211 : iovs->iov_req.iov_base = &io_ctx->req;
743 [ - + - + ]: 336211 : if (dev_ctx->use_bogus_buffer) {
744 : 0 : iovs->iov_data.iov_len = rand();
745 : 0 : iovs->iov_data.iov_base = get_invalid_mem_address(iovs->iov_data.iov_len);
746 [ - + + - ]: 336211 : } else if (dev_ctx->use_valid_buffer) {
747 : 336211 : iovs->iov_data.iov_len = 1024;
748 : 336211 : iovs->iov_data.iov_base = g_valid_buffer;
749 : : }
750 : 336211 : iovs->iov_resp.iov_base = &io_ctx->resp;
751 : :
752 [ - + + + : 336211 : if (dev_ctx->socket_is_blk && g_blk_cmd_array) {
+ + ]
753 : 1 : craft_io_from_array(&g_blk_cmd_array[dev_ctx->submitted_io], io_ctx);
754 : 1 : return;
755 [ - + + + : 336210 : } else if (dev_ctx->test_scsi_tmf && g_scsi_mgmt_cmd_array) {
+ + ]
756 : 2 : craft_io_from_array(&g_scsi_mgmt_cmd_array[dev_ctx->submitted_io], io_ctx);
757 : 2 : return;
758 [ + + ]: 336208 : } else if (g_scsi_cmd_array) {
759 : 2 : craft_io_from_array(&g_scsi_cmd_array[dev_ctx->submitted_io], io_ctx);
760 : 2 : return;
761 : : }
762 : :
763 [ - + + + ]: 336206 : if (dev_ctx->socket_is_blk) {
764 : 143662 : craft_virtio_blk_req(io_ctx);
765 [ - + + + ]: 192544 : } else if (dev_ctx->test_scsi_tmf) {
766 : 48896 : craft_virtio_scsi_tmf_req(dev_ctx, io_ctx);
767 : : } else {
768 : 143648 : craft_virtio_scsi_req(dev_ctx, io_ctx);
769 : : }
770 : : }
771 : : /* build requests end */
772 : :
773 : : /* submit requests begin */
774 : : static uint64_t
775 : 3125 : get_max_num_io(struct fuzz_vhost_dev_ctx *dev_ctx)
776 : : {
777 [ - + + + ]: 3125 : if (dev_ctx->socket_is_blk) {
778 : 1042 : return g_blk_cmd_array_size;
779 [ - + + + ]: 2083 : } else if (dev_ctx->test_scsi_tmf) {
780 : 1041 : return g_scsi_mgmt_cmd_array_size;
781 : : } else {
782 : 1042 : return g_scsi_cmd_array_size;
783 : : }
784 : : }
785 : :
786 : : static int
787 : 336211 : submit_virtio_req_rsp_pair(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
788 : : struct fuzz_vhost_io_ctx *io_ctx)
789 : : {
790 : 336211 : struct fuzz_vhost_iov_ctx *iovs = &io_ctx->iovs;
791 : 336211 : int num_iovs = 2, rc;
792 : :
793 [ - + + - : 336211 : num_iovs += dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer ? 1 : 0;
- + + - ]
794 : :
795 : 336211 : rc = virtqueue_req_start(vq, io_ctx, num_iovs);
796 [ - + ]: 336211 : if (rc) {
797 : 0 : return rc;
798 : : }
799 : 336211 : virtqueue_req_add_iovs(vq, &iovs->iov_req, 1, SPDK_VIRTIO_DESC_RO);
800 : : /* blk and scsi requests favor different orders for the iov objects. */
801 [ - + + + ]: 336211 : if (dev_ctx->socket_is_blk) {
802 [ - + + - : 143663 : if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
- + + - ]
803 : 143663 : virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
804 : : }
805 : 143663 : virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
806 : : } else {
807 : 192548 : virtqueue_req_add_iovs(vq, &iovs->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
808 [ - + + - : 192548 : if (dev_ctx->use_bogus_buffer || dev_ctx->use_valid_buffer) {
- + + - ]
809 : 192548 : virtqueue_req_add_iovs(vq, &iovs->iov_data, 1, SPDK_VIRTIO_DESC_WR);
810 : : }
811 : : }
812 : 336211 : virtqueue_req_flush(vq);
813 : 336211 : return 0;
814 : : }
815 : :
816 : : static void
817 : 3661360 : dev_submit_requests(struct fuzz_vhost_dev_ctx *dev_ctx, struct virtqueue *vq,
818 : : uint64_t max_io_to_submit)
819 : : {
820 : : struct fuzz_vhost_io_ctx *io_ctx;
821 : : int rc;
822 : :
823 [ + + + + ]: 3997571 : while (!TAILQ_EMPTY(&dev_ctx->free_io_ctx) && dev_ctx->submitted_io < max_io_to_submit) {
824 : 336211 : io_ctx = TAILQ_FIRST(&dev_ctx->free_io_ctx);
825 : 336211 : craft_virtio_req_rsp_pair(dev_ctx, io_ctx);
826 : 336211 : rc = submit_virtio_req_rsp_pair(dev_ctx, vq, io_ctx);
827 [ + - ]: 336211 : if (rc == 0) {
828 [ + + ]: 336211 : TAILQ_REMOVE(&dev_ctx->free_io_ctx, io_ctx, link);
829 : 336211 : TAILQ_INSERT_TAIL(&dev_ctx->outstanding_io_ctx, io_ctx, link);
830 : 336211 : dev_ctx->submitted_io++;
831 [ # # ]: 0 : } else if (rc == -ENOMEM) {
832 : : /* There are just not enough available buffers right now. try later. */
833 : 0 : return;
834 [ # # ]: 0 : } else if (rc == -EINVAL) {
835 : : /* The virtqueue must be broken. We know we can fit at least three descriptors */
836 [ # # # # ]: 0 : fprintf(stderr, "One of the virtqueues for dev %p is broken. stopping all devices.\n", dev_ctx);
837 : 0 : g_run = 0;
838 : : }
839 : : }
840 : : }
841 : : /* submit requests end */
842 : :
843 : : /* complete requests begin */
844 : : static void
845 : 336211 : check_successful_op(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
846 : : {
847 : 336211 : bool is_successful = false;
848 : :
849 [ - + + + ]: 336211 : if (dev_ctx->socket_is_blk) {
850 [ + + ]: 143663 : if (io_ctx->resp.blk_resp == 0) {
851 : 1 : is_successful = true;
852 : : }
853 [ - + + + ]: 192548 : } else if (dev_ctx->test_scsi_tmf) {
854 [ + + ]: 48898 : if (io_ctx->resp.scsi_tmf_resp.scsi_tmf_resp.response == 0 &&
855 [ + + ]: 404 : io_ctx->resp.scsi_tmf_resp.an_resp.response == 0) {
856 : 41 : is_successful = true;
857 : : }
858 : : } else {
859 [ + + ]: 143650 : if (io_ctx->resp.scsi_resp.status == 0) {
860 : 595 : is_successful = true;
861 : : }
862 : : }
863 : :
864 [ + + ]: 336211 : if (is_successful) {
865 [ - + - + ]: 637 : fprintf(stderr, "An I/O completed without an error status. This could be worth looking into.\n");
866 [ - + - + ]: 637 : fprintf(stderr,
867 : : "There is also a good chance that the target just failed before setting a status.\n");
868 : 637 : dev_ctx->successful_io++;
869 : 637 : print_req_obj(dev_ctx, io_ctx);
870 [ - + - + ]: 335574 : } else if (g_verbose_mode) {
871 [ # # # # ]: 0 : fprintf(stderr, "The following I/O failed as expected.\n");
872 : 0 : print_req_obj(dev_ctx, io_ctx);
873 : : }
874 : 336211 : }
875 : :
876 : : static void
877 : 336211 : complete_io(struct fuzz_vhost_dev_ctx *dev_ctx, struct fuzz_vhost_io_ctx *io_ctx)
878 : : {
879 [ + + ]: 336211 : TAILQ_REMOVE(&dev_ctx->outstanding_io_ctx, io_ctx, link);
880 [ + + ]: 336211 : TAILQ_INSERT_HEAD(&dev_ctx->free_io_ctx, io_ctx, link);
881 : 336211 : check_successful_op(dev_ctx, io_ctx);
882 : 336211 : dev_ctx->completed_io++;
883 : 336211 : dev_ctx->timeout_tsc = fuzz_refresh_timeout();
884 : 336211 : }
885 : :
886 : : static int
887 : 3668803 : poll_dev(void *ctx)
888 : : {
889 : 3668803 : struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
890 : : struct virtqueue *vq;
891 : 0 : struct fuzz_vhost_io_ctx *io_ctx[FUZZ_QUEUE_DEPTH];
892 : : int num_active_threads;
893 : 3668803 : uint64_t max_io_to_complete = UINT64_MAX;
894 : : uint64_t current_ticks;
895 : 0 : uint32_t len[FUZZ_QUEUE_DEPTH];
896 : : uint16_t num_cpl, i;
897 : :
898 [ + + ]: 3668803 : if (g_json_file) {
899 : 3125 : max_io_to_complete = get_max_num_io(dev_ctx);
900 : : }
901 : :
902 [ - + + + : 3668803 : if (!dev_ctx->socket_is_blk && dev_ctx->test_scsi_tmf) {
- + + + ]
903 : 1225409 : vq = dev_ctx->virtio_dev.vqs[VIRTIO_SCSI_CONTROLQ];
904 : : } else {
905 : 2443394 : vq = dev_ctx->virtio_dev.vqs[VIRTIO_REQUESTQ];
906 : : }
907 : :
908 : 3668803 : num_cpl = virtio_recv_pkts(vq, (void **)io_ctx, len, FUZZ_QUEUE_DEPTH);
909 : :
910 [ + + ]: 4005014 : for (i = 0; i < num_cpl; i++) {
911 : 336211 : complete_io(dev_ctx, io_ctx[i]);
912 : : }
913 : :
914 : 3668803 : current_ticks = spdk_get_ticks();
915 : :
916 [ - + ]: 3668803 : if (current_ticks > dev_ctx->timeout_tsc) {
917 : 0 : dev_ctx->timed_out = true;
918 : 0 : g_run = false;
919 [ # # # # ]: 0 : fprintf(stderr, "The VQ on device %p timed out. Dumping contents now.\n", dev_ctx);
920 : 0 : dump_outstanding_io(dev_ctx);
921 : : }
922 : :
923 [ + + ]: 3668803 : if (current_ticks > g_runtime_ticks) {
924 : 7440 : g_run = 0;
925 : : }
926 : :
927 [ - + + + : 3668803 : if (!g_run || dev_ctx->completed_io >= max_io_to_complete) {
+ + ]
928 [ + + ]: 7443 : if (TAILQ_EMPTY(&dev_ctx->outstanding_io_ctx)) {
929 : 6 : spdk_poller_unregister(&dev_ctx->poller);
930 : 6 : num_active_threads = __sync_sub_and_fetch(&g_num_active_threads, 1);
931 [ + + ]: 6 : if (num_active_threads == 0) {
932 : 2 : g_run = 0;
933 : : }
934 : 6 : spdk_thread_exit(dev_ctx->thread);
935 : : }
936 : 7443 : return 0;
937 : : }
938 : :
939 : 3661360 : dev_submit_requests(dev_ctx, vq, max_io_to_complete);
940 : 3661360 : return 0;
941 : : }
942 : : /* complete requests end */
943 : :
944 : : static void
945 : 6 : start_io(void *ctx)
946 : : {
947 : 6 : struct fuzz_vhost_dev_ctx *dev_ctx = ctx;
948 : :
949 [ - + ]: 6 : if (g_random_seed) {
950 : 0 : dev_ctx->random_seed = g_random_seed;
951 : : } else {
952 : 6 : dev_ctx->random_seed = spdk_get_ticks();
953 : : }
954 : :
955 : 6 : dev_ctx->timeout_tsc = fuzz_refresh_timeout();
956 : :
957 : 6 : dev_ctx->poller = SPDK_POLLER_REGISTER(poll_dev, dev_ctx, 0);
958 [ - + ]: 6 : if (dev_ctx->poller == NULL) {
959 : 0 : return;
960 : : }
961 : :
962 : : }
963 : :
964 : : static int
965 : 1225410 : end_fuzz(void *ctx)
966 : : {
967 [ - + + + : 1225410 : if (!g_run && !g_num_active_threads) {
+ + ]
968 : 2 : spdk_poller_unregister(&g_run_poller);
969 : 2 : cleanup();
970 : 2 : spdk_app_stop(0);
971 : : }
972 : 1225410 : return 0;
973 : : }
974 : :
975 : : static void
976 : 2 : begin_fuzz(void *ctx)
977 : : {
978 : : struct fuzz_vhost_dev_ctx *dev_ctx;
979 : :
980 : 2 : g_runtime_ticks = spdk_get_ticks() + spdk_get_ticks_hz() * g_runtime;
981 : :
982 : 2 : g_valid_buffer = spdk_malloc(0x1000, 0x200, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE);
983 [ - + ]: 2 : if (g_valid_buffer == NULL) {
984 [ # # # # ]: 0 : fprintf(stderr, "Failed to allocate a valid buffer for I/O\n");
985 : 0 : goto out;
986 : : }
987 : :
988 : 2 : g_run_poller = SPDK_POLLER_REGISTER(end_fuzz, NULL, 0);
989 [ - + ]: 2 : if (g_run_poller == NULL) {
990 [ # # # # ]: 0 : fprintf(stderr, "Failed to register a poller for test completion checking.\n");
991 : : }
992 : :
993 [ + + ]: 8 : TAILQ_FOREACH(dev_ctx, &g_dev_list, link) {
994 [ - + ]: 6 : assert(dev_ctx->thread != NULL);
995 : 6 : spdk_thread_send_msg(dev_ctx->thread, start_io, dev_ctx);
996 : 6 : __sync_add_and_fetch(&g_num_active_threads, 1);
997 : : }
998 : :
999 : 2 : return;
1000 : 0 : out:
1001 : 0 : cleanup();
1002 : 0 : spdk_app_stop(0);
1003 : : }
1004 : :
1005 : : static void
1006 : 0 : fuzz_vhost_usage(void)
1007 : : {
1008 [ # # # # ]: 0 : fprintf(stderr, " -j <path> Path to a json file containing named objects.\n");
1009 [ # # # # ]: 0 : fprintf(stderr,
1010 : : " -k Keep the iov pointer addresses from the json file. only valid with -j.\n");
1011 [ # # # # ]: 0 : fprintf(stderr, " -S <integer> Seed value for test.\n");
1012 [ # # # # ]: 0 : fprintf(stderr, " -t <integer> Time in seconds to run the fuzz test.\n");
1013 [ # # # # ]: 0 : fprintf(stderr, " -V Enable logging of each submitted command.\n");
1014 : 0 : }
1015 : :
1016 : : static int
1017 : 2 : fuzz_vhost_parse(int ch, char *arg)
1018 : : {
1019 : : int64_t error_test;
1020 : :
1021 [ + - - + : 2 : switch (ch) {
- - ]
1022 : 1 : case 'j':
1023 : 1 : g_json_file = optarg;
1024 : 1 : break;
1025 : 0 : case 'k':
1026 : 0 : g_keep_iov_pointers = true;
1027 : 0 : break;
1028 : 0 : case 'S':
1029 : 0 : error_test = spdk_strtol(arg, 10);
1030 [ # # ]: 0 : if (error_test < 0) {
1031 [ # # # # ]: 0 : fprintf(stderr, "Invalid value supplied for the random seed.\n");
1032 : 0 : return -1;
1033 : : } else {
1034 : 0 : g_random_seed = spdk_strtol(arg, 10);
1035 : : }
1036 : 0 : break;
1037 : 1 : case 't':
1038 : 1 : g_runtime = spdk_strtol(arg, 10);
1039 [ + - - + ]: 1 : if (g_runtime < 0 || g_runtime > MAX_RUNTIME_S) {
1040 [ # # # # ]: 0 : fprintf(stderr, "You must supply a positive runtime value less than 86401.\n");
1041 : 0 : return -1;
1042 : : }
1043 : 1 : break;
1044 : 0 : case 'V':
1045 : 0 : g_verbose_mode = true;
1046 : 0 : break;
1047 : 0 : case '?':
1048 : : default:
1049 : 0 : return -EINVAL;
1050 : : }
1051 : 2 : return 0;
1052 : : }
1053 : :
1054 : : int
1055 : 2 : main(int argc, char **argv)
1056 : : {
1057 : 2 : struct spdk_app_opts opts = {};
1058 : : int rc;
1059 : :
1060 : 2 : spdk_app_opts_init(&opts, sizeof(opts));
1061 : 2 : opts.name = "vhost_fuzz";
1062 : 2 : opts.rpc_addr = NULL;
1063 : 2 : g_runtime = DEFAULT_RUNTIME;
1064 : :
1065 : 2 : rc = spdk_app_parse_args(argc, argv, &opts, "j:kS:t:V", NULL, fuzz_vhost_parse, fuzz_vhost_usage);
1066 [ - + ]: 2 : if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) {
1067 [ # # # # ]: 0 : fprintf(stderr, "Unable to parse the application arguments.\n");
1068 : 0 : return -1;
1069 : : }
1070 : :
1071 [ + + ]: 2 : if (g_json_file != NULL) {
1072 : 1 : g_blk_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
1073 : : (void **)&g_blk_cmd_array,
1074 : : sizeof(struct fuzz_vhost_io_ctx),
1075 : : BLK_IO_NAME, parse_vhost_blk_cmds);
1076 : 1 : g_scsi_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
1077 : : (void **)&g_scsi_cmd_array,
1078 : : sizeof(struct fuzz_vhost_io_ctx),
1079 : : SCSI_IO_NAME, parse_vhost_scsi_cmds);
1080 : 1 : g_scsi_mgmt_cmd_array_size = fuzz_parse_args_into_array(g_json_file,
1081 : : (void **)&g_scsi_mgmt_cmd_array,
1082 : : sizeof(struct fuzz_vhost_io_ctx),
1083 : : SCSI_IO_NAME, parse_vhost_scsi_mgmt_cmds);
1084 [ - + - - : 1 : if (g_blk_cmd_array_size == 0 && g_scsi_cmd_array_size == 0 && g_scsi_mgmt_cmd_array_size == 0) {
- - ]
1085 [ # # # # ]: 0 : fprintf(stderr, "The provided json file did not contain any valid commands. Exiting.\n");
1086 : 0 : return -EINVAL;
1087 : : }
1088 : : }
1089 : :
1090 : 2 : rc = spdk_app_start(&opts, begin_fuzz, NULL);
1091 : :
1092 : 2 : spdk_app_fini();
1093 : 2 : return rc;
1094 : : }
|