4 * IO engine that talks to an NBD server.
6 * Copyright (C) 2019 Red Hat Inc.
7 * Written by Richard W.M. Jones <rjones@redhat.com>
19 #include "../optgroup.h"
21 /* Actually this differs across servers, but for nbdkit ... */
22 #define NBD_MAX_REQUEST_SIZE (64 * 1024 * 1024)
24 /* Storage for the NBD handle. */
26 struct nbd_handle *nbd;
29 /* The list of completed io_u structs. */
30 struct io_u **completed;
40 static struct fio_option options[] = {
44 .help = "Name of NBD URI",
45 .category = FIO_OPT_C_ENGINE,
46 .group = FIO_OPT_G_NBD,
47 .type = FIO_OPT_STR_STORE,
48 .off1 = offsetof(struct nbd_options, uri),
55 /* Alocates nbd_data. */
56 static int nbd_setup(struct thread_data *td)
58 struct nbd_data *nbd_data;
59 struct nbd_options *o = td->eo;
64 nbd_data = calloc(1, sizeof(*nbd_data));
66 td_verror(td, errno, "calloc");
69 td->io_ops_data = nbd_data;
71 /* Pretend to deal with files. See engines/rbd.c */
72 if (!td->files_index) {
73 add_file(td, "nbd", 0, 0);
74 td->o.nr_files = td->o.nr_files ? : 1;
79 nbd_data->nbd = nbd_create();
81 log_err("fio: nbd_create: %s\n", nbd_get_error());
85 /* Get the debug flag which can be set through LIBNBD_DEBUG=1. */
86 nbd_data->debug = nbd_get_debug(nbd_data->nbd);
88 /* Connect synchronously here so we can check for the size and
89 * in future other properties of the server.
92 log_err("fio: nbd: uri parameter was not specified\n");
95 r = nbd_connect_uri(nbd_data->nbd, o->uri);
97 log_err("fio: nbd_connect_uri: %s\n", nbd_get_error());
100 size = nbd_get_size(nbd_data->nbd);
102 log_err("fio: nbd_get_size: %s\n", nbd_get_error());
106 f->real_file_size = size;
108 nbd_close (nbd_data->nbd);
109 nbd_data->nbd = NULL;
114 /* Closes socket and frees nbd_data -- the opposite of nbd_setup. */
115 static void nbd_cleanup(struct thread_data *td)
117 struct nbd_data *nbd_data = td->io_ops_data;
121 nbd_close(nbd_data->nbd);
126 /* Connect to the server from each thread. */
127 static int nbd_init(struct thread_data *td)
129 struct nbd_options *o = td->eo;
130 struct nbd_data *nbd_data = td->io_ops_data;
134 log_err("fio: nbd: uri parameter was not specified\n");
138 nbd_data->nbd = nbd_create();
139 if (!nbd_data->nbd) {
140 log_err("fio: nbd_create: %s\n", nbd_get_error());
143 /* This is actually a synchronous connect and handshake. */
144 r = nbd_connect_uri(nbd_data->nbd, o->uri);
146 log_err("fio: nbd_connect_uri: %s\n", nbd_get_error());
150 log_info("fio: connected to NBD server\n");
154 /* A command in flight has been completed. */
155 static int cmd_completed (void *vp, int *error)
158 struct nbd_data *nbd_data;
159 struct io_u **completed;
162 nbd_data = io_u->engine_data;
165 log_info("fio: nbd: command completed\n");
168 io_u->error = *error;
172 /* Add this completion to the list so it can be picked up
175 completed = realloc(nbd_data->completed,
176 sizeof(struct io_u *) *
177 (nbd_data->nr_completed+1));
178 if (completed == NULL) {
183 nbd_data->completed = completed;
184 nbd_data->completed[nbd_data->nr_completed] = io_u;
185 nbd_data->nr_completed++;
190 /* Begin read or write request. */
191 static enum fio_q_status nbd_queue(struct thread_data *td,
194 struct nbd_data *nbd_data = td->io_ops_data;
195 nbd_completion_callback completion = { .callback = cmd_completed,
199 fio_ro_check(td, io_u);
201 io_u->engine_data = nbd_data;
203 if (io_u->ddir == DDIR_WRITE || io_u->ddir == DDIR_READ)
204 assert(io_u->xfer_buflen <= NBD_MAX_REQUEST_SIZE);
206 switch (io_u->ddir) {
208 r = nbd_aio_pread(nbd_data->nbd,
209 io_u->xfer_buf, io_u->xfer_buflen,
210 io_u->offset, completion, 0);
213 r = nbd_aio_pwrite(nbd_data->nbd,
214 io_u->xfer_buf, io_u->xfer_buflen,
215 io_u->offset, completion, 0);
218 r = nbd_aio_trim(nbd_data->nbd, io_u->xfer_buflen,
219 io_u->offset, completion, 0);
222 /* XXX We could probably also handle
223 * DDIR_SYNC_FILE_RANGE with a bit of effort.
225 r = nbd_aio_flush(nbd_data->nbd, completion, 0);
228 io_u->error = EINVAL;
229 return FIO_Q_COMPLETED;
233 /* errno is optional information on libnbd error path;
234 * if it's 0, set it to a default value
236 io_u->error = nbd_get_errno();
237 if (io_u->error == 0)
239 return FIO_Q_COMPLETED;
243 log_info("fio: nbd: command issued\n");
248 static unsigned retire_commands(struct nbd_handle *nbd)
253 while ((cookie = nbd_aio_peek_command_completed(nbd)) > 0) {
254 /* Ignore the return value. cmd_completed has already
255 * checked for an error and set io_u->error. We only
256 * have to call this to retire the command.
258 nbd_aio_command_completed(nbd, cookie);
262 if (nbd_get_debug(nbd))
263 log_info("fio: nbd: %u commands retired\n", r);
267 static int nbd_getevents(struct thread_data *td, unsigned int min,
268 unsigned int max, const struct timespec *t)
270 struct nbd_data *nbd_data = td->io_ops_data;
275 /* XXX This handling of timeout is wrong because it will wait
276 * for up to loop iterations * timeout.
278 timeout = !t ? -1 : t->tv_sec * 1000 + t->tv_nsec / 1000000;
280 while (events < min) {
281 r = nbd_poll(nbd_data->nbd, timeout);
284 log_err("fio: nbd_poll: %s\n", nbd_get_error());
288 /* poll made progress */
289 events += retire_commands(nbd_data->nbd);
296 static struct io_u *nbd_event(struct thread_data *td, int event)
298 struct nbd_data *nbd_data = td->io_ops_data;
300 if (nbd_data->nr_completed == 0)
303 /* XXX We ignore the event number and assume fio calls us
304 * exactly once for [0..nr_events-1].
306 nbd_data->nr_completed--;
307 return nbd_data->completed[nbd_data->nr_completed];
310 static int nbd_io_u_init(struct thread_data *td, struct io_u *io_u)
312 io_u->engine_data = NULL;
316 static void nbd_io_u_free(struct thread_data *td, struct io_u *io_u)
318 /* Nothing needs to be done. */
321 static int nbd_open_file(struct thread_data *td, struct fio_file *f)
326 static int nbd_invalidate(struct thread_data *td, struct fio_file *f)
331 static struct ioengine_ops ioengine = {
333 .version = FIO_IOOPS_VERSION,
335 .option_struct_size = sizeof(struct nbd_options),
336 .flags = FIO_DISKLESSIO | FIO_NOEXTEND,
340 .cleanup = nbd_cleanup,
342 .getevents = nbd_getevents,
344 .io_u_init = nbd_io_u_init,
345 .io_u_free = nbd_io_u_free,
347 .open_file = nbd_open_file,
348 .invalidate = nbd_invalidate,
351 static void fio_init fio_nbd_register(void)
353 register_ioengine(&ioengine);
356 static void fio_exit fio_nbd_unregister(void)
358 unregister_ioengine(&ioengine);