[PATCH] Simple support for networked IO
[fio.git] / engines / net.c
CommitLineData
ed92ac0c
JA
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
17struct net_data {
18 int send_to_net;
19 struct io_u *last_io_u;
20};
21
22static 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
37static 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
46static 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 td_verror(td, EINVAL);
69 return 1;
70}
71
72static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
73{
74 struct net_data *nd = td->io_ops->data;
75 struct fio_file *f = io_u->file;
76 unsigned int ret = 0;
77
78 if (io_u->ddir == DDIR_WRITE)
79 ret = write(f->fd, io_u->buf, io_u->buflen);
80 else if (io_u->ddir == DDIR_READ)
81 ret = read(f->fd, io_u->buf, io_u->buflen);
82
83 if (ret != io_u->buflen) {
84 if (ret > 0) {
85 io_u->resid = io_u->buflen - ret;
86 io_u->error = EIO;
87 } else
88 io_u->error = errno;
89 }
90
91 if (!io_u->error)
92 nd->last_io_u = io_u;
93
94 return io_u->error;
95}
96
97static int fio_netio_setup_connect(struct thread_data *td, const char *host,
98 const char *port)
99{
100 struct sockaddr_in addr;
101 struct fio_file *f;
102
103 memset(&addr, 0, sizeof(addr));
104 addr.sin_family = AF_INET;
105 addr.sin_port = htons(atoi(port));
106
107 if (inet_aton(host, &addr.sin_addr) != 1) {
108 struct hostent *hent = gethostbyname(host);
109
110 if (!hent) {
111 td_vmsg(td, errno, "gethostbyname");
112 return 1;
113 }
114
115 memcpy(&addr.sin_addr, hent->h_addr, 4);
116 }
117
118 f = &td->files[0];
119
120 f->fd = socket(AF_INET, SOCK_STREAM, 0);
121 if (f->fd < 0) {
122 td_vmsg(td, errno, "socket");
123 return 1;
124 }
125
126 if (connect(f->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
127 td_vmsg(td, errno, "connect");
128 return 1;
129 }
130
131 return 0;
132
133}
134
135static int fio_netio_setup_listen(struct thread_data *td, const char *port)
136{
137 struct sockaddr_in addr;
138 socklen_t socklen;
139 int fd, opt;
140
141 fd = socket(AF_INET, SOCK_STREAM, 0);
142 if (fd < 0) {
143 td_vmsg(td, errno, "socket");
144 return 1;
145 }
146
147 opt = 1;
148 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
149 td_vmsg(td, errno, "setsockopt");
150 return 1;
151 }
152
153 memset(&addr, 0, sizeof(addr));
154 addr.sin_family = AF_INET;
155 addr.sin_addr.s_addr = htonl(INADDR_ANY);
156 addr.sin_port = htons(atoi(port));
157
158 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
159 td_vmsg(td, errno, "bind");
160 return 1;
161 }
162 if (listen(fd, 1) < 0) {
163 td_vmsg(td, errno, "listen");
164 return 1;
165 }
166
167 socklen = sizeof(addr);
168 td->files[0].fd = accept(fd, (struct sockaddr *) &addr, &socklen);
169 if (td->files[0].fd < 0) {
170 td_vmsg(td, errno, "accept");
171 return 1;
172 }
173
174 return 0;
175}
176
177static int fio_netio_setup(struct thread_data *td)
178{
179 char host[64], port[64], buf[128];
180 struct net_data *nd;
181 char *sep;
182 int ret;
183
184 /*
185 * work around for late init call
186 */
187 if (td->io_ops->init(td))
188 return 1;
189
190 nd = td->io_ops->data;
191
192 if (td->iomix) {
193 log_err("fio: network connections must be read OR write\n");
194 return 1;
195 }
196 if (td->nr_files > 1) {
197 log_err("fio: only one file supported for network\n");
198 return 1;
199 }
200
201 strcpy(buf, td->filename);
202
203 sep = strchr(buf, ':');
204 if (!sep) {
205 log_err("fio: bad network host:port <<%s>>\n", td->filename);
206 return 1;
207 }
208
209 *sep = '\0';
210 sep++;
211 strcpy(host, buf);
212 strcpy(port, sep);
213
214 if (td->ddir == READ) {
215 nd->send_to_net = 0;
216 ret = fio_netio_setup_listen(td, port);
217 } else {
218 nd->send_to_net = 1;
219 ret = fio_netio_setup_connect(td, host, port);
220 }
221
222 if (!ret) {
223 td->io_size = td->total_file_size;
224 td->total_io_size = td->io_size;
225 td->files[0].real_file_size = td->io_size;
226 }
227
228 return ret;
229}
230
231static void fio_netio_cleanup(struct thread_data *td)
232{
233 if (td->io_ops->data) {
234 free(td->io_ops->data);
235 td->io_ops->data = NULL;
236 }
237}
238
239static int fio_netio_init(struct thread_data *td)
240{
241 struct net_data *nd;
242
243 if (td->io_ops->data)
244 return 0;
245
246 nd = malloc(sizeof(*nd));
247 nd->last_io_u = NULL;
248 td->io_ops->data = nd;
249 return 0;
250}
251
252static struct ioengine_ops ioengine = {
253 .name = "net",
254 .version = FIO_IOOPS_VERSION,
255 .init = fio_netio_init,
256 .prep = fio_netio_prep,
257 .queue = fio_netio_queue,
258 .getevents = fio_netio_getevents,
259 .event = fio_netio_event,
260 .cleanup = fio_netio_cleanup,
261 .setup = fio_netio_setup,
262 .flags = FIO_SYNCIO | FIO_NETIO,
263};
264
265static void fio_init fio_netio_register(void)
266{
267 register_ioengine(&ioengine);
268}
269
270static void fio_exit fio_netio_unregister(void)
271{
272 unregister_ioengine(&ioengine);
273}