1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include "nfsgen.h"
28
29 char *testname;
30
31 #define BUFF_32k 32*1024
32
33 #define MIN(a, b) ((a) < (b) ? (a) : (b))
34
35 void usage(int);
36 void fill_buffer(char *, int);
37 void mk_file(char *, int, char *, int);
38 void ck_file(int, int, char *);
39 void ck_locking(int, int);
40
41 /*
42 * Prints out the usage message and exits with the
43 * passed in exit code "ec"
44 */
45 void
46 usage(int ec)
47 {
48 printf("\t Test UNINITIATED: ");
49 printf("usage: %s -u <u.g> -d <dbg> -b <base_dir> -s <fsize> \n"
50 " -n <ocnt> -l -v -W <tc_nap>\n", testname);
51 puts("\t -u <u.g> Uid(u) and Gid(g) to become (numeric value)\n");
52 puts("\t -d <dbg> Information level: 0 - none \n"
53 "\t 1 - errors \n"
54 "\t 2 - errors and debug\n");
55 puts("\t -b <base_dir> The directory under which to create files\n");
56 puts("\t -s <fsize> The size to make the files created\n");
57 puts("\t -n <ocnt> Number of files to create and open\n");
58 puts("\t -l Skip the locking test\n");
59 puts("\t -v Skip the file content validation test\n");
60 puts("\t -W <tc_nap> Pause period (in seconds) \n"
61 "\t -S <num> Scenario number \n");
62 exit(ec);
63 }
64
65 /*
66 * For a buffer "ptr" fill it with a numerically
67 * increasing byte value for the entire "size"
68 * of the buffer.
69 */
70 void
71 fill_buffer(char *ptr, int size)
72 {
73 int i;
74
75 for (i = 0; i < size; i++) {
76 ptr[i] = (i & 0xff);
77 }
78 }
79
80 /*
81 * Create a file using "path" and "file_flags" then write
82 * the data described by "buf" and size then close the file.
83 */
84 void
85 mk_file(char *path, int file_flags, char *buf, int size)
86 {
87 int fd, rc;
88
89 /*
90 * create and open a file
91 */
92 if ((fd = open_file(path, file_flags, 0777)) < 0) {
93 fprintf(stderr, "\t Test UNINITIATED: ");
94 exit(NOOK);
95 }
96
97 if ((rc = write(fd, buf, size)) != size) {
98 fprintf(stderr, "\t Test UNINITIATED: ");
99 fprintf(stderr, "error on write (%d)\n", errno);
100 close_file(fd, path);
101 exit(NOOK);
102 }
103 close(fd);
104 }
105
106 /*
107 * Close all the files.
108 */
109 void
110 cls_file(int fds[], int file_opens)
111 {
112 int c;
113 char fileGumbo[25];
114
115 for (c = 0; c < file_opens; c++) {
116 sprintf(fileGumbo, "F%06d", c);
117 close_file(fds[c], fileGumbo);
118 }
119 }
120
121 /*
122 * The intent is to verify that the file "fd" still matches
123 * "buffy" which is a 32k buffer setup by fill_buffer for
124 * the size of the file "file_size".
125 */
126 void
127 ck_file(int fd, int file_size, char *buffy)
128 {
129 int blk = 0, rc, wc, fs = file_size;
130 char yffub[BUFF_32k];
131
132
133 do {
134 /*
135 * compare in 32k blocks.
136 */
137 wc = MIN(fs, BUFF_32k);
138
139 if ((rc = read(fd, yffub, wc)) != wc) {
140 fprintf(stderr, "\t Test FAIL: ");
141 Perror("read():");
142 exit(NOOK);
143 }
144
145 if ((rc = memcmp(buffy, yffub, wc)) != 0) {
146 fprintf(stderr, "\t Test FAIL: ");
147 eprint("(rc=%d) written != read "
148 " in 32k blk @offset %d\n",
149 rc, blk);
150 exit(NOOK);
151 }
152 blk += wc;
153 fs -= wc;
154 } while (fs);
155 }
156
157 /*
158 * For each "file_opens" file descriptors in array "fds"
159 * open the file, acquire a write lock then fork a child
160 * that hammers to get a write lock, in the meantime the
161 * parent sleeps for "parent_sleep" time, wakes up and
162 * releases the initial write lock and waits for the
163 * child to exit.
164 */
165 int
166 s1(int *fds, int file_opens, int parent_sleep, char *buffy)
167 {
168 char fileGumbo[35];
169 int stat;
170 pid_t kid;
171 int c, s;
172
173 for (c = 0; c < file_opens; c++) {
174 sprintf(fileGumbo, "F%06d", c);
175 fds[c] = open_file(fileGumbo, O_RDWR, 0);
176 if (fds[c] < 0) {
177 fprintf(stderr, "\t Test FAIL: ");
178 eprint("EEK!.. open failed");
179 return (-1);
180 } else {
181 /* parent will lock file first.. */
182 if (lock_reg(fds[c], 0, F_WRLCK, 0, 0, 0) != OK) {
183 /* error */
184 fprintf(stderr, "\t Test FAIL: ");
185 eprint("lock: not acquired\n");
186 return (-1);
187 }
188
189 if ((kid = fork()) == 0) {
190 /*
191 * kid spins till parent sleep time
192 * exhaused..
193 * but try not to print the conflict lock error.
194 */
195 s = showerror;
196 if (debug == 0)
197 showerror = 0;
198 while (lock_reg(fds[c], 0, F_WRLCK,
199 0, 0, 0) != OK) {
200 if (errno != EAGAIN) {
201 fprintf(stderr,
202 "\t Test FAIL: ");
203 Perror("Child: WRLCK");
204 exit(1);
205 }
206 }
207 showerror = s;
208
209 if (lock_reg(fds[c], 0, F_UNLCK, 0, 0,
210 0) != OK) {
211 fprintf(stderr, "\t Test FAIL: ");
212 eprint("Child: unlock failed\n");
213 exit(2);
214 }
215 /* kid delete it */
216 unlink(fileGumbo);
217 exit(0);
218 } else {
219 /* parent.. */
220
221 sleep(parent_sleep);
222 if (lock_reg(fds[c], 0, F_UNLCK, 0, 0,
223 0) != OK) {
224 fprintf(stderr, "\t Test FAIL: ");
225 eprint("unlock failed\n");
226 }
227 /* hang about for the kid .. */
228 wait(&stat);
229 }
230 }
231 /* and now.. parent deletes it */
232 unlink(fileGumbo);
233 }
234 printf("\t Test PASS: successfully completed execution runs.\n");
235
236 return (0);
237 }
238
239 /*
240 * For "file_opens" file descriptors in "fds" open the file
241 * read donly, close it and open it write only, close it and
242 * then open it read only finally close and unlink it.
243 */
244 void
245 s2(int *fds, int file_opens)
246 {
247 int c;
248 char fileGumbo[35];
249
250 dprint("Opening files O_RDONLY: ");
251 for (c = 0; c < file_opens; c++) {
252 sprintf(fileGumbo, "F%06d", c);
253 fds[c] = open_file(fileGumbo, O_RDONLY, 0);
254 if (fds[c] < 0) {
255 fprintf(stderr, "\t Test FAIL: ");
256 eprint("0001 EEK!.. open failed (%s)\n",
257 fileGumbo);
258 }
259 }
260 dprint("OK\n");
261
262 cls_file(fds, file_opens);
263
264 dprint("Reopening files O_WRONLY: ");
265 for (c = 0; c < file_opens; c++) {
266 sprintf(fileGumbo, "F%06d", c);
267 fds[c] = open_file(fileGumbo, O_WRONLY, 0);
268 if (fds[c] < 0) {
269 fprintf(stderr, "\t Test FAIL: ");
270 eprint("0002 EEK!.. open(O_WRONLY) failed (%s)\n",
271 fileGumbo);
272 }
273 }
274 dprint("OK\n");
275
276 cls_file(fds, file_opens);
277
278 dprint("Reopening files O_RDONLY: ");
279 for (c = 0; c < file_opens; c++) {
280 sprintf(fileGumbo, "F%06d", c);
281 fds[c] = open_file(fileGumbo, O_RDONLY, 0);
282 if (fds[c] < 0) {
283 fprintf(stderr, "\t Test FAIL: ");
284 eprint("0003 EEK!.. open(O_RDONLY) failed (%s)\n",
285 fileGumbo);
286 }
287 }
288 dprint("OK\n");
289
290 dprint("Close and unlink files: ");
291 for (c = 0; c < file_opens; c++) {
292 close(fds[c]);
293 sprintf(fileGumbo, "F%06d", c);
294 unlink(fileGumbo);
295 }
296 dprint("OK\n");
297
298 printf("\t Test PASS: successfully completed execution runs.\n");
299 }
300
301 /*
302 * For "file_opens" file descriptors in "fds" open the file
303 * read/write read_lock the entire file, write_lock the
304 * first anad last 1024 bytes, release the 1st read_lock,
305 * get a write_lock on thte rest of thte file (middle bit)
306 * unlock everythihng and delete file.
307 *
308 */
309 void
310 s3(int *fds, int file_opens)
311 {
312 int c;
313 char fileGumbo[35];
314
315 for (c = 0; c < file_opens; c++) {
316 sprintf(fileGumbo, "F%06d", c);
317 fds[c] = open_file(fileGumbo, O_RDWR, 0);
318 if (fds[c] < 0) {
319 fprintf(stderr, "\t Test FAIL: ");
320 eprint("0001 EEK!.. open failed (%s)\n",
321 fileGumbo);
322 }
323 }
324
325 for (c = 0; c < file_opens; c++) {
326 /* Get a read lock for the entire file. */
327 if (lock_reg(fds[c], 0, F_RDLCK, 0, 0, 0) != OK) {
328 /* error */
329 fprintf(stderr, "\t Test FAIL: ");
330 eprint("read lock not acquired (F%06d)\n", c);
331 return;
332 }
333 /* now write lock for last 1024 bytes of the file */
334 if (lock_reg(fds[c], 0, F_WRLCK, 0, SEEK_END, -1024) != OK) {
335 /* error */
336 fprintf(stderr, "\t Test FAIL: ");
337 eprint("wrirte lock last 1k not acquired (F%06d)\n", c);
338 return;
339 }
340
341 /* now a write lock for first 1024 bytes. */
342 if (lock_reg(fds[c], 0, F_WRLCK, 0, SEEK_SET, 1024) != OK) {
343 /* error */
344 fprintf(stderr, "\t Test FAIL: ");
345 eprint("write lock first 1k not acquired (F%06d)\n", c);
346 return;
347 }
348
349 /* release thte read lock */
350 if (lock_reg(fds[c], 0, F_UNLCK, 0, 0, 0) != OK) {
351 /* error */
352 fprintf(stderr, "\t Test FAIL: ");
353 eprint("unlock failed (F%06d) for read lock\n", c);
354 return;
355 }
356
357 /* get a write lock for the middle of the file.. */
358 if (lock_reg(fds[c], 0, F_WRLCK, 1024+1, SEEK_SET,
359 (BUFF_32k-(2048+1))) != OK) {
360 /* error */
361 fprintf(stderr, "\t Test FAIL: ");
362 eprint("write lock middle chunk not "
363 "acquired (F%06d)\n", c);
364 return;
365 }
366 if (lock_reg(fds[c], 0, F_UNLCK, 0, SEEK_END, -1024) != OK) {
367 /* error */
368 fprintf(stderr, "\t Test FAIL: ");
369 eprint("wrirte lock last 1k not acquired (F%06d)\n", c);
370 return;
371 }
372
373 /* now a write lock for first 1024 bytes. */
374 if (lock_reg(fds[c], 0, F_UNLCK, 0, SEEK_SET, 1024) != OK) {
375 /* error */
376 fprintf(stderr, "\t Test FAIL: ");
377 eprint("write lock first 1k not acquired (F%06d)\n", c);
378 return;
379 }
380 /* get a write lock for the middle of the file.. */
381 if (lock_reg(fds[c], 0, F_UNLCK, 1024+1, SEEK_SET,
382 (BUFF_32k-(2048+1))) != OK) {
383 /* error */
384 fprintf(stderr, "\t Test FAIL: ");
385 eprint("write lock middle chunk not "
386 "acquired (F%06d)\n", c);
387 return;
388 }
389 close(fds[c]);
390 }
391 /* now run 'n delete 'em.. */
392 for (c = 0; c < file_opens; c++) {
393 sprintf(fileGumbo, "F%06d", c);
394 unlink(fileGumbo);
395 }
396
397 printf("\t Test PASS: successfully completed execution runs.\n");
398 }
399 /*
400 * For each "file_opens" file descriptors in array "fds"
401 * open the file, acquire a write lock then unlink the
402 * file _without_ a close.
403 */
404 void
405 s4(int *fds, int file_opens)
406 {
407 char fileGumbo[35];
408 int c;
409
410 for (c = 0; c < file_opens; c++) {
411 sprintf(fileGumbo, "F%06d", c);
412 fds[c] = open_file(fileGumbo, O_RDWR, 0);
413 if (fds[c] < 0) {
414 fprintf(stderr, "\t Test FAIL: ");
415 printf("EEK!.. open failed");
416 } else {
417 /* lock file */
418 if (lock_reg(fds[c], 0, F_WRLCK, 0, 0, 0) != OK) {
419 /* error */
420 fprintf(stderr, "\t Test FAIL: ");
421 eprint("lock: not acquired\n");
422 }
423 /* yank out thte file without a close() */
424 unlink(fileGumbo);
425 }
426 }
427 printf("\t Test PASS: successfully completed execution runs.\n");
428 }
429
430 /*
431 * Main test loop.
432 */
433 int
434 main(int argc, char *argv[])
435 {
436 extern int optind;
437 extern char *optarg;
438
439 char buffy[BUFF_32k];
440 char fileGumbo[25];
441
442 char *buf, *base_dir = NULL;
443
444 int *fds, c;
445
446 int do_lock_checking = 1, do_content_validation = 1, nap_time = 30;
447
448 int file_opens = 250;
449 int file_size = BUFF_32k;
450 int file_flags = (O_CREAT|O_TRUNC|O_RDWR);
451 int mand = 0;
452 int stressTest = 0;
453
454 struct rlimit rlp;
455
456 testname = argv[0];
457
458 while ((c = getopt(argc, argv, "S:d:b:s:n:hW:lv")) != -1) {
459
460 switch (c) {
461 /* sez which stress test to do.. */
462 case 'S':
463 stressTest = atoi(optarg);
464 break;
465 /* Skip the lock checks */
466 case 'l':
467 do_lock_checking = 0;
468 break;
469 /* Skip the content validation */
470 case 'v':
471 do_content_validation = 0;
472 break;
473 /* number of files to open */
474 case 'n':
475 file_opens = atoi(optarg);
476 break;
477 /* the size of a file */
478 case 's':
479 file_size = atoi(optarg);
480 break;
481 /* base directory into which chdir and create files */
482 case 'b':
483 base_dir = optarg;
484 break;
485 /*
486 * debug flags treated as bit field for
487 * debug and showerror
488 */
489 case 'd':
490 switch (atoi(optarg)) {
491 case 0:
492 debug = 0;
493 showerror = 0;
494 break;
495 case 1:
496 debug = 0;
497 showerror = 1;
498 break;
499 case 2:
500 debug = 1;
501 showerror = 1;
502 break;
503 default:
504 usage(-1);
505 }
506 break;
507
508 /* the user wants me to nap... */
509 case 'W':
510 nap_time = atoi(optarg);
511 break;
512 case 'h':
513 usage(0);
514 break;
515 default:
516 usage(-1);
517 }
518 }
519
520 if (base_dir == NULL) {
521 fprintf(stderr, "\t Test UNINITIATED: ");
522 printf(" specify base directory via -b\n");
523 usage(-1);
524 }
525
526 if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) {
527 fprintf(stderr, "\t Test UNINITIATED: ");
528 eprint("getrlimit() for RLIMIT_NOFILE failed\n");
529 Perror(":-(");
530 return (-1);
531 }
532
533 dprint("RLMIT_NOFILE is cur=%d / max=%d\n",
534 rlp.rlim_cur, rlp.rlim_max);
535
536 /*
537 * The user has specified a number of files to open.
538 * We may have to increase the process limit for MAX
539 * open files. We also need to account for the fact
540 * that the process already has 3 open file descriptors
541 * for std{in,our,err}.
542 */
543 if (file_opens+3 > rlp.rlim_cur) {
544 rlp.rlim_cur = MIN(rlp.rlim_max, file_opens+3);
545 if (rlp.rlim_cur <= file_opens)
546 file_opens = rlp.rlim_cur-3;
547
548 if (setrlimit(RLIMIT_NOFILE, &rlp) < 0) {
549 fprintf(stderr, "\t Test UNINITIATED: ");
550 eprint("setrlimit() for RLIMIT_NOFILE failed\n");
551 Perror(":-/");
552 return (-1);
553 }
554 }
555
556 fill_buffer(buffy, BUFF_32k);
557
558 if (chdir(base_dir) != 0) {
559 fprintf(stderr, "\t Test UNINITIATED: ");
560 eprint("chdir() to %s failed\n");
561 Perror("chdir()");
562 return (-1);
563 }
564
565 /*
566 * generate file names and files.
567 */
568 dprint("Generating files: ");
569 for (c = 0; c < file_opens; c++) {
570 sprintf(fileGumbo, "F%06d", c);
571 mk_file(fileGumbo, file_flags, buffy, BUFF_32k);
572 }
573 dprint("OK.\n");
574
575 if ((fds = malloc(sizeof (int)*file_opens)) == NULL) {
576 printf("\t Test UNRESOLVED: ");
577 eprint("malloc for RLIMIT_NOFILE fds failed\n");
578 Perror(":-(");
579 return (-1);
580 }
581
582 switch (stressTest) {
583 case 1:
584 starttime(
585 "\n st_0003{a}: stress conflict lock until "
586 "child gets the lock\n\t ");
587 s1(fds, file_opens, nap_time, buffy);
588 printf("\t ==> Going to create and open %d files with "
589 "%d processes.\n", file_opens, file_opens);
590 endtime(" ");
591 break;
592 case 2:
593 starttime(
594 "\n st_0003{b}: stress open/close files with "
595 "RDONLY and WRONLY\n\t ");
596 printf("\t ==> Going to create and open %d files.\n",
597 file_opens);
598 s2(fds, file_opens);
599 endtime(" ");
600 break;
601 case 3:
602 starttime(
603 "\n st_0003{d}: stress read/write lock with "
604 "different boundary\n\t ");
605 printf("\t ==> Going to create and open %d files.\n",
606 file_opens);
607 s3(fds, file_opens);
608 endtime(" ");
609 break;
610 case 4:
611 starttime(
612 "\n st_0003{c}: stress unlink WRLCK files "
613 "without a close\n\t ");
614 printf("\t ==> Going to create and open %d files.\n",
615 file_opens);
616 s4(fds, file_opens);
617 endtime(" ");
618 break;
619 }
620
621 return (0);
622 }