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 TESTCASE "Stress create, write, close, sleep, open, and delete\n\t "
32 #define BUFF_32k 32*1024
33
34 #define MIN(a, b) ((a) < (b) ? (a) : (b))
35
36 void usage(int);
37 void fill_buffer(char *, int);
38 int mk_file(char *, int, char *, int);
39 void ck_file(int, int, char *);
40 void ck_locking(int, int);
41
42 /*
43 * Prints out the usage message and exits with the
44 * passed in exit code "ec"
45 */
46 void
47 usage(int ec)
48 {
49 printf("\t Test UNINITIATED: ");
50
51 printf("usage: %s -u <u.g> -d <dbg> -b <base_dir> -s <fsize> \n"
52 " -n <ocnt> -l -v -W <tc_nap>\n", testname);
53 puts("\t -u <u.g> Uid (u) & Gid (g) to become (numeric value)\n");
54 puts("\t -d <dbg> Information level: 0 - none \n"
55 "\t 1 - errors \n"
56 "\t 2 - errors and debug\n");
57 puts("\t -b <base_dir> The directory under which to create files\n");
58 puts("\t -s <fsize> The size to make the files created\n");
59 puts("\t -n <ocnt> Number of files to create and open\n");
60 puts("\t -l Skip the locking test\n");
61 puts("\t -v Skip the file content validation test\n");
62 puts("\t -W <tc_nap> Pause period (in seconds) between \n"
63 "\t create/clsoe and open/lock/validate/unlink\n");
64 exit(ec);
65 }
66
67 /*
68 * Given a buffer pointer "ptr" fill it with a numerically
69 * increasing byte value for its "size"
70 */
71 void
72 fill_buffer(char *ptr, int size)
73 {
74 int i;
75
76 for (i = 0; i < size; i++) {
77 ptr[i] = (i & 0xff);
78 }
79 }
80
81 /*
82 * Create a file using "path" and "file_flags" then write
83 * the data described by "buf" and size.
84 *
85 * Returns: Open file descritor.
86 */
87 int
88 mk_file(char *path, int file_flags, char *buf, int size)
89 {
90 int fd, rc;
91
92 /*
93 * create and open a file
94 */
95 if ((fd = open_file(path, file_flags, 0777)) < 0) {
96 printf("\t Test UNINITIATED: open_file() failed\n");
97 return (NOOK);
98 }
99
100 if ((rc = write(fd, buf, size)) != size) {
101 printf("\t Test UNINITIATED: ");
102 eprint("error on write (%d)\n", errno);
103 close_file(fd, path);
104 return (NOOK);
105 }
106 return (fd);
107 }
108
109 /*
110 * The intent is to verify that the file "fd" still matches
111 * "buffy" which is a 32k buffer setup by fill_buffer for
112 * the size of the file "file_size".
113 */
114 void
115 ck_file(int fd, int file_size, char *buffy)
116 {
117 int blk = 0, rc, wc, fs = file_size;
118 char yffub[BUFF_32k];
119
120
121 do {
122 /*
123 * compare in 32k blocks.
124 */
125 wc = MIN(fs, BUFF_32k);
126
127 if ((rc = read(fd, yffub, wc)) != wc) {
128 fprintf(stderr, "\t Test FAIL: ");
129 Perror("read():");
130 exit(NOOK);
131 }
132
133 if ((rc = memcmp(buffy, yffub, wc)) != 0) {
134 fprintf(stderr, "\t Test FAIL: ");
135 eprint("(rc=%d) written != read "
136 " in 32k blk @offset %d\n",
137 rc, blk);
138 exit(NOOK);
139 }
140 blk += wc;
141 fs -= wc;
142 } while (fs);
143 }
144
145 /*
146 * First accuire a write lock on "fd" for the entire file.
147 * Then fork a child to hammer away attempting to accuire
148 * a write lock on thte same file. In the meantime the
149 * parent sleeps for 5 seconds
150 */
151 void
152 ck_locking(int fd, int mand)
153 {
154 int stat, s;
155 pid_t kid;
156
157 /* parent will lock file first.. */
158 if (lock_reg(fd, mand, F_WRLCK, 0, 0, 0) != OK) {
159 /* error */
160 fprintf(stderr, "\t Test FAIL: ");
161 eprint("lock: not acquired\n");
162 return;
163 }
164
165 if ((kid = fork()) == 0) { /* child.. */
166 /* try not to print the conflict lock error on debug=1 */
167 s = showerror;
168 if (debug == 0)
169 showerror = 0;
170 /* try conflict lock until parent release the lock */
171 while (lock_reg(fd, mand, F_WRLCK, 0, 0, 0) != OK) {
172 if (errno != EAGAIN) {
173 fprintf(stderr, "\t Test FAIL: ");
174 Perror("Child: WRLCK");
175 }
176 }
177 showerror = s;
178
179 if (lock_reg(fd, mand, F_UNLCK, 0, 0, 0) != OK) {
180 fprintf(stderr, "\t Test FAIL: ");
181 eprint("Child: unlock failed\n");
182 }
183 exit(0);
184 } else { /* parent.. */
185 sleep(5);
186 if (lock_reg(fd, mand, F_UNLCK, 0, 0, 0) != OK) {
187 fprintf(stderr, "\t Test FAIL: ");
188 eprint("unlock failed\n");
189 }
190 /* hang about for the kid .. */
191 wait(&stat);
192 }
193 }
194
195
196
197 /*
198 * Main test loop.
199 */
200 int
201 main(int argc, char *argv[])
202 {
203 extern int optind;
204 extern char *optarg;
205
206 char buffy[BUFF_32k];
207 char fileGumbo[25];
208
209 char *buf, *base_dir = NULL;
210
211 int *fds, c;
212
213 int do_lock_checking = 1, do_content_validation = 1, nap_time = 30;
214
215 int file_opens = 250;
216 int file_size = BUFF_32k;
217 int file_flags = (O_CREAT|O_TRUNC|O_RDWR);
218 int mand = 0;
219
220 struct rlimit rlp;
221
222 testname = argv[0];
223 printf("\n %s: ", testname);
224 starttime(TESTCASE);
225
226 while ((c = getopt(argc, argv, "W:d:b:s:n:lv")) != -1) {
227
228 switch (c) {
229 /* Skip the lock checks */
230 case 'l':
231 do_lock_checking = 0;
232 break;
233 /* Skip the content validation */
234 case 'v':
235 do_content_validation = 0;
236 break;
237 /* number of files to open */
238 case 'n':
239 file_opens = atoi(optarg);
240 break;
241 /* the size of a file */
242 case 's':
243 file_size = atoi(optarg);
244 break;
245 /* base directory into which chdir and create files */
246 case 'b':
247 base_dir = optarg;
248 break;
249 /*
250 * debug flags treated as bit flags for
251 * debug and showerror
252 */
253 case 'd':
254 switch (atoi(optarg)) {
255 case 0:
256 debug = 0;
257 showerror = 0;
258 break;
259 case 1:
260 debug = 0;
261 showerror = 1;
262 break;
263 case 2:
264 debug = 1;
265 showerror = 1;
266 break;
267 default:
268 usage(-1);
269 }
270 break;
271
272 /* the user wants me to nap... */
273 case 'W':
274 nap_time = atoi(optarg);
275 break;
276 default:
277 usage(-1);
278 }
279 }
280
281 if (base_dir == NULL) {
282 fprintf(stderr, "\t Test UNINITIATED: ");
283 printf(" specify base directory via -b\n");
284 usage(-1);
285 }
286
287 if (getrlimit(RLIMIT_NOFILE, &rlp) < 0) {
288 fprintf(stderr, "\t Test UNINITIATED: ");
289 eprint("getrlimit() for RLIMIT_NOFILE failed\n");
290 Perror(":-(");
291 return (-1);
292 }
293
294 dprint("RLMIT_NOFILE is cur=%d / max=%d\n", rlp.rlim_cur, rlp.rlim_max);
295
296 /*
297 * The user has specified a number of files to open.
298 * We may have to increase the process limit for MAX
299 * open files. We also need to account for the fact
300 * that the process already has 3 open file descriptors
301 * for std{in,our,err}.
302 */
303 if (file_opens+3 > rlp.rlim_cur) {
304 rlp.rlim_cur = MIN(rlp.rlim_max, file_opens+3);
305 if (rlp.rlim_cur <= file_opens)
306 file_opens = rlp.rlim_cur-3;
307
308 if (setrlimit(RLIMIT_NOFILE, &rlp) < 0) {
309 eprint("setrlimit() for RLIMIT_NOFILE failed\n");
310 Perror(":-/");
311 return (-1);
312 }
313 }
314
315 fill_buffer(buffy, BUFF_32k);
316
317 if (chdir(base_dir) != 0) {
318 fprintf(stderr, "\t Test UNINITIATED: ");
319 eprint("chdir() to %s failed\n", base_dir);
320 Perror("chdir()");
321 return (-1);
322 }
323
324 if ((fds = malloc(sizeof (int)*file_opens)) == NULL) {
325 fprintf(stderr, "\t Test UNINITIATED: ");
326 eprint("malloc for RLIMIT_NOFILE fds failed\n");
327 Perror(":-(");
328 return (-1);
329 }
330
331 /*
332 * generate file names and files.
333 */
334 for (c = 0; c < file_opens; c++) {
335 sprintf(fileGumbo, "F%06d", c);
336 fds[c] = mk_file(fileGumbo, file_flags, buffy, BUFF_32k);
337 }
338
339 /*
340 * Close all the files.
341 */
342 for (c = 0; c < file_opens; c++) {
343 sprintf(fileGumbo, "F%06d", c);
344 close_file(fds[c], fileGumbo);
345 }
346
347 /*
348 * Take a nap if the user wants ya to..
349 */
350 if (nap_time) {
351 sleep(nap_time);
352 }
353
354 /*
355 * Now re-open the files and check the contents.
356 */
357 for (c = 0; c < file_opens; c++) {
358 sprintf(fileGumbo, "F%06d", c);
359 fds[c] = open_file(fileGumbo, O_RDWR, 0);
360 if (fds[c] < 0) {
361 eprint("EEK!.. open failed");
362 } else {
363 /*
364 * Optionally perform some locking checks.
365 */
366 if (do_lock_checking) {
367 ck_locking(fds[c], mand);
368 }
369 /*
370 * Do optional content validation
371 */
372 if (do_content_validation) {
373 ck_file(fds[c], file_size, &buffy[0]);
374 }
375 }
376 /* and now.. delete it without clsoe (it a test) */
377 unlink(fileGumbo);
378 }
379
380 printf("\t ==> Going to create and open %d files.\n", file_opens);
381
382 printf("\t Test PASS: ");
383 printf("%s completed execution runs.\n", testname);
384 endtime(" ");
385
386 return (0);
387 }