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