1 /* 2 * Copyright (c) 2004 Daniel McNeil <daniel (at) osdl.org> 3 * 2004 Open Source Development Lab 4 * 5 * Copyright (c) 2004 Marty Ridgeway <mridge (at) us.ibm.com> 6 * 7 * Copyright (c) 2011 Cyril Hrubis <chrubis (at) suse.cz> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 17 * the GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24 #define _GNU_SOURCE 25 26 #include <stdlib.h> 27 #include <sys/types.h> 28 #include <errno.h> 29 #include <signal.h> 30 #include <fcntl.h> 31 #include <stdio.h> 32 #include <unistd.h> 33 #include <sys/mman.h> 34 #include <sys/wait.h> 35 #include <limits.h> 36 #include <getopt.h> 37 38 39 #include "config.h" 40 #include "test.h" 41 #include "safe_macros.h" 42 43 char *TCID = "aiodio_sparse"; 44 int TST_TOTAL = 1; 45 46 #ifdef HAVE_LIBAIO 47 #include <libaio.h> 48 49 #define NUM_CHILDREN 1000 50 51 int debug; 52 int fd; 53 54 static void setup(void); 55 static void cleanup(void); 56 static void usage(void); 57 58 #include "common_sparse.h" 59 60 /* 61 * do async DIO writes to a sparse file 62 */ 63 int aiodio_sparse(int fd, int align, int writesize, int filesize, int num_aio) 64 { 65 int i, w; 66 struct iocb **iocbs; 67 off_t offset; 68 io_context_t myctx; 69 struct io_event event; 70 int aio_inflight; 71 72 if ((num_aio * writesize) > filesize) 73 num_aio = filesize / writesize; 74 75 memset(&myctx, 0, sizeof(myctx)); 76 io_queue_init(num_aio, &myctx); 77 78 iocbs = malloc(sizeof(struct iocb *) * num_aio); 79 for (i = 0; i < num_aio; i++) { 80 if ((iocbs[i] = malloc(sizeof(struct iocb))) == 0) { 81 tst_resm(TBROK | TERRNO, "malloc()"); 82 return 1; 83 } 84 } 85 86 /* 87 * allocate the iocbs array and iocbs with buffers 88 */ 89 offset = 0; 90 for (i = 0; i < num_aio; i++) { 91 void *bufptr; 92 93 TEST(posix_memalign(&bufptr, align, writesize)); 94 if (TEST_RETURN) { 95 tst_resm(TBROK | TRERRNO, "cannot allocate aligned memory"); 96 return 1; 97 } 98 memset(bufptr, 0, writesize); 99 io_prep_pwrite(iocbs[i], fd, bufptr, writesize, offset); 100 offset += writesize; 101 } 102 103 /* 104 * start the 1st num_aio write requests 105 */ 106 if ((w = io_submit(myctx, num_aio, iocbs)) < 0) { 107 tst_resm(TBROK, "io_submit() returned %i", w); 108 return 1; 109 } 110 111 if (debug) 112 tst_resm(TINFO, "io_submit() returned %d", w); 113 114 /* 115 * As AIO requests finish, keep issuing more AIO until done. 116 */ 117 aio_inflight = num_aio; 118 119 while (offset < filesize) { 120 int n; 121 struct iocb *iocbp; 122 123 if (debug) 124 tst_resm(TINFO, 125 "aiodio_sparse: offset %p filesize %d inflight %d", 126 &offset, filesize, aio_inflight); 127 128 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) { 129 if (-n != EINTR) 130 tst_resm(TBROK, "io_getevents() returned %d", 131 n); 132 break; 133 } 134 135 if (debug) 136 tst_resm(TINFO, 137 "aiodio_sparse: io_getevent() returned %d", n); 138 139 aio_inflight--; 140 141 /* 142 * check if write succeeded. 143 */ 144 iocbp = (struct iocb *)event.obj; 145 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) { 146 tst_resm(TBROK, 147 "AIO write offset %lld expected %ld got %ld", 148 iocbp->u.c.offset, iocbp->u.c.nbytes, 149 event.res); 150 break; 151 } 152 153 if (debug) 154 tst_resm(TINFO, 155 "aiodio_sparse: io_getevent() res %ld res2 %ld", 156 event.res, event.res2); 157 158 /* start next write */ 159 io_prep_pwrite(iocbp, fd, iocbp->u.c.buf, writesize, offset); 160 offset += writesize; 161 if ((w = io_submit(myctx, 1, &iocbp)) < 0) { 162 tst_resm(TBROK, "io_submit failed at offset %ld", 163 offset); 164 break; 165 } 166 167 if (debug) 168 tst_resm(TINFO, "io_submit() return %d", w); 169 170 aio_inflight++; 171 } 172 173 /* 174 * wait for AIO requests in flight. 175 */ 176 while (aio_inflight > 0) { 177 int n; 178 struct iocb *iocbp; 179 180 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) { 181 tst_resm(TBROK, "io_getevents failed"); 182 break; 183 } 184 aio_inflight--; 185 /* 186 * check if write succeeded. 187 */ 188 iocbp = (struct iocb *)event.obj; 189 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) { 190 tst_resm(TBROK, 191 "AIO write offset %lld expected %ld got %ld", 192 iocbp->u.c.offset, iocbp->u.c.nbytes, 193 event.res); 194 } 195 } 196 197 return 0; 198 } 199 200 static void usage(void) 201 { 202 fprintf(stderr, "usage: dio_sparse [-n children] [-s filesize]" 203 " [-w writesize]\n"); 204 exit(1); 205 } 206 207 int main(int argc, char **argv) 208 { 209 char *filename = "aiodio_sparse"; 210 int pid[NUM_CHILDREN]; 211 int num_children = 1; 212 int i; 213 long alignment = 512; 214 int writesize = 65536; 215 int filesize = 100 * 1024 * 1024; 216 int num_aio = 16; 217 int children_errors = 0; 218 int c; 219 int ret; 220 221 while ((c = getopt(argc, argv, "dw:n:a:s:i:")) != -1) { 222 char *endp; 223 switch (c) { 224 case 'd': 225 debug++; 226 break; 227 case 'i': 228 num_aio = atoi(optarg); 229 break; 230 case 'a': 231 alignment = strtol(optarg, &endp, 0); 232 alignment = (int)scale_by_kmg((long long)alignment, 233 *endp); 234 break; 235 case 'w': 236 writesize = strtol(optarg, &endp, 0); 237 writesize = 238 (int)scale_by_kmg((long long)writesize, *endp); 239 break; 240 case 's': 241 filesize = strtol(optarg, &endp, 0); 242 filesize = 243 (int)scale_by_kmg((long long)filesize, *endp); 244 break; 245 case 'n': 246 num_children = atoi(optarg); 247 if (num_children > NUM_CHILDREN) { 248 fprintf(stderr, 249 "number of children limited to %d\n", 250 NUM_CHILDREN); 251 num_children = NUM_CHILDREN; 252 } 253 break; 254 case '?': 255 usage(); 256 break; 257 } 258 } 259 260 setup(); 261 tst_resm(TINFO, "Dirtying free blocks"); 262 dirty_freeblocks(filesize); 263 264 fd = SAFE_OPEN(cleanup, filename, 265 O_DIRECT | O_WRONLY | O_CREAT | O_EXCL, 0600); 266 SAFE_FTRUNCATE(cleanup, fd, filesize); 267 268 tst_resm(TINFO, "Starting I/O tests"); 269 signal(SIGTERM, SIG_DFL); 270 for (i = 0; i < num_children; i++) { 271 switch (pid[i] = fork()) { 272 case 0: 273 SAFE_CLOSE(NULL, fd); 274 read_sparse(filename, filesize); 275 break; 276 case -1: 277 while (i-- > 0) 278 kill(pid[i], SIGTERM); 279 280 tst_brkm(TBROK | TERRNO, cleanup, "fork()"); 281 default: 282 continue; 283 } 284 } 285 tst_sig(FORK, DEF_HANDLER, cleanup); 286 287 ret = aiodio_sparse(fd, alignment, writesize, filesize, num_aio); 288 289 tst_resm(TINFO, "Killing childrens(s)"); 290 291 for (i = 0; i < num_children; i++) 292 kill(pid[i], SIGTERM); 293 294 for (i = 0; i < num_children; i++) { 295 int status; 296 pid_t p; 297 298 p = waitpid(pid[i], &status, 0); 299 if (p < 0) { 300 tst_resm(TBROK | TERRNO, "waitpid()"); 301 } else { 302 if (WIFEXITED(status) && WEXITSTATUS(status) == 10) 303 children_errors++; 304 } 305 } 306 307 if (children_errors) 308 tst_resm(TFAIL, "%i children(s) exited abnormally", 309 children_errors); 310 311 if (!children_errors && !ret) 312 tst_resm(TPASS, "Test passed"); 313 314 cleanup(); 315 tst_exit(); 316 } 317 318 static void setup(void) 319 { 320 tst_sig(FORK, DEF_HANDLER, cleanup); 321 tst_tmpdir(); 322 } 323 324 static void cleanup(void) 325 { 326 if (fd > 0 && close(fd)) 327 tst_resm(TWARN | TERRNO, "Failed to close file"); 328 329 tst_rmdir(); 330 } 331 332 #else 333 int main(void) 334 { 335 tst_brkm(TCONF, NULL, "test requires libaio and it's development packages"); 336 } 337 #endif 338