Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / qa / workunits / direct_io / direct_io_test.c
1 /*
2  * Ceph - scalable distributed file system
3  *
4  * Copyright (C) 2011 New Dream Network
5  *
6  * This is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License version 2.1, as published by the Free Software
9  * Foundation.  See file COPYING.
10  *
11  */
12
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <fcntl.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <time.h>
23 #include <unistd.h>
24
25 /*
26  * direct_io_test
27  *
28  * This test does some I/O using O_DIRECT.
29  *
30  * Semantics of O_DIRECT can be found at http://lwn.net/Articles/348739/
31  *
32  */
33
34 static int g_num_pages = 100;
35
36 static int g_duration = 10;
37
38 struct chunk {
39         uint64_t offset;
40         uint64_t pad0;
41         uint64_t pad1;
42         uint64_t pad2;
43         uint64_t pad3;
44         uint64_t pad4;
45         uint64_t pad5;
46         uint64_t not_offset;
47 } __attribute__((packed));
48
49 static int page_size;
50
51 static char temp_file[] = "direct_io_temp_file_XXXXXX";
52
53 static int safe_write(int fd, const void *buf, signed int len)
54 {
55         const char *b = (const char*)buf;
56         /* Handle EINTR and short writes */
57         while (1) {
58                 int res = write(fd, b, len);
59                 if (res < 0) {
60                         int err = errno;
61                         if (err != EINTR) {
62                                 return err;
63                         }
64                 }
65                 len -= res;
66                 b += res;
67                 if (len <= 0)
68                         return 0;
69         }
70 }
71
72 static int do_read(int fd, char *buf, int buf_sz)
73 {
74         /* We assume no short reads or EINTR. It's not really clear how
75          * those things interact with O_DIRECT. */
76         int ret = read(fd, buf, buf_sz);
77         if (ret < 0) {
78                 int err = errno;
79                 printf("do_read: error: %d (%s)\n", err, strerror(err));
80                 return err;
81         }
82         if (ret != buf_sz) {
83                 printf("do_read: short read\n");
84                 return -EIO;
85         }
86         return 0;
87 }
88
89 static int setup_temp_file(void)
90 {
91         int fd;
92         int64_t num_chunks, i;
93
94         if (page_size % sizeof(struct chunk)) {
95                 printf("setup_big_file: page_size doesn't divide evenly "
96                         "into data blocks.\n");
97                 return -EINVAL;
98         }
99
100         fd = mkstemp(temp_file);
101         if (fd < 0) {
102                 int err = errno;
103                 printf("setup_big_file: mkostemps failed with error %d\n", err);
104                 return err;
105         }
106
107         num_chunks = g_num_pages * (page_size / sizeof(struct chunk));
108         for (i = 0; i < num_chunks; ++i) {
109                 int ret;
110                 struct chunk c;
111                 memset(&c, 0, sizeof(c));
112                 c.offset = i * sizeof(struct chunk);
113                 c.pad0 = 0;
114                 c.pad1 = 1;
115                 c.pad2 = 2;
116                 c.pad3 = 3;
117                 c.pad4 = 4;
118                 c.pad5 = 5;
119                 c.not_offset = ~c.offset;
120                 ret = safe_write(fd, &c, sizeof(struct chunk));
121                 if (ret) {
122                         printf("setup_big_file: safe_write failed with "
123                                "error: %d\n", ret);
124                         TEMP_FAILURE_RETRY(close(fd));
125                         unlink(temp_file);
126                         return ret;
127                 }
128         }
129         TEMP_FAILURE_RETRY(close(fd));
130         return 0;
131 }
132
133 static int verify_chunk(const struct chunk *c, uint64_t offset)
134 {
135         if (c->offset != offset) {
136                 printf("verify_chunk(%" PRId64 "): bad offset value (got: %"
137                        PRId64 ", expected: %" PRId64 "\n", offset, c->offset, offset);
138                 return EIO;
139         }
140         if (c->pad0 != 0) {
141                 printf("verify_chunk(%" PRId64 "): bad pad0 value\n", offset);
142                 return EIO;
143         }
144         if (c->pad1 != 1) {
145                 printf("verify_chunk(%" PRId64 "): bad pad1 value\n", offset);
146                 return EIO;
147         }
148         if (c->pad2 != 2) {
149                 printf("verify_chunk(%" PRId64 "): bad pad2 value\n", offset);
150                 return EIO;
151         }
152         if (c->pad3 != 3) {
153                 printf("verify_chunk(%" PRId64 "): bad pad3 value\n", offset);
154                 return EIO;
155         }
156         if (c->pad4 != 4) {
157                 printf("verify_chunk(%" PRId64 "): bad pad4 value\n", offset);
158                 return EIO;
159         }
160         if (c->pad5 != 5) {
161                 printf("verify_chunk(%" PRId64 "): bad pad5 value\n", offset);
162                 return EIO;
163         }
164         if (c->not_offset != ~offset) {
165                 printf("verify_chunk(%" PRId64 "): bad not_offset value\n",
166                        offset);
167                 return EIO;
168         }
169         return 0;
170 }
171
172 static int do_o_direct_reads(void)
173 {
174         int fd, ret;
175         unsigned int i;
176         void *buf = 0;
177         time_t cur_time, end_time;
178         ret = posix_memalign(&buf, page_size, page_size);
179         if (ret) {
180                 printf("do_o_direct_reads: posix_memalign returned %d\n", ret);
181                 goto done;
182         }
183
184         fd = open(temp_file, O_RDONLY | O_DIRECT);
185         if (fd < 0) {
186                 ret = errno;
187                 printf("do_o_direct_reads: error opening fd: %d\n", ret);
188                 goto free_buf;
189         }
190
191         // read the first chunk and see if it looks OK
192         ret = do_read(fd, buf, page_size);
193         if (ret)
194                 goto close_fd;
195         ret = verify_chunk((struct chunk*)buf, 0);
196         if (ret)
197                 goto close_fd;
198
199         // read some random chunks and see how they look
200         cur_time = time(NULL);
201         end_time = cur_time + g_duration;
202         i = 0;
203         do {
204                 time_t next_time;
205                 uint64_t offset;
206                 int page;
207                 unsigned int seed;
208
209                 seed = i++;
210                 page = rand_r(&seed) % g_num_pages;
211                 offset = page;
212                 offset *= page_size;
213                 if (lseek64(fd, offset, SEEK_SET) == -1) {
214                         int err = errno;
215                         printf("lseek64(%" PRId64 ") failed: error %d (%s)\n",
216                                offset, err, strerror(err));
217                         goto close_fd;
218                 }
219                 ret = do_read(fd, buf, page_size);
220                 if (ret)
221                         goto close_fd;
222                 ret = verify_chunk((struct chunk*)buf, offset);
223                 if (ret)
224                         goto close_fd;
225                 next_time = time(NULL);
226                 if (next_time > cur_time) {
227                         printf(".");
228                 }
229                 cur_time = next_time;
230         } while (time(NULL) < end_time);
231
232         printf("\ndo_o_direct_reads: SUCCESS\n");
233 close_fd:
234         TEMP_FAILURE_RETRY(close(fd));
235 free_buf:
236         free(buf);
237 done:
238         return ret;
239 }
240
241 static void usage(char *argv0)
242 {
243         printf("%s: tests direct I/O\n", argv0);
244         printf("-d <seconds>:          sets duration to <seconds>\n");
245         printf("-h:                    this help\n");
246         printf("-p <pages>:            sets number of pages to allocate\n");
247 }
248
249 static void parse_args(int argc, char *argv[])
250 {
251         int c;
252         while ((c = getopt (argc, argv, "d:hp:")) != -1) {
253                 switch (c) {
254                 case 'd':
255                         g_duration = atoi(optarg);
256                         if (g_duration <= 0) {
257                                 printf("tried to set invalid value of "
258                                        "g_duration: %d\n", g_num_pages);
259                                 exit(1);
260                         }
261                         break;
262                 case 'h':
263                         usage(argv[0]);
264                         exit(0);
265                         break;
266                 case 'p':
267                         g_num_pages = atoi(optarg);
268                         if (g_num_pages <= 0) {
269                                 printf("tried to set invalid value of "
270                                        "g_num_pages: %d\n", g_num_pages);
271                                 exit(1);
272                         }
273                         break;
274                 case '?':
275                         usage(argv[0]);
276                         exit(1);
277                         break;
278                 default:
279                         usage(argv[0]);
280                         exit(1);
281                         break;
282                 }
283         }
284 }
285
286 int main(int argc, char *argv[])
287 {
288         int ret;
289
290         parse_args(argc, argv);
291
292         setvbuf(stdout, NULL, _IONBF, 0);
293
294         page_size = getpagesize();
295
296         ret = setup_temp_file();
297         if (ret) {
298                 printf("setup_temp_file failed with error %d\n", ret);
299                 goto done;
300         }
301
302         ret = do_o_direct_reads();
303         if (ret) {
304                 printf("do_o_direct_reads failed with error %d\n", ret);
305                 goto unlink_temp_file;
306         }
307
308 unlink_temp_file:
309         unlink(temp_file);
310 done:
311         return ret;
312 }