Windows fio: implement some more POSIX functions
[fio.git] / engines / net.c
1 /*
2  * net engine
3  *
4  * IO engine that reads/writes to/from sockets.
5  *
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <netdb.h>
15 #include <sys/poll.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20
21 #include "../fio.h"
22
23 struct netio_data {
24         int listenfd;
25         int use_splice;
26         int pipes[2];
27         struct sockaddr_in addr;
28         struct sockaddr_un addr_un;
29 };
30
31 struct netio_options {
32         struct thread_data *td;
33         unsigned int port;
34         unsigned int proto;
35         unsigned int listen;
36 };
37
38 struct udp_close_msg {
39         uint32_t magic;
40         uint32_t cmd;
41 };
42
43 enum {
44         FIO_LINK_CLOSE = 0x89,
45         FIO_LINK_CLOSE_MAGIC = 0x6c696e6b,
46
47         FIO_TYPE_TCP    = 1,
48         FIO_TYPE_UDP    = 2,
49         FIO_TYPE_UNIX   = 3,
50 };
51
52 static int str_hostname_cb(void *data, const char *input);
53 static struct fio_option options[] = {
54         {
55                 .name   = "hostname",
56                 .type   = FIO_OPT_STR_STORE,
57                 .cb     = str_hostname_cb,
58                 .help   = "Hostname for net IO engine",
59         },
60         {
61                 .name   = "port",
62                 .type   = FIO_OPT_INT,
63                 .off1   = offsetof(struct netio_options, port),
64                 .minval = 1,
65                 .maxval = 65535,
66                 .help   = "Port to use for TCP or UDP net connections",
67         },
68         {
69                 .name   = "protocol",
70                 .alias  = "proto",
71                 .type   = FIO_OPT_STR,
72                 .off1   = offsetof(struct netio_options, proto),
73                 .help   = "Network protocol to use",
74                 .def    = "tcp",
75                 .posval = {
76                           { .ival = "tcp",
77                             .oval = FIO_TYPE_TCP,
78                             .help = "Transmission Control Protocol",
79                           },
80                           { .ival = "udp",
81                             .oval = FIO_TYPE_UDP,
82                             .help = "Unreliable Datagram Protocol",
83                           },
84                           { .ival = "unix",
85                             .oval = FIO_TYPE_UNIX,
86                             .help = "UNIX domain socket",
87                           },
88                 },
89         },
90         {
91                 .name   = "listen",
92                 .type   = FIO_OPT_STR_SET,
93                 .off1   = offsetof(struct netio_options, listen),
94                 .help   = "Listen for incoming TCP connections",
95         },
96         {
97                 .name   = NULL,
98         },
99 };
100
101 /*
102  * Return -1 for error and 'nr events' for a positive number
103  * of events
104  */
105 static int poll_wait(struct thread_data *td, int fd, short events)
106 {
107         struct pollfd pfd;
108         int ret;
109
110         while (!td->terminate) {
111                 pfd.fd = fd;
112                 pfd.events = events;
113                 ret = poll(&pfd, 1, -1);
114                 if (ret < 0) {
115                         if (errno == EINTR)
116                                 break;
117
118                         td_verror(td, errno, "poll");
119                         return -1;
120                 } else if (!ret)
121                         continue;
122
123                 break;
124         }
125
126         if (pfd.revents & events)
127                 return 1;
128
129         return -1;
130 }
131
132 static int fio_netio_prep(struct thread_data *td, struct io_u *io_u)
133 {
134         struct netio_options *o = td->eo;
135
136         /*
137          * Make sure we don't see spurious reads to a receiver, and vice versa
138          */
139         if (o->proto == FIO_TYPE_TCP)
140                 return 0;
141
142         if ((o->listen && io_u->ddir == DDIR_WRITE) ||
143             (!o->listen && io_u->ddir == DDIR_READ)) {
144                 td_verror(td, EINVAL, "bad direction");
145                 return 1;
146         }
147                 
148         return 0;
149 }
150
151 #ifdef FIO_HAVE_SPLICE
152 static int splice_io_u(int fdin, int fdout, unsigned int len)
153 {
154         int bytes = 0;
155
156         while (len) {
157                 int ret = splice(fdin, NULL, fdout, NULL, len, 0);
158
159                 if (ret < 0) {
160                         if (!bytes)
161                                 bytes = ret;
162
163                         break;
164                 } else if (!ret)
165                         break;
166
167                 bytes += ret;
168                 len -= ret;
169         }
170
171         return bytes;
172 }
173
174 /*
175  * Receive bytes from a socket and fill them into the internal pipe
176  */
177 static int splice_in(struct thread_data *td, struct io_u *io_u)
178 {
179         struct netio_data *nd = td->io_ops->data;
180
181         return splice_io_u(io_u->file->fd, nd->pipes[1], io_u->xfer_buflen);
182 }
183
184 /*
185  * Transmit 'len' bytes from the internal pipe
186  */
187 static int splice_out(struct thread_data *td, struct io_u *io_u,
188                       unsigned int len)
189 {
190         struct netio_data *nd = td->io_ops->data;
191
192         return splice_io_u(nd->pipes[0], io_u->file->fd, len);
193 }
194
195 static int vmsplice_io_u(struct io_u *io_u, int fd, unsigned int len)
196 {
197         struct iovec iov = {
198                 .iov_base = io_u->xfer_buf,
199                 .iov_len = len,
200         };
201         int bytes = 0;
202
203         while (iov.iov_len) {
204                 int ret = vmsplice(fd, &iov, 1, SPLICE_F_MOVE);
205
206                 if (ret < 0) {
207                         if (!bytes)
208                                 bytes = ret;
209                         break;
210                 } else if (!ret)
211                         break;
212
213                 iov.iov_len -= ret;
214                 iov.iov_base += ret;
215                 bytes += ret;
216         }
217
218         return bytes;
219
220 }
221
222 /*
223  * vmsplice() pipe to io_u buffer
224  */
225 static int vmsplice_io_u_out(struct thread_data *td, struct io_u *io_u,
226                              unsigned int len)
227 {
228         struct netio_data *nd = td->io_ops->data;
229
230         return vmsplice_io_u(io_u, nd->pipes[0], len);
231 }
232
233 /*
234  * vmsplice() io_u to pipe
235  */
236 static int vmsplice_io_u_in(struct thread_data *td, struct io_u *io_u)
237 {
238         struct netio_data *nd = td->io_ops->data;
239
240         return vmsplice_io_u(io_u, nd->pipes[1], io_u->xfer_buflen);
241 }
242
243 /*
244  * splice receive - transfer socket data into a pipe using splice, then map
245  * that pipe data into the io_u using vmsplice.
246  */
247 static int fio_netio_splice_in(struct thread_data *td, struct io_u *io_u)
248 {
249         int ret;
250
251         ret = splice_in(td, io_u);
252         if (ret > 0)
253                 return vmsplice_io_u_out(td, io_u, ret);
254
255         return ret;
256 }
257
258 /*
259  * splice transmit - map data from the io_u into a pipe by using vmsplice,
260  * then transfer that pipe to a socket using splice.
261  */
262 static int fio_netio_splice_out(struct thread_data *td, struct io_u *io_u)
263 {
264         int ret;
265
266         ret = vmsplice_io_u_in(td, io_u);
267         if (ret > 0)
268                 return splice_out(td, io_u, ret);
269
270         return ret;
271 }
272 #else
273 static int fio_netio_splice_in(struct thread_data *td, struct io_u *io_u)
274 {
275         errno = EOPNOTSUPP;
276         return -1;
277 }
278
279 static int fio_netio_splice_out(struct thread_data *td, struct io_u *io_u)
280 {
281         errno = EOPNOTSUPP;
282         return -1;
283 }
284 #endif
285
286 static int fio_netio_send(struct thread_data *td, struct io_u *io_u)
287 {
288         struct netio_data *nd = td->io_ops->data;
289         struct netio_options *o = td->eo;
290         int ret, flags = OS_MSG_DONTWAIT;
291
292         do {
293                 if (o->proto == FIO_TYPE_UDP) {
294                         struct sockaddr *to = (struct sockaddr *) &nd->addr;
295
296                         ret = sendto(io_u->file->fd, io_u->xfer_buf,
297                                         io_u->xfer_buflen, flags, to,
298                                         sizeof(*to));
299                 } else {
300                         /*
301                          * if we are going to write more, set MSG_MORE
302                          */
303 #ifdef MSG_MORE
304                         if (td->this_io_bytes[DDIR_WRITE] + io_u->xfer_buflen <
305                             td->o.size)
306                                 flags |= MSG_MORE;
307 #endif
308                         ret = send(io_u->file->fd, io_u->xfer_buf,
309                                         io_u->xfer_buflen, flags);
310                 }
311                 if (ret > 0)
312                         break;
313
314                 ret = poll_wait(td, io_u->file->fd, POLLOUT);
315                 if (ret <= 0)
316                         break;
317
318                 flags &= ~OS_MSG_DONTWAIT;
319         } while (1);
320
321         return ret;
322 }
323
324 static int is_udp_close(struct io_u *io_u, int len)
325 {
326         struct udp_close_msg *msg;
327
328         if (len != sizeof(struct udp_close_msg))
329                 return 0;
330
331         msg = io_u->xfer_buf;
332         if (ntohl(msg->magic) != FIO_LINK_CLOSE_MAGIC)
333                 return 0;
334         if (ntohl(msg->cmd) != FIO_LINK_CLOSE)
335                 return 0;
336
337         return 1;
338 }
339
340 static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
341 {
342         struct netio_data *nd = td->io_ops->data;
343         struct netio_options *o = td->eo;
344         int ret, flags = OS_MSG_DONTWAIT;
345
346         do {
347                 if (o->proto == FIO_TYPE_UDP) {
348                         fio_socklen_t len = sizeof(nd->addr);
349                         struct sockaddr *from = (struct sockaddr *) &nd->addr;
350
351                         ret = recvfrom(io_u->file->fd, io_u->xfer_buf,
352                                         io_u->xfer_buflen, flags, from, &len);
353                         if (is_udp_close(io_u, ret)) {
354                                 td->done = 1;
355                                 return 0;
356                         }
357                 } else {
358                         ret = recv(io_u->file->fd, io_u->xfer_buf,
359                                         io_u->xfer_buflen, flags);
360                 }
361                 if (ret > 0)
362                         break;
363
364                 ret = poll_wait(td, io_u->file->fd, POLLIN);
365                 if (ret <= 0)
366                         break;
367                 flags &= ~OS_MSG_DONTWAIT;
368                 flags |= MSG_WAITALL;
369         } while (1);
370
371         return ret;
372 }
373
374 static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
375 {
376         struct netio_data *nd = td->io_ops->data;
377         struct netio_options *o = td->eo;
378         int ret;
379
380         fio_ro_check(td, io_u);
381
382         if (io_u->ddir == DDIR_WRITE) {
383                 if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
384                     o->proto == FIO_TYPE_UNIX)
385                         ret = fio_netio_send(td, io_u);
386                 else
387                         ret = fio_netio_splice_out(td, io_u);
388         } else if (io_u->ddir == DDIR_READ) {
389                 if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
390                     o->proto == FIO_TYPE_UNIX)
391                         ret = fio_netio_recv(td, io_u);
392                 else
393                         ret = fio_netio_splice_in(td, io_u);
394         } else
395                 ret = 0;        /* must be a SYNC */
396
397         if (ret != (int) io_u->xfer_buflen) {
398                 if (ret >= 0) {
399                         io_u->resid = io_u->xfer_buflen - ret;
400                         io_u->error = 0;
401                         return FIO_Q_COMPLETED;
402                 } else {
403                         int err = errno;
404
405                         if (io_u->ddir == DDIR_WRITE && err == EMSGSIZE)
406                                 return FIO_Q_BUSY;
407
408                         io_u->error = err;
409                 }
410         }
411
412         if (io_u->error)
413                 td_verror(td, io_u->error, "xfer");
414
415         return FIO_Q_COMPLETED;
416 }
417
418 static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
419 {
420         struct netio_data *nd = td->io_ops->data;
421         struct netio_options *o = td->eo;
422         int type, domain;
423
424         if (o->proto == FIO_TYPE_TCP) {
425                 domain = AF_INET;
426                 type = SOCK_STREAM;
427         } else if (o->proto == FIO_TYPE_UDP) {
428                 domain = AF_INET;
429                 type = SOCK_DGRAM;
430         } else if (o->proto == FIO_TYPE_UNIX) {
431                 domain = AF_UNIX;
432                 type = SOCK_STREAM;
433         } else {
434                 log_err("fio: bad network type %d\n", o->proto);
435                 f->fd = -1;
436                 return 1;
437         }
438
439         f->fd = socket(domain, type, 0);
440         if (f->fd < 0) {
441                 td_verror(td, errno, "socket");
442                 return 1;
443         }
444
445         if (o->proto == FIO_TYPE_UDP)
446                 return 0;
447         else if (o->proto == FIO_TYPE_TCP) {
448                 fio_socklen_t len = sizeof(nd->addr);
449
450                 if (connect(f->fd, (struct sockaddr *) &nd->addr, len) < 0) {
451                         td_verror(td, errno, "connect");
452                         close(f->fd);
453                         return 1;
454                 }
455         } else {
456                 struct sockaddr_un *addr = &nd->addr_un;
457                 fio_socklen_t len;
458
459                 len = sizeof(addr->sun_family) + strlen(addr->sun_path) + 1;
460
461                 if (connect(f->fd, (struct sockaddr *) addr, len) < 0) {
462                         td_verror(td, errno, "connect");
463                         close(f->fd);
464                         return 1;
465                 }
466         }
467
468         return 0;
469 }
470
471 static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
472 {
473         struct netio_data *nd = td->io_ops->data;
474         struct netio_options *o = td->eo;
475         fio_socklen_t socklen = sizeof(nd->addr);
476
477         if (o->proto == FIO_TYPE_UDP) {
478                 f->fd = nd->listenfd;
479                 return 0;
480         }
481
482         log_info("fio: waiting for connection\n");
483
484         if (poll_wait(td, nd->listenfd, POLLIN) < 0)
485                 return 1;
486
487         f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr, &socklen);
488         if (f->fd < 0) {
489                 td_verror(td, errno, "accept");
490                 return 1;
491         }
492
493         return 0;
494 }
495
496 static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
497 {
498         int ret;
499         struct netio_options *o = td->eo;
500
501         if (o->listen)
502                 ret = fio_netio_accept(td, f);
503         else
504                 ret = fio_netio_connect(td, f);
505
506         if (ret)
507                 f->fd = -1;
508         return ret;
509 }
510
511 static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
512 {
513         struct netio_data *nd = td->io_ops->data;
514         struct udp_close_msg msg;
515         struct sockaddr *to = (struct sockaddr *) &nd->addr;
516         int ret;
517
518         msg.magic = htonl(FIO_LINK_CLOSE_MAGIC);
519         msg.cmd = htonl(FIO_LINK_CLOSE);
520
521         ret = sendto(f->fd, &msg, sizeof(msg), MSG_WAITALL, to,
522                         sizeof(nd->addr));
523         if (ret < 0)
524                 td_verror(td, errno, "sendto udp link close");
525 }
526
527 static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
528 {
529         struct netio_options *o = td->eo;
530
531         /*
532          * If this is an UDP connection, notify the receiver that we are
533          * closing down the link
534          */
535         if (o->proto == FIO_TYPE_UDP)
536                 fio_netio_udp_close(td, f);
537
538         return generic_close_file(td, f);
539 }
540
541 static int fio_netio_setup_connect_inet(struct thread_data *td,
542                                         const char *host, unsigned short port)
543 {
544         struct netio_data *nd = td->io_ops->data;
545
546         nd->addr.sin_family = AF_INET;
547         nd->addr.sin_port = htons(port);
548
549         if (inet_aton(host, &nd->addr.sin_addr) != 1) {
550                 struct hostent *hent;
551
552                 hent = gethostbyname(host);
553                 if (!hent) {
554                         td_verror(td, errno, "gethostbyname");
555                         return 1;
556                 }
557
558                 memcpy(&nd->addr.sin_addr, hent->h_addr, 4);
559         }
560
561         return 0;
562 }
563
564 static int fio_netio_setup_connect_unix(struct thread_data *td,
565                                         const char *path)
566 {
567         struct netio_data *nd = td->io_ops->data;
568         struct sockaddr_un *soun = &nd->addr_un;
569
570         soun->sun_family = AF_UNIX;
571         strcpy(soun->sun_path, path);
572         return 0;
573 }
574
575 static int fio_netio_setup_connect(struct thread_data *td)
576 {
577         struct netio_options *o = td->eo;
578
579         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
580                 return fio_netio_setup_connect_inet(td, td->o.filename,o->port);
581         else
582                 return fio_netio_setup_connect_unix(td, td->o.filename);
583 }
584
585 static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
586 {
587         struct netio_data *nd = td->io_ops->data;
588         struct sockaddr_un *addr = &nd->addr_un;
589         mode_t mode;
590         int len, fd;
591
592         fd = socket(AF_UNIX, SOCK_STREAM, 0);
593         if (fd < 0) {
594                 log_err("fio: socket: %s\n", strerror(errno));
595                 return -1;
596         }
597
598         mode = umask(000);
599
600         memset(addr, 0, sizeof(*addr));
601         addr->sun_family = AF_UNIX;
602         strcpy(addr->sun_path, path);
603         unlink(path);
604
605         len = sizeof(addr->sun_family) + strlen(path) + 1;
606
607         if (bind(fd, (struct sockaddr *) addr, len) < 0) {
608                 log_err("fio: bind: %s\n", strerror(errno));
609                 close(fd);
610                 return -1;
611         }
612
613         umask(mode);
614         nd->listenfd = fd;
615         return 0;
616 }
617
618 static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
619 {
620         struct netio_data *nd = td->io_ops->data;
621         struct netio_options *o = td->eo;
622         int fd, opt, type;
623
624         if (o->proto == FIO_TYPE_TCP)
625                 type = SOCK_STREAM;
626         else
627                 type = SOCK_DGRAM;
628
629         fd = socket(AF_INET, type, 0);
630         if (fd < 0) {
631                 td_verror(td, errno, "socket");
632                 return 1;
633         }
634
635         opt = 1;
636         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
637                 td_verror(td, errno, "setsockopt");
638                 return 1;
639         }
640 #ifdef SO_REUSEPORT
641         if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
642                 td_verror(td, errno, "setsockopt");
643                 return 1;
644         }
645 #endif
646
647         nd->addr.sin_family = AF_INET;
648         nd->addr.sin_addr.s_addr = htonl(INADDR_ANY);
649         nd->addr.sin_port = htons(port);
650
651         if (bind(fd, (struct sockaddr *) &nd->addr, sizeof(nd->addr)) < 0) {
652                 td_verror(td, errno, "bind");
653                 return 1;
654         }
655
656         nd->listenfd = fd;
657         return 0;
658 }
659
660 static int fio_netio_setup_listen(struct thread_data *td)
661 {
662         struct netio_data *nd = td->io_ops->data;
663         struct netio_options *o = td->eo;
664         int ret;
665
666         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
667                 ret = fio_netio_setup_listen_inet(td, o->port);
668         else
669                 ret = fio_netio_setup_listen_unix(td, td->o.filename);
670
671         if (ret)
672                 return ret;
673         if (o->proto == FIO_TYPE_UDP)
674                 return 0;
675
676         if (listen(nd->listenfd, 10) < 0) {
677                 td_verror(td, errno, "listen");
678                 nd->listenfd = -1;
679                 return 1;
680         }
681
682         return 0;
683 }
684
685 static int fio_netio_init(struct thread_data *td)
686 {
687         struct netio_options *o = td->eo;
688         int ret;
689
690         if (td_random(td)) {
691                 log_err("fio: network IO can't be random\n");
692                 return 1;
693         }
694
695         if (o->proto == FIO_TYPE_UNIX && o->port) {
696                 log_err("fio: network IO port not valid with unix socket\n");
697                 return 1;
698         } else if (o->proto != FIO_TYPE_UNIX && !o->port) {
699                 log_err("fio: network IO requires port for tcp or udp\n");
700                 return 1;
701         }
702
703         if (o->proto != FIO_TYPE_TCP) {
704                 if (o->listen) {
705                         log_err("fio: listen only valid for TCP proto IO\n");
706                         return 1;
707                 }
708                 if (td_rw(td)) {
709                         log_err("fio: datagram network connections must be"
710                                    " read OR write\n");
711                         return 1;
712                 }
713                 if (o->proto == FIO_TYPE_UNIX && !td->o.filename) {
714                         log_err("fio: UNIX sockets need host/filename\n");
715                         return 1;
716                 }
717                 o->listen = td_read(td);
718         }
719
720         if (o->proto != FIO_TYPE_UNIX && o->listen && td->o.filename) {
721                 log_err("fio: hostname not valid for inbound network IO\n");
722                 return 1;
723         }
724
725         if (o->listen)
726                 ret = fio_netio_setup_listen(td);
727         else
728                 ret = fio_netio_setup_connect(td);
729
730         return ret;
731 }
732
733 static void fio_netio_cleanup(struct thread_data *td)
734 {
735         struct netio_data *nd = td->io_ops->data;
736
737         if (nd) {
738                 if (nd->listenfd != -1)
739                         close(nd->listenfd);
740                 if (nd->pipes[0] != -1)
741                         close(nd->pipes[0]);
742                 if (nd->pipes[1] != -1)
743                         close(nd->pipes[1]);
744
745                 free(nd);
746         }
747 }
748
749 static int fio_netio_setup(struct thread_data *td)
750 {
751         struct netio_data *nd;
752
753         if (!td->files_index) {
754                 add_file(td, td->o.filename ?: "net");
755                 td->o.nr_files = td->o.nr_files ?: 1;
756         }
757
758         if (!td->io_ops->data) {
759                 nd = malloc(sizeof(*nd));;
760
761                 memset(nd, 0, sizeof(*nd));
762                 nd->listenfd = -1;
763                 nd->pipes[0] = nd->pipes[1] = -1;
764                 td->io_ops->data = nd;
765         }
766
767         return 0;
768 }
769
770 #ifdef FIO_HAVE_SPLICE
771 static int fio_netio_setup_splice(struct thread_data *td)
772 {
773         struct netio_data *nd;
774
775         fio_netio_setup(td);
776
777         nd = td->io_ops->data;
778         if (nd) {
779                 if (pipe(nd->pipes) < 0)
780                         return 1;
781
782                 nd->use_splice = 1;
783                 return 0;
784         }
785
786         return 1;
787 }
788
789 static struct ioengine_ops ioengine_splice = {
790         .name                   = "netsplice",
791         .version                = FIO_IOOPS_VERSION,
792         .prep                   = fio_netio_prep,
793         .queue                  = fio_netio_queue,
794         .setup                  = fio_netio_setup_splice,
795         .init                   = fio_netio_init,
796         .cleanup                = fio_netio_cleanup,
797         .open_file              = fio_netio_open_file,
798         .close_file             = generic_close_file,
799         .options                = options,
800         .option_struct_size     = sizeof(struct netio_options),
801         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
802                                   FIO_SIGTERM | FIO_PIPEIO,
803 };
804 #endif
805
806 static struct ioengine_ops ioengine_rw = {
807         .name                   = "net",
808         .version                = FIO_IOOPS_VERSION,
809         .prep                   = fio_netio_prep,
810         .queue                  = fio_netio_queue,
811         .setup                  = fio_netio_setup,
812         .init                   = fio_netio_init,
813         .cleanup                = fio_netio_cleanup,
814         .open_file              = fio_netio_open_file,
815         .close_file             = fio_netio_close_file,
816         .options                = options,
817         .option_struct_size     = sizeof(struct netio_options),
818         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
819                                   FIO_SIGTERM | FIO_PIPEIO,
820 };
821
822 static int str_hostname_cb(void *data, const char *input)
823 {
824         struct netio_options *o = data;
825
826         if (o->td->o.filename)
827                 free(o->td->o.filename);
828         o->td->o.filename = strdup(input);
829         return 0;
830 }
831
832 static void fio_init fio_netio_register(void)
833 {
834         register_ioengine(&ioengine_rw);
835 #ifdef FIO_HAVE_SPLICE
836         register_ioengine(&ioengine_splice);
837 #endif
838 }
839
840 static void fio_exit fio_netio_unregister(void)
841 {
842         unregister_ioengine(&ioengine_rw);
843 #ifdef FIO_HAVE_SPLICE
844         unregister_ioengine(&ioengine_splice);
845 #endif
846 }