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