Home | History | Annotate | Download | only in nfs_stress
      1 /*
      2  * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
      3  * Copyright (c) International Business Machines  Corp., 2001
      4  *
      5  * This program is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU General Public License as
      7  * published by the Free Software Foundation; either version 2 of
      8  * the License, or (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it would be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17 
     18  * Description:
     19  * This program is designed to stress the NFS implimentation. Many bugs were
     20  * uncovered in the AIX operating system implimentation of NFS when AIX kernel
     21  * was built over NFS. Source directory on a remote machine (one server many
     22  * clients) NFS-mounted on to a directory on a local machine from which the
     23  * kernel build was initiated. Apparently many defects/bugs were uncovered when
     24  * multiple users tried to build the kernel by NFS mounting the kernel source
     25  * from a remote machine and tried to build the kernel on a local machine.
     26  *
     27  * The test's aimed to stress NFS client/server and recreates such a senario.
     28  * Spawn N number of threads. Each thread does the following:
     29  *   * create a directory tree;
     30  *   * populate it with ".c" files and makefiles;
     31  *     hostname.1234
     32  *             | - 1234.0.0.c
     33  *             | - ..........
     34  *             | - makefile
     35  *             |_ 1234.0
     36  *                    |
     37  *                    | - 1234.1.0.c
     38  *                    | - ..........
     39  *                    | - makefile
     40  *                    |_ 1234.1
     41  *                           |....
     42  *
     43  *   * initate a build, executable will print hello world;
     44  *   * clean up all the executables that were created;
     45  *   * recurssively remove each subdir and its contents.
     46  *
     47  */
     48 
     49 #define _GNU_SOURCE
     50 #include <stdio.h>
     51 #include <sys/stat.h>
     52 #include <sys/wait.h>
     53 #include <unistd.h>
     54 #include <stdlib.h>
     55 #include <fcntl.h>
     56 #include <unistd.h>
     57 #include <pthread.h>
     58 #include <sys/mount.h>
     59 #include <linux/limits.h>
     60 #include <errno.h>
     61 #include <linux/unistd.h>
     62 
     63 #include "lapi/mkdirat.h"
     64 #include "tst_safe_pthread.h"
     65 #include "tst_safe_stdio.h"
     66 #include "tst_test.h"
     67 
     68 #define gettid() syscall(__NR_gettid)
     69 
     70 static int thrd_num = 8;
     71 static int dirs_num = 100;
     72 static int file_num = 100;
     73 
     74 static char *t_arg, *d_arg, *f_arg;
     75 
     76 static struct tst_option opts[] = {
     77 	{"t:", &t_arg, "-t x    Number of threads to generate, default: 8\n"},
     78 	{"d:", &d_arg, "-d x    Number of subdirs to generate, default: 100\n"},
     79 	{"f:", &f_arg, "-f x    Number of c files in each dir, default: 100\n"},
     80 	{NULL, NULL, NULL}
     81 };
     82 
     83 static void run_targets(const char *dirname, char *cfile, pid_t tid)
     84 {
     85 	int i, k, fd;
     86 	char subdir[PATH_MAX] = {0};
     87 	char *output_file;
     88 	char buf[11];
     89 	const char *const cmd_run[] = {cfile, NULL};
     90 
     91 	SAFE_ASPRINTF(&output_file, "%s/cmd.out", dirname);
     92 
     93 	/* run each binary */
     94 	for (i = 0; i < dirs_num; ++i) {
     95 		for (k = 0; k < file_num; ++k) {
     96 			snprintf(cfile, PATH_MAX, "%s%s/%d.%d.%d",
     97 				 dirname, subdir, tid, i, k);
     98 
     99 			tst_run_cmd(cmd_run, output_file, NULL, 0);
    100 
    101 			fd = SAFE_OPEN(output_file, O_RDONLY);
    102 			SAFE_READ(1, fd, buf, 11);
    103 			if (strncmp(buf, "hello world", 11))
    104 				tst_brk(TFAIL, "command printed wrong message");
    105 			SAFE_CLOSE(fd);
    106 		}
    107 		strcat(subdir, "/dir");
    108 	}
    109 
    110 	free(output_file);
    111 }
    112 
    113 static void *thread_fn(LTP_ATTRIBUTE_UNUSED void *args)
    114 {
    115 	const char prog_buf[] = "#include <stdio.h>\n"
    116 				"int main(void)\n{\n"
    117 				"\tprintf(\"hello world\");\n"
    118 				"\treturn 0;\n}\n";
    119 
    120 	const char make_buf_n[] = "CFLAGS := -O -w -g\n"
    121 				  "SRCS=$(wildcard *.c)\n"
    122 				  "TARGETS=$(SRCS:.c=)\n"
    123 				  "all: $(TARGETS)\n"
    124 				  "$(TARGETS): %: %.c\n"
    125 				  "\t$(CC) -o $@ $<\n"
    126 				  "clean:\n\trm -f $(TARGETS)\n"
    127 				  ".PHONY: all clean\n";
    128 
    129 	const char make_buf[] = "CFLAGS := -O -w -g\n"
    130 				"SUBDIR = dir\n"
    131 				"SRCS=$(wildcard *.c)\n"
    132 				"TARGETS=$(SRCS:.c=)\n"
    133 				"all: $(SUBDIR) $(TARGETS)\n"
    134 				"$(TARGETS): %: %.c\n"
    135 				"\t$(CC) -o $@ $<\n"
    136 				"$(SUBDIR):\n\t$(MAKE) -C $@\n"
    137 				"clean:\n"
    138 				"\trm -f $(TARGETS)\n"
    139 				"\t$(MAKE) -C $(SUBDIR) clean\n"
    140 				".PHONY: all $(SUBDIR) clean\n";
    141 
    142 	int i, k, fd, dirfd, ret;
    143 	char *dirname;
    144 	char cfile[PATH_MAX];
    145 	char hostname[256];
    146 	pid_t tid = gettid();
    147 
    148 	SAFE_GETHOSTNAME(hostname, 256);
    149 	SAFE_ASPRINTF(&dirname, "%s.%ld", hostname, tid);
    150 
    151 	SAFE_MKDIR(dirname, 0755);
    152 	dirfd = SAFE_OPEN(dirname, O_DIRECTORY);
    153 
    154 	for (i = 0; i < dirs_num; ++i) {
    155 
    156 		fd = openat(dirfd, "makefile", O_CREAT | O_RDWR,
    157 			    S_IRWXU | S_IRWXG | S_IRWXO);
    158 		if (fd < 0)
    159 			tst_brk(TFAIL | TERRNO, "openat(makefile) failed");
    160 
    161 		if (i == dirs_num - 1)
    162 			SAFE_WRITE(1, fd, make_buf_n, sizeof(make_buf_n) - 1);
    163 		else
    164 			SAFE_WRITE(1, fd, make_buf, sizeof(make_buf) - 1);
    165 
    166 		SAFE_CLOSE(fd);
    167 
    168 		for (k = 0; k < file_num; ++k) {
    169 			snprintf(cfile, PATH_MAX, "%d.%d.%d.c", tid, i, k);
    170 			fd = openat(dirfd, cfile, O_CREAT | O_RDWR,
    171 				    S_IRWXU | S_IRWXG | S_IRWXO);
    172 			if (fd < 0) {
    173 				tst_brk(TFAIL | TERRNO,
    174 					"openat(%s) failed", cfile);
    175 			}
    176 
    177 			SAFE_WRITE(1, fd, prog_buf, sizeof(prog_buf) - 1);
    178 			SAFE_CLOSE(fd);
    179 		}
    180 
    181 		if (i == dirs_num - 1)
    182 			break;
    183 
    184 		ret = mkdirat(dirfd, "dir", 0755);
    185 		if (ret < 0)
    186 			tst_brk(TFAIL | TERRNO, "mkdirat('dir') failed");
    187 		dirfd = openat(dirfd, "dir", O_DIRECTORY);
    188 		if (dirfd < 0)
    189 			tst_brk(TFAIL | TERRNO, "openat('dir') failed");
    190 	}
    191 
    192 	const char *const cmd_make[] = {"make", "-s", "-C", dirname, NULL};
    193 	const char *const cmd_make_clean[] = {
    194 		"make", "-C", dirname, "-s", "clean", NULL};
    195 
    196 	tst_run_cmd(cmd_make, NULL, NULL, 0);
    197 
    198 	run_targets(dirname, cfile, tid);
    199 
    200 	tst_run_cmd(cmd_make_clean, NULL, NULL, 0);
    201 
    202 	free(dirname);
    203 
    204 	return NULL;
    205 }
    206 
    207 static void setup(void)
    208 {
    209 	thrd_num = atoi(t_arg);
    210 	dirs_num = atoi(d_arg);
    211 	file_num = atoi(f_arg);
    212 }
    213 
    214 static void do_test(void)
    215 {
    216 	int i;
    217 	pthread_t id[thrd_num];
    218 
    219 	for (i = 0; i < thrd_num; ++i)
    220 		SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, NULL);
    221 
    222 	for (i = 0; i < thrd_num; ++i)
    223 		SAFE_PTHREAD_JOIN(id[i], NULL);
    224 
    225 	tst_res(TPASS, "'make' successfully build and clean all targets");
    226 }
    227 
    228 static struct tst_test test = {
    229 	.options = opts,
    230 	.test_all = do_test,
    231 	.setup = setup,
    232 };
    233