373821b2142bb8d3a7428179d4c4218ff05118e1
[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         int state;
479
480         if (o->proto == FIO_TYPE_UDP) {
481                 f->fd = nd->listenfd;
482                 return 0;
483         }
484
485         state = td->runstate;
486         td_set_runstate(td, TD_SETTING_UP);
487
488         log_info("fio: waiting for connection\n");
489
490         if (poll_wait(td, nd->listenfd, POLLIN) < 0)
491                 goto err;
492
493         f->fd = accept(nd->listenfd, (struct sockaddr *) &nd->addr, &socklen);
494         if (f->fd < 0) {
495                 td_verror(td, errno, "accept");
496                 goto err;
497         }
498
499         td_set_runstate(td, state);
500         return 0;
501 err:
502         td_set_runstate(td, state);
503         return 1;
504 }
505
506 static int fio_netio_open_file(struct thread_data *td, struct fio_file *f)
507 {
508         int ret;
509         struct netio_options *o = td->eo;
510
511         if (o->listen)
512                 ret = fio_netio_accept(td, f);
513         else
514                 ret = fio_netio_connect(td, f);
515
516         if (ret)
517                 f->fd = -1;
518         return ret;
519 }
520
521 static void fio_netio_udp_close(struct thread_data *td, struct fio_file *f)
522 {
523         struct netio_data *nd = td->io_ops->data;
524         struct udp_close_msg msg;
525         struct sockaddr *to = (struct sockaddr *) &nd->addr;
526         int ret;
527
528         msg.magic = htonl(FIO_LINK_CLOSE_MAGIC);
529         msg.cmd = htonl(FIO_LINK_CLOSE);
530
531         ret = sendto(f->fd, &msg, sizeof(msg), MSG_WAITALL, to,
532                         sizeof(nd->addr));
533         if (ret < 0)
534                 td_verror(td, errno, "sendto udp link close");
535 }
536
537 static int fio_netio_close_file(struct thread_data *td, struct fio_file *f)
538 {
539         struct netio_options *o = td->eo;
540
541         /*
542          * If this is an UDP connection, notify the receiver that we are
543          * closing down the link
544          */
545         if (o->proto == FIO_TYPE_UDP)
546                 fio_netio_udp_close(td, f);
547
548         return generic_close_file(td, f);
549 }
550
551 static int fio_netio_setup_connect_inet(struct thread_data *td,
552                                         const char *host, unsigned short port)
553 {
554         struct netio_data *nd = td->io_ops->data;
555
556         if (!host) {
557                 log_err("fio: connect with no host to connect to.\n");
558                 if (td_read(td))
559                         log_err("fio: did you forget to set 'listen'?\n");
560
561                 td_verror(td, EINVAL, "no hostname= set");
562                 return 1;
563         }
564
565         nd->addr.sin_family = AF_INET;
566         nd->addr.sin_port = htons(port);
567
568         if (inet_aton(host, &nd->addr.sin_addr) != 1) {
569                 struct hostent *hent;
570
571                 hent = gethostbyname(host);
572                 if (!hent) {
573                         td_verror(td, errno, "gethostbyname");
574                         return 1;
575                 }
576
577                 memcpy(&nd->addr.sin_addr, hent->h_addr, 4);
578         }
579
580         return 0;
581 }
582
583 static int fio_netio_setup_connect_unix(struct thread_data *td,
584                                         const char *path)
585 {
586         struct netio_data *nd = td->io_ops->data;
587         struct sockaddr_un *soun = &nd->addr_un;
588
589         soun->sun_family = AF_UNIX;
590         strcpy(soun->sun_path, path);
591         return 0;
592 }
593
594 static int fio_netio_setup_connect(struct thread_data *td)
595 {
596         struct netio_options *o = td->eo;
597
598         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
599                 return fio_netio_setup_connect_inet(td, td->o.filename,o->port);
600         else
601                 return fio_netio_setup_connect_unix(td, td->o.filename);
602 }
603
604 static int fio_netio_setup_listen_unix(struct thread_data *td, const char *path)
605 {
606         struct netio_data *nd = td->io_ops->data;
607         struct sockaddr_un *addr = &nd->addr_un;
608         mode_t mode;
609         int len, fd;
610
611         fd = socket(AF_UNIX, SOCK_STREAM, 0);
612         if (fd < 0) {
613                 log_err("fio: socket: %s\n", strerror(errno));
614                 return -1;
615         }
616
617         mode = umask(000);
618
619         memset(addr, 0, sizeof(*addr));
620         addr->sun_family = AF_UNIX;
621         strcpy(addr->sun_path, path);
622         unlink(path);
623
624         len = sizeof(addr->sun_family) + strlen(path) + 1;
625
626         if (bind(fd, (struct sockaddr *) addr, len) < 0) {
627                 log_err("fio: bind: %s\n", strerror(errno));
628                 close(fd);
629                 return -1;
630         }
631
632         umask(mode);
633         nd->listenfd = fd;
634         return 0;
635 }
636
637 static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
638 {
639         struct netio_data *nd = td->io_ops->data;
640         struct netio_options *o = td->eo;
641         int fd, opt, type;
642
643         if (o->proto == FIO_TYPE_TCP)
644                 type = SOCK_STREAM;
645         else
646                 type = SOCK_DGRAM;
647
648         fd = socket(AF_INET, type, 0);
649         if (fd < 0) {
650                 td_verror(td, errno, "socket");
651                 return 1;
652         }
653
654         opt = 1;
655         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
656                 td_verror(td, errno, "setsockopt");
657                 return 1;
658         }
659 #ifdef SO_REUSEPORT
660         if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
661                 td_verror(td, errno, "setsockopt");
662                 return 1;
663         }
664 #endif
665
666         nd->addr.sin_family = AF_INET;
667         nd->addr.sin_addr.s_addr = htonl(INADDR_ANY);
668         nd->addr.sin_port = htons(port);
669
670         if (bind(fd, (struct sockaddr *) &nd->addr, sizeof(nd->addr)) < 0) {
671                 td_verror(td, errno, "bind");
672                 return 1;
673         }
674
675         nd->listenfd = fd;
676         return 0;
677 }
678
679 static int fio_netio_setup_listen(struct thread_data *td)
680 {
681         struct netio_data *nd = td->io_ops->data;
682         struct netio_options *o = td->eo;
683         int ret;
684
685         if (o->proto == FIO_TYPE_UDP || o->proto == FIO_TYPE_TCP)
686                 ret = fio_netio_setup_listen_inet(td, o->port);
687         else
688                 ret = fio_netio_setup_listen_unix(td, td->o.filename);
689
690         if (ret)
691                 return ret;
692         if (o->proto == FIO_TYPE_UDP)
693                 return 0;
694
695         if (listen(nd->listenfd, 10) < 0) {
696                 td_verror(td, errno, "listen");
697                 nd->listenfd = -1;
698                 return 1;
699         }
700
701         return 0;
702 }
703
704 static int fio_netio_init(struct thread_data *td)
705 {
706         struct netio_options *o = td->eo;
707         int ret;
708
709 #ifdef WIN32
710         WSADATA wsd;
711         WSAStartup(MAKEWORD(2,2), &wsd);
712 #endif
713
714         if (td_random(td)) {
715                 log_err("fio: network IO can't be random\n");
716                 return 1;
717         }
718
719         if (o->proto == FIO_TYPE_UNIX && o->port) {
720                 log_err("fio: network IO port not valid with unix socket\n");
721                 return 1;
722         } else if (o->proto != FIO_TYPE_UNIX && !o->port) {
723                 log_err("fio: network IO requires port for tcp or udp\n");
724                 return 1;
725         }
726
727         if (o->proto != FIO_TYPE_TCP) {
728                 if (o->listen) {
729                         log_err("fio: listen only valid for TCP proto IO\n");
730                         return 1;
731                 }
732                 if (td_rw(td)) {
733                         log_err("fio: datagram network connections must be"
734                                    " read OR write\n");
735                         return 1;
736                 }
737                 if (o->proto == FIO_TYPE_UNIX && !td->o.filename) {
738                         log_err("fio: UNIX sockets need host/filename\n");
739                         return 1;
740                 }
741                 o->listen = td_read(td);
742         }
743
744         if (o->proto != FIO_TYPE_UNIX && o->listen && td->o.filename) {
745                 log_err("fio: hostname not valid for inbound network IO\n");
746                 return 1;
747         }
748
749         if (o->listen)
750                 ret = fio_netio_setup_listen(td);
751         else
752                 ret = fio_netio_setup_connect(td);
753
754         return ret;
755 }
756
757 static void fio_netio_cleanup(struct thread_data *td)
758 {
759         struct netio_data *nd = td->io_ops->data;
760
761         if (nd) {
762                 if (nd->listenfd != -1)
763                         close(nd->listenfd);
764                 if (nd->pipes[0] != -1)
765                         close(nd->pipes[0]);
766                 if (nd->pipes[1] != -1)
767                         close(nd->pipes[1]);
768
769                 free(nd);
770         }
771 }
772
773 static int fio_netio_setup(struct thread_data *td)
774 {
775         struct netio_data *nd;
776
777         if (!td->files_index) {
778                 add_file(td, td->o.filename ?: "net");
779                 td->o.nr_files = td->o.nr_files ?: 1;
780         }
781
782         if (!td->io_ops->data) {
783                 nd = malloc(sizeof(*nd));;
784
785                 memset(nd, 0, sizeof(*nd));
786                 nd->listenfd = -1;
787                 nd->pipes[0] = nd->pipes[1] = -1;
788                 td->io_ops->data = nd;
789         }
790
791         return 0;
792 }
793
794 #ifdef FIO_HAVE_SPLICE
795 static int fio_netio_setup_splice(struct thread_data *td)
796 {
797         struct netio_data *nd;
798
799         fio_netio_setup(td);
800
801         nd = td->io_ops->data;
802         if (nd) {
803                 if (pipe(nd->pipes) < 0)
804                         return 1;
805
806                 nd->use_splice = 1;
807                 return 0;
808         }
809
810         return 1;
811 }
812
813 static struct ioengine_ops ioengine_splice = {
814         .name                   = "netsplice",
815         .version                = FIO_IOOPS_VERSION,
816         .prep                   = fio_netio_prep,
817         .queue                  = fio_netio_queue,
818         .setup                  = fio_netio_setup_splice,
819         .init                   = fio_netio_init,
820         .cleanup                = fio_netio_cleanup,
821         .open_file              = fio_netio_open_file,
822         .close_file             = generic_close_file,
823         .options                = options,
824         .option_struct_size     = sizeof(struct netio_options),
825         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
826                                   FIO_SIGTERM | FIO_PIPEIO,
827 };
828 #endif
829
830 static struct ioengine_ops ioengine_rw = {
831         .name                   = "net",
832         .version                = FIO_IOOPS_VERSION,
833         .prep                   = fio_netio_prep,
834         .queue                  = fio_netio_queue,
835         .setup                  = fio_netio_setup,
836         .init                   = fio_netio_init,
837         .cleanup                = fio_netio_cleanup,
838         .open_file              = fio_netio_open_file,
839         .close_file             = fio_netio_close_file,
840         .options                = options,
841         .option_struct_size     = sizeof(struct netio_options),
842         .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
843                                   FIO_SIGTERM | FIO_PIPEIO,
844 };
845
846 static int str_hostname_cb(void *data, const char *input)
847 {
848         struct netio_options *o = data;
849
850         if (o->td->o.filename)
851                 free(o->td->o.filename);
852         o->td->o.filename = strdup(input);
853         return 0;
854 }
855
856 static void fio_init fio_netio_register(void)
857 {
858         register_ioengine(&ioengine_rw);
859 #ifdef FIO_HAVE_SPLICE
860         register_ioengine(&ioengine_splice);
861 #endif
862 }
863
864 static void fio_exit fio_netio_unregister(void)
865 {
866         unregister_ioengine(&ioengine_rw);
867 #ifdef FIO_HAVE_SPLICE
868         unregister_ioengine(&ioengine_splice);
869 #endif
870 }