[PATCH] Network engine updates
[fio.git] / engines / net.c
1 /*
2  * Transfer data over the net. Pretty basic setup, will only support
3  * 1 file per thread/job.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <assert.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 #include <netdb.h>
13
14 #include "../fio.h"
15 #include "../os.h"
16
17 struct net_data {
18         int send_to_net;
19         struct io_u *last_io_u;
20 };
21
22 static int fio_netio_getevents(struct thread_data *td, int fio_unused min,
23                                 int max, struct timespec fio_unused *t)
24 {
25         assert(max <= 1);
26
27         /*
28          * we can only have one finished io_u for sync io, since the depth
29          * is always 1
30          */
31         if (list_empty(&td->io_u_busylist))
32                 return 0;
33
34         return 1;
35 }
36
37 static struct io_u *fio_netio_event(struct thread_data *td, int event)
38 {
39         struct net_data *nd = td->io_ops->data;
40
41         assert(event == 0);
42
43         return nd->last_io_u;
44 }
45
46 static int fio_netio_prep(struct thread_data *td, struct io_u *io_u)
47 {
48         struct net_data *nd = td->io_ops->data;
49         struct fio_file *f = io_u->file;
50
51         if (nd->send_to_net) {
52                 if (io_u->ddir == DDIR_READ) {
53                         td_verror(td, EINVAL);
54                         return 1;
55                 }
56         } else {
57                 if (io_u->ddir == DDIR_WRITE) {
58                         td_verror(td, EINVAL);
59                         return 1;
60                 }
61         }
62
63         if (io_u->ddir == DDIR_SYNC)
64                 return 0;
65         if (io_u->offset == f->last_completed_pos)
66                 return 0;
67
68         /*
69          * If offset is different from last end position, it's a seek.
70          * As network io is purely sequential, we don't allow seeks.
71          */
72         td_verror(td, EINVAL);
73         return 1;
74 }
75
76 static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
77 {
78         struct net_data *nd = td->io_ops->data;
79         struct fio_file *f = io_u->file;
80         unsigned int ret = 0;
81
82         if (io_u->ddir == DDIR_WRITE)
83                 ret = write(f->fd, io_u->buf, io_u->buflen);
84         else if (io_u->ddir == DDIR_READ)
85                 ret = read(f->fd, io_u->buf, io_u->buflen);
86
87         if (ret != io_u->buflen) {
88                 if (ret > 0) {
89                         io_u->resid = io_u->buflen - ret;
90                         io_u->error = EIO;
91                 } else
92                         io_u->error = errno;
93         }
94
95         if (!io_u->error)
96                 nd->last_io_u = io_u;
97
98         return io_u->error;
99 }
100
101 static int fio_netio_setup_connect(struct thread_data *td, const char *host,
102                                    unsigned short port)
103 {
104         struct sockaddr_in addr;
105         struct fio_file *f;
106
107         memset(&addr, 0, sizeof(addr));
108         addr.sin_family = AF_INET;
109         addr.sin_port = htons(port);
110
111         if (inet_aton(host, &addr.sin_addr) != 1) {
112                 struct hostent *hent = gethostbyname(host);
113
114                 if (!hent) {
115                         td_vmsg(td, errno, "gethostbyname");
116                         return 1;
117                 }
118
119                 memcpy(&addr.sin_addr, hent->h_addr, 4);
120         }
121
122         f = &td->files[0];
123
124         f->fd = socket(AF_INET, SOCK_STREAM, 0);
125         if (f->fd < 0) {
126                 td_vmsg(td, errno, "socket");
127                 return 1;
128         }
129
130         if (connect(f->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
131                 td_vmsg(td, errno, "connect");
132                 return 1;
133         }
134
135         return 0;
136
137 }
138
139 static int fio_netio_setup_listen(struct thread_data *td, unsigned short port)
140 {
141         struct sockaddr_in addr;
142         socklen_t socklen;
143         int fd, opt;
144
145         fd = socket(AF_INET, SOCK_STREAM, 0);
146         if (fd < 0) {
147                 td_vmsg(td, errno, "socket");
148                 return 1;
149         }
150
151         opt = 1;
152         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
153                 td_vmsg(td, errno, "setsockopt");
154                 return 1;
155         }
156
157         memset(&addr, 0, sizeof(addr));
158         addr.sin_family = AF_INET;
159         addr.sin_addr.s_addr = htonl(INADDR_ANY);
160         addr.sin_port = htons(port);
161
162         if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
163                 td_vmsg(td, errno, "bind");
164                 return 1;
165         }
166         if (listen(fd, 1) < 0) {
167                 td_vmsg(td, errno, "listen");
168                 return 1;
169         }
170
171         socklen = sizeof(addr);
172         td->files[0].fd = accept(fd, (struct sockaddr *) &addr, &socklen);
173         if (td->files[0].fd < 0) {
174                 td_vmsg(td, errno, "accept");
175                 return 1;
176         }
177
178         return 0;
179 }
180
181 static int fio_netio_setup(struct thread_data *td)
182 {
183         char host[64], buf[128];
184         struct net_data *nd;
185         unsigned short port;
186         char *sep;
187         int ret;
188
189         /*
190          * work around for late init call
191          */
192         if (td->io_ops->init(td))
193                 return 1;
194
195         nd = td->io_ops->data;
196
197         if (td->iomix) {
198                 log_err("fio: network connections must be read OR write\n");
199                 return 1;
200         }
201         if (td->nr_files > 1) {
202                 log_err("fio: only one file supported for network\n");
203                 return 1;
204         }
205
206         strcpy(buf, td->filename);
207
208         sep = strchr(buf, ':');
209         if (!sep) {
210                 log_err("fio: bad network host:port <<%s>>\n", td->filename);
211                 return 1;
212         }
213
214         *sep = '\0';
215         sep++;
216         strcpy(host, buf);
217         port = atoi(sep);
218
219         if (td->ddir == READ) {
220                 nd->send_to_net = 0;
221                 ret = fio_netio_setup_listen(td, port);
222         } else {
223                 nd->send_to_net = 1;
224                 ret = fio_netio_setup_connect(td, host, port);
225         }
226
227         if (!ret) {
228                 td->io_size = td->total_file_size;
229                 td->total_io_size = td->io_size;
230                 td->files[0].real_file_size = td->io_size;
231         }
232
233         return ret;
234 }
235
236 static void fio_netio_cleanup(struct thread_data *td)
237 {
238         if (td->io_ops->data) {
239                 free(td->io_ops->data);
240                 td->io_ops->data = NULL;
241         }
242 }
243
244 static int fio_netio_init(struct thread_data *td)
245 {
246         struct net_data *nd;
247
248         /*
249          * Hack to work-around the ->setup() function calling init on its
250          * own, since it needs ->io_ops->data to be set up.
251          */
252         if (td->io_ops->data)
253                 return 0;
254
255         nd  = malloc(sizeof(*nd));
256         nd->last_io_u = NULL;
257         td->io_ops->data = nd;
258         return 0;
259 }
260
261 static struct ioengine_ops ioengine = {
262         .name           = "net",
263         .version        = FIO_IOOPS_VERSION,
264         .init           = fio_netio_init,
265         .prep           = fio_netio_prep,
266         .queue          = fio_netio_queue,
267         .getevents      = fio_netio_getevents,
268         .event          = fio_netio_event,
269         .cleanup        = fio_netio_cleanup,
270         .setup          = fio_netio_setup,
271         .flags          = FIO_SYNCIO | FIO_NETIO,
272 };
273
274 static void fio_init fio_netio_register(void)
275 {
276         register_ioengine(&ioengine);
277 }
278
279 static void fio_exit fio_netio_unregister(void)
280 {
281         unregister_ioengine(&ioengine);
282 }