net: exit on WAITALL and !ret
[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 = "User 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                 else if (!ret && (flags & MSG_WAITALL))
364                         break;
365
366                 ret = poll_wait(td, io_u->file->fd, POLLIN);
367                 if (ret <= 0)
368                         break;
369                 flags &= ~OS_MSG_DONTWAIT;
370                 flags |= MSG_WAITALL;
371         } while (1);
372
373         return ret;
374 }
375
376 static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
377 {
378         struct netio_data *nd = td->io_ops->data;
379         struct netio_options *o = td->eo;
380         int ret;
381
382         fio_ro_check(td, io_u);
383
384         if (io_u->ddir == DDIR_WRITE) {
385                 if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
386                     o->proto == FIO_TYPE_UNIX)
387                         ret = fio_netio_send(td, io_u);
388                 else
389                         ret = fio_netio_splice_out(td, io_u);
390         } else if (io_u->ddir == DDIR_READ) {
391                 if (!nd->use_splice || o->proto == FIO_TYPE_UDP ||
392                     o->proto == FIO_TYPE_UNIX)
393                         ret = fio_netio_recv(td, io_u);
394                 else
395                         ret = fio_netio_splice_in(td, io_u);
396         } else
397                 ret = 0;        /* must be a SYNC */
398
399         if (ret != (int) io_u->xfer_buflen) {
400                 if (ret >= 0) {
401                         io_u->resid = io_u->xfer_buflen - ret;
402                         io_u->error = 0;
403                         return FIO_Q_COMPLETED;
404                 } else {
405                         int err = errno;
406
407                         if (io_u->ddir == DDIR_WRITE && err == EMSGSIZE)
408                                 return FIO_Q_BUSY;
409
410                         io_u->error = err;
411                 }
412         }
413
414         if (io_u->error)
415                 td_verror(td, io_u->error, "xfer");
416
417         return FIO_Q_COMPLETED;
418 }
419
420 static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
421 {
422         struct netio_data *nd = td->io_ops->data;
423         struct netio_options *o = td->eo;
424         int type, domain;
425
426         if (o->proto == FIO_TYPE_TCP) {
427                 domain = AF_INET;
428                 type = SOCK_STREAM;
429         } else if (o->proto == FIO_TYPE_UDP) {
430                 domain = AF_INET;
431                 type = SOCK_DGRAM;
432         } else if (o->proto == FIO_TYPE_UNIX) {
433                 domain = AF_UNIX;
434                 type = SOCK_STREAM;
435         } else {
436                 log_err("fio: bad network type %d\n", o->proto);
437                 f->fd = -1;
438                 return 1;
439         }
440
441         f->fd = socket(domain, type, 0);
442         if (f->fd < 0) {
443                 td_verror(td, errno, "socket");
444                 return 1;
445         }
446
447         if (o->proto == FIO_TYPE_UDP)
448                 return 0;
449         else if (o->proto == FIO_TYPE_TCP) {
450                 fio_socklen_t len = sizeof(nd->addr);
451
452                 if (connect(f->fd, (struct sockaddr *) &nd->addr, len) < 0) {
453                         td_verror(td, errno, "connect");
454                         close(f->fd);
455                         return 1;
456                 }
457         } else {
458                 struct sockaddr_un *addr = &nd->addr_un;
459                 fio_socklen_t len;
460
461                 len = sizeof(addr->sun_family) + strlen(addr->sun_path) + 1;
462
463                 if (connect(f->fd, (struct sockaddr *) addr, len) < 0) {
464                         td_verror(td, errno, "connect");
465                         close(f->fd);
466                         return 1;
467                 }
468         }
469
470         return 0;
471 }
472
473 static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
474 {
475         struct netio_data *nd = td->io_ops->data;
476         struct netio_options *o = td->eo;
477         fio_socklen_t socklen = sizeof(nd->addr);
478
479         if (o->proto == FIO_TYPE_UDP) {
480                 f->fd = nd->listenfd;
481                 return 0;
482         }
483
484         log_info("fio: waiting for connection\n");
485
486         if (poll_wait(td, nd->listenfd, POLLIN) < 0)
487                 return 1;
488
489         f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr, &socklen);
490         if (f->fd < 0) {
491                 td_verror(td, errno, "accept");
492                 return 1;
493         }
494
495         return 0;
496 }
497
498 static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
499 {
500         int ret;
501         struct netio_options *o = td->eo;
502
503         if (o->listen)
504                 ret = fio_netio_accept(td, f);
505         else
506                 ret = fio_netio_connect(td, f);
507
508         if (ret)
509                 f->fd = -1;
510         return ret;
511 }
512
513 static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
514 {
515         struct netio_data *nd = td->io_ops->data;
516         struct udp_close_msg msg;
517         struct sockaddr *to = (struct sockaddr *) &nd->addr;
518         int ret;
519
520         msg.magic = htonl(FIO_LINK_CLOSE_MAGIC);
521         msg.cmd = htonl(FIO_LINK_CLOSE);
522
523         ret = sendto(f->fd, &msg, sizeof(msg), MSG_WAITALL, to,
524                         sizeof(nd->addr));
525         if (ret < 0)
526                 td_verror(td, errno, "sendto udp link close");
527 }
528
529 static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
530 {
531         struct netio_options *o = td->eo;
532
533         /*
534          * If this is an UDP connection, notify the receiver that we are
535          * closing down the link
536          */
537         if (o->proto == FIO_TYPE_UDP)
538                 fio_netio_udp_close(td, f);
539
540         return generic_close_file(td, f);
541 }
542
543 static int fio_netio_setup_connect_inet(struct thread_data *td,
544                                         const char *host, unsigned short port)
545 {
546         struct netio_data *nd = td->io_ops->data;
547
548         if (!host) {
549                 log_err("fio: connect with no host to connect to.\n");
550                 if (td_read(td))
551                         log_err("fio: did you forget to set 'listen'?\n");
552
553                 td_verror(td, EINVAL, "no hostname= set");
554                 return 1;
555         }
556
557         nd->addr.sin_family = AF_INET;
558         nd->addr.sin_port = htons(port);
559
560         if (inet_aton(host, &nd->addr.sin_addr) != 1) {
561                 struct hostent *hent;
562
563                 hent = gethostbyname(host);
564                 if (!hent) {
565                         td_verror(td, errno, "gethostbyname");
566                         return 1;
567                 }
568
569                 memcpy(&nd->addr.sin_addr, hent->h_addr, 4);
570         }
571
572         return 0;
573 }
574
575 static int fio_netio_setup_connect_unix(struct thread_data *td,
576                                         const char *path)
577 {
578         struct netio_data *nd = td->io_ops->data;
579         struct sockaddr_un *soun = &nd->addr_un;
580
581         soun->sun_family = AF_UNIX;
582         strcpy(soun->sun_path, path);
583         return 0;
584 }
585
586 static int fio_netio_setup_connect(struct thread_data *td)
587 {
588         struct netio_options *o = td->eo;
589
590         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
591                 return fio_netio_setup_connect_inet(td, td->o.filename,o->port);
592         else
593                 return fio_netio_setup_connect_unix(td, td->o.filename);
594 }
595
596 static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
597 {
598         struct netio_data *nd = td->io_ops->data;
599         struct sockaddr_un *addr = &nd->addr_un;
600         mode_t mode;
601         int len, fd;
602
603         fd = socket(AF_UNIX, SOCK_STREAM, 0);
604         if (fd < 0) {
605                 log_err("fio: socket: %s\n", strerror(errno));
606                 return -1;
607         }
608
609         mode = umask(000);
610
611         memset(addr, 0, sizeof(*addr));
612         addr->sun_family = AF_UNIX;
613         strcpy(addr->sun_path, path);
614         unlink(path);
615
616         len = sizeof(addr->sun_family) + strlen(path) + 1;
617
618         if (bind(fd, (struct sockaddr *) addr, len) < 0) {
619                 log_err("fio: bind: %s\n", strerror(errno));
620                 close(fd);
621                 return -1;
622         }
623
624         umask(mode);
625         nd->listenfd = fd;
626         return 0;
627 }
628
629 static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
630 {
631         struct netio_data *nd = td->io_ops->data;
632         struct netio_options *o = td->eo;
633         int fd, opt, type;
634
635         if (o->proto == FIO_TYPE_TCP)
636                 type = SOCK_STREAM;
637         else
638                 type = SOCK_DGRAM;
639
640         fd = socket(AF_INET, type, 0);
641         if (fd < 0) {
642                 td_verror(td, errno, "socket");
643                 return 1;
644         }
645
646         opt = 1;
647         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
648                 td_verror(td, errno, "setsockopt");
649                 return 1;
650         }
651 #ifdef SO_REUSEPORT
652         if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
653                 td_verror(td, errno, "setsockopt");
654                 return 1;
655         }
656 #endif
657
658         nd->addr.sin_family = AF_INET;
659         nd->addr.sin_addr.s_addr = htonl(INADDR_ANY);
660         nd->addr.sin_port = htons(port);
661
662         if (bind(fd, (struct sockaddr *) &nd->addr, sizeof(nd->addr)) < 0) {
663                 td_verror(td, errno, "bind");
664                 return 1;
665         }
666
667         nd->listenfd = fd;
668         return 0;
669 }
670
671 static int fio_netio_setup_listen(struct thread_data *td)
672 {
673         struct netio_data *nd = td->io_ops->data;
674         struct netio_options *o = td->eo;
675         int ret;
676
677         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
678                 ret = fio_netio_setup_listen_inet(td, o->port);
679         else
680                 ret = fio_netio_setup_listen_unix(td, td->o.filename);
681
682         if (ret)
683                 return ret;
684         if (o->proto == FIO_TYPE_UDP)
685                 return 0;
686
687         if (listen(nd->listenfd, 10) < 0) {
688                 td_verror(td, errno, "listen");
689                 nd->listenfd = -1;
690                 return 1;
691         }
692
693         return 0;
694 }
695
696 static int fio_netio_init(struct thread_data *td)
697 {
698         struct netio_options *o = td->eo;
699         int ret;
700
701 #ifdef WIN32
702         WSADATA wsd;
703         WSAStartup(MAKEWORD(2,2), &wsd);
704 #endif
705
706         if (td_random(td)) {
707                 log_err("fio: network IO can't be random\n");
708                 return 1;
709         }
710
711         if (o->proto == FIO_TYPE_UNIX && o->port) {
712                 log_err("fio: network IO port not valid with unix socket\n");
713                 return 1;
714         } else if (o->proto != FIO_TYPE_UNIX && !o->port) {
715                 log_err("fio: network IO requires port for tcp or udp\n");
716                 return 1;
717         }
718
719         if (o->proto != FIO_TYPE_TCP) {
720                 if (o->listen) {
721                         log_err("fio: listen only valid for TCP proto IO\n");
722                         return 1;
723                 }
724                 if (td_rw(td)) {
725                         log_err("fio: datagram network connections must be"
726                                    " read OR write\n");
727                         return 1;
728                 }
729                 if (o->proto == FIO_TYPE_UNIX && !td->o.filename) {
730                         log_err("fio: UNIX sockets need host/filename\n");
731                         return 1;
732                 }
733                 o->listen = td_read(td);
734         }
735
736         if (o->proto != FIO_TYPE_UNIX && o->listen && td->o.filename) {
737                 log_err("fio: hostname not valid for inbound network IO\n");
738                 return 1;
739         }
740
741         if (o->listen)
742                 ret = fio_netio_setup_listen(td);
743         else
744                 ret = fio_netio_setup_connect(td);
745
746         return ret;
747 }
748
749 static void fio_netio_cleanup(struct thread_data *td)
750 {
751         struct netio_data *nd = td->io_ops->data;
752
753         if (nd) {
754                 if (nd->listenfd != -1)
755                         close(nd->listenfd);
756                 if (nd->pipes[0] != -1)
757                         close(nd->pipes[0]);
758                 if (nd->pipes[1] != -1)
759                         close(nd->pipes[1]);
760
761                 free(nd);
762         }
763 }
764
765 static int fio_netio_setup(struct thread_data *td)
766 {
767         struct netio_data *nd;
768
769         if (!td->files_index) {
770                 add_file(td, td->o.filename ?: "net");
771                 td->o.nr_files = td->o.nr_files ?: 1;
772         }
773
774         if (!td->io_ops->data) {
775                 nd = malloc(sizeof(*nd));;
776
777                 memset(nd, 0, sizeof(*nd));
778                 nd->listenfd = -1;
779                 nd->pipes[0] = nd->pipes[1] = -1;
780                 td->io_ops->data = nd;
781         }
782
783         return 0;
784 }
785
786 #ifdef FIO_HAVE_SPLICE
787 static int fio_netio_setup_splice(struct thread_data *td)
788 {
789         struct netio_data *nd;
790
791         fio_netio_setup(td);
792
793         nd = td->io_ops->data;
794         if (nd) {
795                 if (pipe(nd->pipes) < 0)
796                         return 1;
797
798                 nd->use_splice = 1;
799                 return 0;
800         }
801
802         return 1;
803 }
804
805 static struct ioengine_ops ioengine_splice = {
806         .name                   = "netsplice",
807         .version                = FIO_IOOPS_VERSION,
808         .prep                   = fio_netio_prep,
809         .queue                  = fio_netio_queue,
810         .setup                  = fio_netio_setup_splice,
811         .init                   = fio_netio_init,
812         .cleanup                = fio_netio_cleanup,
813         .open_file              = fio_netio_open_file,
814         .close_file             = generic_close_file,
815         .options                = options,
816         .option_struct_size     = sizeof(struct netio_options),
817         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
818                                   FIO_SIGTERM | FIO_PIPEIO,
819 };
820 #endif
821
822 static struct ioengine_ops ioengine_rw = {
823         .name                   = "net",
824         .version                = FIO_IOOPS_VERSION,
825         .prep                   = fio_netio_prep,
826         .queue                  = fio_netio_queue,
827         .setup                  = fio_netio_setup,
828         .init                   = fio_netio_init,
829         .cleanup                = fio_netio_cleanup,
830         .open_file              = fio_netio_open_file,
831         .close_file             = fio_netio_close_file,
832         .options                = options,
833         .option_struct_size     = sizeof(struct netio_options),
834         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
835                                   FIO_SIGTERM | FIO_PIPEIO,
836 };
837
838 static int str_hostname_cb(void *data, const char *input)
839 {
840         struct netio_options *o = data;
841
842         if (o->td->o.filename)
843                 free(o->td->o.filename);
844         o->td->o.filename = strdup(input);
845         return 0;
846 }
847
848 static void fio_init fio_netio_register(void)
849 {
850         register_ioengine(&ioengine_rw);
851 #ifdef FIO_HAVE_SPLICE
852         register_ioengine(&ioengine_splice);
853 #endif
854 }
855
856 static void fio_exit fio_netio_unregister(void)
857 {
858         unregister_ioengine(&ioengine_rw);
859 #ifdef FIO_HAVE_SPLICE
860         unregister_ioengine(&ioengine_splice);
861 #endif
862 }