All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
@ 2022-01-25  2:47 Murphy Zhou
  2022-01-25  7:29 ` Amir Goldstein
  2022-01-25 11:04 ` Cyril Hrubis
  0 siblings, 2 replies; 18+ messages in thread
From: Murphy Zhou @ 2022-01-25  2:47 UTC (permalink / raw)
  To: ltp

Exercise fsnotify and inotify interfaces while IO going on.

Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
---
 testcases/kernel/fs/fs-notify/Makefile        |  18 ++
 .../fs/fs-notify/fanotify_flush_stress.c      |  52 ++++
 .../fs/fs-notify/fanotify_init_stress.c       |  32 +++
 .../fs/fs-notify/fanotify_mark_stress.c       |  58 +++++
 .../kernel/fs/fs-notify/fanotify_watch.c      | 244 ++++++++++++++++++
 testcases/kernel/fs/fs-notify/freadfile.c     |  27 ++
 .../kernel/fs/fs-notify/fsnotify-stress.sh    |  64 +++++
 testcases/kernel/fs/fs-notify/fwritefile.c    |  27 ++
 .../fs/fs-notify/inotify_add_rm_stress.c      |  36 +++
 .../kernel/fs/fs-notify/inotify_init_stress.c |  22 ++
 .../fs/fs-notify/inotify_inmodify_stress.c    |  17 ++
 testcases/kernel/fs/fs-notify/inotify_watch.c |  98 +++++++
 testcases/kernel/fs/fs-notify/readfile.c      |  28 ++
 testcases/kernel/fs/fs-notify/rw_files.sh     |  90 +++++++
 testcases/kernel/fs/fs-notify/writefile.c     |  28 ++
 15 files changed, 841 insertions(+)
 create mode 100644 testcases/kernel/fs/fs-notify/Makefile
 create mode 100644 testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/fanotify_init_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/fanotify_watch.c
 create mode 100644 testcases/kernel/fs/fs-notify/freadfile.c
 create mode 100755 testcases/kernel/fs/fs-notify/fsnotify-stress.sh
 create mode 100644 testcases/kernel/fs/fs-notify/fwritefile.c
 create mode 100644 testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/inotify_init_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
 create mode 100644 testcases/kernel/fs/fs-notify/inotify_watch.c
 create mode 100644 testcases/kernel/fs/fs-notify/readfile.c
 create mode 100755 testcases/kernel/fs/fs-notify/rw_files.sh
 create mode 100644 testcases/kernel/fs/fs-notify/writefile.c

diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
new file mode 100644
index 000000000..328783942
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/Makefile
@@ -0,0 +1,18 @@
+#
+#    kernel/fs/fs-notify testcases Makefile.
+#
+
+top_srcdir			?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+BINARIES:=fanotify_flush_stress fanotify_init_stress \
+fanotify_mark_stress fanotify_watch inotify_watch \
+inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
+writefile freadfile fwritefile readfile
+
+INSTALL_TARGETS			:= fsnotify-stress.sh $(BINARIES) rw_files.sh
+
+MAKE_TARGETS			:= $(BINARIES)
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
new file mode 100644
index 000000000..493acfb56
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
@@ -0,0 +1,52 @@
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fanotify.h>
+#include <unistd.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+	char buf;
+	int fd;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	if (fd == -1) {
+		perror("fanotify_init");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Loop marking all kinds of events and flush */
+	while (1) {
+
+		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
+
+			perror("fanotify_mark add");
+
+		if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
+						0, -1, argv[1]) == -1)
+			perror("fanotify_mark flush mount");
+
+		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
+
+			perror("fanotify_mark add");
+
+		if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
+			perror("fanotify_mark flush");
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
new file mode 100644
index 000000000..d11924b57
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
@@ -0,0 +1,32 @@
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/fanotify.h>
+
+int main(int argc, char *argv[])
+{
+	char buf;
+	int fd;
+	while (1) {
+
+		/* Create the file descriptor for accessing the fanotify API */
+		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
+				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
+		if (fd == -1)
+			perror("fanotify_init");
+
+		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
+				argv[1]) == -1)
+			perror("fanotify_mark");
+
+		close(fd);
+	}
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
new file mode 100644
index 000000000..7f648e103
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
@@ -0,0 +1,58 @@
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fanotify.h>
+#include <unistd.h>
+#include <string.h>
+
+void add_mark(int fd, uint64_t mask, char *path)
+{
+	if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
+		perror("fanotify_mark add");
+}
+
+void remove_mark(int fd, uint64_t mask, char *path)
+{
+	if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
+		perror("fanotify_mark remove");
+}
+
+int main(int argc, char *argv[])
+{
+	char buf;
+	int fd;
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	if (fd == -1) {
+		perror("fanotify_init");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Loop marking all kinds of events */
+	while (1) {
+		add_mark(fd, FAN_ACCESS, argv[1]);
+		remove_mark(fd, FAN_ACCESS, argv[1]);
+		add_mark(fd, FAN_MODIFY, argv[1]);
+		remove_mark(fd, FAN_MODIFY, argv[1]);
+		add_mark(fd, FAN_OPEN_PERM, argv[1]);
+		remove_mark(fd, FAN_OPEN_PERM, argv[1]);
+		add_mark(fd, FAN_CLOSE, argv[1]);
+		remove_mark(fd, FAN_CLOSE, argv[1]);
+		add_mark(fd, FAN_OPEN, argv[1]);
+		remove_mark(fd, FAN_OPEN, argv[1]);
+		add_mark(fd, FAN_ACCESS_PERM, argv[1]);
+		remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
+		add_mark(fd, FAN_ONDIR, argv[1]);
+		remove_mark(fd, FAN_ONDIR, argv[1]);
+		add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
+		remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
new file mode 100644
index 000000000..1028e0e30
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
@@ -0,0 +1,244 @@
+/* Example in man 7 fanotify */
+
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fanotify.h>
+#include <unistd.h>
+#include <string.h>
+
+/* get comm for pid from /proc/pid/comm */
+static char * get_comm_pid(unsigned int pid)
+{
+	char * proc_name;
+	char * comm;
+	int comm_fd, cnt;
+
+	proc_name = (char *)malloc(50);
+	if (proc_name != NULL)
+		sprintf(proc_name, "/proc/%u/comm\0", pid);
+	else
+		return NULL;
+
+	comm = (char *)malloc(50);
+	if (comm != NULL)
+		memset(comm, 0, 50);
+	else
+		return NULL;
+
+	comm_fd = open(proc_name, O_RDONLY);
+	if (comm_fd != -1) {
+		cnt = read(comm_fd, comm, 50);
+		if (cnt != -1) {
+			comm[cnt] = '\0';
+			close(comm_fd);
+			return comm;
+		}
+		close(comm_fd);
+	}
+
+	return NULL;
+}
+
+/* Read all available fanotify events from the file descriptor 'fd' */
+
+static void handle_events(int fd)
+{
+	const struct fanotify_event_metadata *metadata;
+	struct fanotify_event_metadata buf[200];
+	ssize_t len;
+	char path[PATH_MAX];
+	ssize_t path_len;
+	char procfd_path[PATH_MAX];
+	struct fanotify_response response;
+
+	/* Loop while events can be read from fanotify file descriptor */
+	for(;;) {
+
+		/* Read some events */
+		len = read(fd, (void *) &buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN) {
+			perror("read");
+			exit(EXIT_FAILURE);
+		}
+
+		/* Check if end of available data reached */
+		if (len <= 0)
+			break;
+
+		/* Point to the first event in the buffer */
+		metadata = buf;
+
+		/* Loop over all events in the buffer */
+		while (FAN_EVENT_OK(metadata, len)) {
+
+			/* Check that run-time and compile-time structures match */
+
+			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+				fprintf(stderr,
+				    "Mismatch of fanotify metadata version.\n");
+				exit(EXIT_FAILURE);
+			}
+
+			/* metadata->fd contains either FAN_NOFD, indicating a
+			   queue overflow, or a file descriptor (a nonnegative
+			   integer). Here, we simply ignore queue overflow. */
+
+			if (metadata->fd >= 0) {
+
+				/* Handle open permission event */
+				if (metadata->mask & FAN_OPEN_PERM) {
+					printf("FAN_OPEN_PERM: ");
+
+					/* Allow file to be opened */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle access permission event */
+				if (metadata->mask & FAN_ACCESS_PERM) {
+					printf("FAN_ACCESS_PERM: ");
+
+					/* Allow file to be accessed */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle closing of writable file event */
+				if (metadata->mask & FAN_CLOSE_WRITE)
+					printf("FAN_CLOSE_WRITE: ");
+
+				/* Handle closing of not writable file event */
+				if (metadata->mask & FAN_CLOSE_NOWRITE)
+					printf("FAN_CLOSE_NOWRITE: ");
+
+				/* Handle modify file event */
+				if (metadata->mask & FAN_MODIFY)
+					printf("FAN_MODIFY: ");
+
+				/* Handle open event */
+				if (metadata->mask & FAN_OPEN)
+					printf("FAN_OPEN: ");
+
+				/* Handle access event */
+				if (metadata->mask & FAN_ACCESS)
+					printf("FAN_ACCESS: ");
+
+				/* Handle access event */
+				if (metadata->mask & FAN_ONDIR)
+					printf("FAN_ONDIR: ");
+
+				/* Handle access event */
+				if (metadata->mask & FAN_EVENT_ON_CHILD)
+					printf("FAN_EVENT_ON_CHILD: ");
+
+				/* Retrieve/print the accessed file path*/
+				snprintf(procfd_path, sizeof(procfd_path),
+					    "/proc/self/fd/%d", metadata->fd);
+				path_len = readlink(procfd_path, path,
+					    sizeof(path) - 1);
+				if (path_len == -1) {
+					perror("readlink");
+					exit(EXIT_FAILURE);
+				}
+
+				path[path_len] = '\0';
+				printf("File %s.\t\t Comm %s", path,
+					get_comm_pid(metadata->pid));
+
+				/* Close the file descriptor of the event */
+
+				close(metadata->fd);
+			}
+
+			/* Advance to next event */
+			metadata = FAN_EVENT_NEXT(metadata, len);
+		}
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	char buf;
+	int fd, poll_num;
+	nfds_t nfds;
+	struct pollfd fds[2];
+	FILE *f;
+#if 0
+	/* Check mount point is supplied */
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+#endif
+	printf("%s on %s\n", argv[0], argv[1]);
+	fflush(stdout);
+	f = freopen("fanotify.log", "w+", stdout);
+	if (f == NULL) {
+		perror("freopen");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	if (fd == -1) {
+		perror("fanotify_init");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Mark the mount for:
+	   - permission events before opening files
+	   - notification events after closing a write-enabled
+		 file descriptor */
+	if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+			FAN_ONDIR | FAN_EVENT_ON_CHILD,
+			-1, argv[1]) == -1) {
+
+		perror("fanotify_mark");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Prepare for polling */
+	nfds = 1;
+
+	/* Fanotify input */
+	fds[0].fd = fd;
+	fds[0].events = POLLIN;
+
+	/* This is the loop to wait for incoming events */
+	printf("Listening for events.\n");
+	while (1) {
+		poll_num = poll(fds, nfds, -1);
+		if (poll_num == -1) {
+			if (errno == EINTR)     /* Interrupted by a signal */
+				continue;           /* Restart poll() */
+
+			perror("poll");         /* Unexpected error */
+			exit(EXIT_FAILURE);
+		}
+
+		if (poll_num > 0) {
+
+			if (fds[0].revents & POLLIN) {
+
+				/* Fanotify events are available */
+				handle_events(fd);
+			}
+		}
+	}
+
+	fclose(f);
+	printf("Listening for events stopped.\n");
+	exit(EXIT_SUCCESS);
+}
diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
new file mode 100644
index 000000000..24bf76bcd
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/freadfile.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, char **argv)
+{
+	int fd;
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while(1) {
+		f = fopen(argv[1], "r+");
+		if (f == NULL) {
+			perror("freadfile fopen");
+			exit(EXIT_FAILURE);
+		}
+		fread(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+	}
+	fclose(f);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
new file mode 100755
index 000000000..d6fd5482b
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+export TIMEOUT=10
+
+STRESSES="fanotify_flush_stress fanotify_init_stress \
+fanotify_mark_stress fanotify_watch inotify_watch \
+inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
+IO_OPES="writefile freadfile fwritefile readfile"
+
+NR_CPUS=$(grep -c processor /proc/cpuinfo)
+[ $NR_CPUS -lt 4 ] && NR_CPUS=4
+
+function cleanup_processes()
+{
+	while ps -eo pid,comm | \
+		grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
+		killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
+		sleep 1
+	done
+}
+
+function cleanup()
+{
+	sleep 3 # waiting possible unfinished processes
+	cleanup_processes
+	rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
+	cd /
+	sync
+}
+
+function run_stress()
+{
+	local i j rcnt=0
+	echo -e "* CPU count: $NR_CPUS"
+	echo -e "* TIMEOUT for each subcase: $TIMEOUT"
+
+	echo -e "* Starting fsnotify stress on directory and regular file"
+	touch $TMPDIR/testfile
+	>tmp
+	for i in $STRESSES $IO_OPES rw_files.sh; do
+		for j in $STRESSES ; do
+			[ "$i" == "$j" ] && continue
+			# skip duplicate combinations
+			grep -w $j tmp | grep -qw $i && continue
+			echo -e "* Starting $i and $j, rcnt $rcnt"
+			./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
+			./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
+			./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
+			./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
+			sleep $TIMEOUT
+			cleanup_processes
+			echo -e "$i $j" >> tmp
+			((rcnt++))
+		done
+	done
+}
+
+trap "cleanup; exit;" 2
+
+echo "***** Starting tests *****"
+run_stress
+
+echo -e "\n***** Cleanup fanotify inotify device *****"
+cleanup
diff --git a/testcases/kernel/fs/fs-notify/fwritefile.c b/testcases/kernel/fs/fs-notify/fwritefile.c
new file mode 100644
index 000000000..a2956d60b
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/fwritefile.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, char **argv)
+{
+	int fd;
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while(1) {
+		f = fopen(argv[1], "w+");
+		if (f == NULL) {
+			perror("fwritefile fopen");
+			exit(EXIT_FAILURE);
+		}
+		fwrite(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+	}
+	fclose(f);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
new file mode 100644
index 000000000..e88fc088e
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
@@ -0,0 +1,36 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+
+int main(int argc, char *argv[])
+{
+	int notify_fd;
+	int wd, ret;
+
+	notify_fd = inotify_init1(IN_CLOEXEC);
+	if (notify_fd == -1) {
+		perror("inotify_init1");
+		return -1;
+	}
+
+	while (1) {
+		wd = inotify_add_watch(notify_fd, argv[1],
+			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
+			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
+			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
+			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+		if (wd < 0)
+			perror("inotify_add_watch");
+
+		ret = inotify_rm_watch(notify_fd, wd);
+		if (ret < 0)
+			perror("inotify_rm_watch");
+	}
+	close(notify_fd);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/inotify_init_stress.c b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
new file mode 100644
index 000000000..62cb7c2e6
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
@@ -0,0 +1,22 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+
+int main(int argc, char *argv[])
+{
+	int notify_fd;
+	int wd;
+
+	while (1) {
+		notify_fd = inotify_init1(IN_CLOEXEC);
+		if (notify_fd == -1)
+			perror("inotify_init1");
+		close(notify_fd);
+	}
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
new file mode 100644
index 000000000..546ccb76f
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
@@ -0,0 +1,17 @@
+#include <sys/inotify.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * Calls inotify_rm_watch in a loop.
+ */
+int main(int argc, char **argv)
+{
+	int fd = inotify_init();
+	while (1) {
+		int wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
+		inotify_rm_watch(fd, wd);
+	}
+	close(fd);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/inotify_watch.c b/testcases/kernel/fs/fs-notify/inotify_watch.c
new file mode 100644
index 000000000..b4b908b6b
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/inotify_watch.c
@@ -0,0 +1,98 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <sys/inotify.h>
+
+int main(int argc, char *argv[])
+{
+	int notify_fd;
+	int wd, ret;
+	char *buf;
+	FILE *f;
+
+	f = freopen("inotify.log", "w+", stdout);
+	if (f == NULL) {
+		perror("freopen");
+		exit(EXIT_FAILURE);
+	}
+
+	buf = malloc(sizeof(struct inotify_event) + NAME_MAX + 1);
+	if (buf == NULL) {
+		perror("malloc");
+		return -1;
+	}
+
+	notify_fd = inotify_init1(IN_CLOEXEC);
+	if (notify_fd == -1) {
+		perror("inotify_init1");
+		return -1;
+	}
+
+	wd = inotify_add_watch(notify_fd, argv[1],
+		IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE |
+		IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
+		IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+
+	if (wd < 0) {
+		perror("inotify_add_watch");
+		return -1;
+	}
+
+	while (ret = read(notify_fd, buf, NAME_MAX) != -1) {
+		struct inotify_event *ip = (struct inotify_event *)buf;
+		printf("name %s event ", ip->name);
+		switch (ip->mask) {
+		case IN_ACCESS:
+			printf("access\n");
+			break;
+		case IN_ATTRIB:
+			printf("attrib\n");
+			break;
+		case IN_CLOSE_WRITE:
+			printf("close write\n");
+			break;
+		case IN_CLOSE_NOWRITE:
+			printf("close nowrite\n");
+			break;
+		case IN_CREATE:
+			printf("create\n");
+			break;
+		case IN_DELETE:
+			printf("delete\n");
+			break;
+		case IN_DELETE_SELF:
+			printf("deleteself\n");
+			break;
+		case IN_MODIFY:
+			printf("modify\n");
+			break;
+		case IN_MOVE_SELF:
+			printf("move self\n");
+			break;
+		case IN_MOVED_FROM:
+			printf("move from\n");
+			break;
+		case IN_MOVED_TO:
+			printf("move to\n");
+			break;
+		case IN_OPEN:
+			printf("open\n");
+			break;
+		default:
+			printf("\n");
+			break;
+		};
+	}
+
+	ret = inotify_rm_watch(notify_fd, wd);
+	if (ret < 0)
+		perror("inotify_rm_watch");
+
+	fclose(f);
+	close(notify_fd);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/readfile.c b/testcases/kernel/fs/fs-notify/readfile.c
new file mode 100644
index 000000000..2ab1a3694
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/readfile.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, char **argv)
+{
+	int fd, ret;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(argv[1], O_RDWR);
+		if (fd == -1) {
+			perror("readfile open");
+			exit(EXIT_FAILURE);
+		}
+		ret = read(fd, buf, BUFSIZ);
+		if (ret == -1)
+			perror("readfile read");
+		usleep(1);
+	}
+	close(fd);
+	return 0;
+}
diff --git a/testcases/kernel/fs/fs-notify/rw_files.sh b/testcases/kernel/fs/fs-notify/rw_files.sh
new file mode 100755
index 000000000..bb3387160
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/rw_files.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+
+TDIR=$1
+TIMEO=$2
+
+[ -z "$TDIR" ] && TDIR=/tmp/
+[ -d $TDIR ] || TDIR=/tmp/
+[ -z "$TIMEO" ] && TIMEO=1m
+
+[ ! -d $TDIR ] && exit
+
+function add_files()
+{
+	local i=0
+
+	while true ; do
+
+		touch f-$i
+		ln -s f-$i f-$i-sym
+		ln f-$i f-$i-hdl
+
+		mkdir d-$i
+
+		mknod c-$i c 1 2
+		mknod b-$i b 1 2
+		mknod p-$i p
+
+		((i++))
+		[ -e stoptest ] && { echo $FUNCNAME; exit; }
+	done
+}
+
+function mv_files()
+{
+	local i=0
+
+	while true ; do
+
+		mv -f f-$i f-$i-rename
+		mv -f f-$i-rename f-$i
+		((i++))
+		[ -e stoptest ] && { echo $FUNCNAME; exit; }
+	done
+}
+
+function read_files()
+{
+	while true ; do
+
+		find .
+		cat f-*
+		ls d-*
+		[ -e stoptest ] && { echo $FUNCNAME; exit; }
+	done
+}
+
+function write_files()
+{
+	while true ; do
+
+		for j in f-* d-* c-* b-* p-* ; do
+			echo 1 > $j
+			echo 2 >> $j
+			[ -e stoptest ] && { echo $FUNCNAME; exit; }
+		done
+		[ -e stoptest ] && { echo $FUNCNAME; exit; }
+	done
+}
+
+function rm_files()
+{
+	while true ; do
+
+		rm -rf d-* f-* c-* b-* p-*
+		sleep 3
+		[ -e stoptest ] && { echo $FUNCNAME; exit; }
+	done
+}
+
+pushd $TDIR > /dev/null 2>&1
+rm -f stoptest
+add_files > /dev/null 2>&1 &
+mv_files > /dev/null 2>&1 &
+read_files > /dev/null 2>&1 &
+write_files > /dev/null 2>&1 &
+rm_files > /dev/null 2>&1 &
+popd > /dev/null 2>&1
+
+sleep $TIMEO
+touch $TDIR/stoptest
diff --git a/testcases/kernel/fs/fs-notify/writefile.c b/testcases/kernel/fs/fs-notify/writefile.c
new file mode 100644
index 000000000..9025b5d95
--- /dev/null
+++ b/testcases/kernel/fs/fs-notify/writefile.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, char **argv)
+{
+	int fd, ret;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(argv[1], O_RDWR);
+		if (fd == -1) {
+			perror("writefile open");
+			exit(EXIT_FAILURE);
+		}
+		ret = write(fd, buf, BUFSIZ);
+		if (ret == -1)
+			perror("writefile write");
+		usleep(1);
+	}
+	close(fd);
+	return 0;
+}
-- 
2.31.1


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25  2:47 [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests Murphy Zhou
@ 2022-01-25  7:29 ` Amir Goldstein
  2022-01-25  8:30   ` Murphy Zhou
  2022-01-25 11:04 ` Cyril Hrubis
  1 sibling, 1 reply; 18+ messages in thread
From: Amir Goldstein @ 2022-01-25  7:29 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: Jan Kara, LTP List

On Tue, Jan 25, 2022 at 4:47 AM Murphy Zhou <jencce.kernel@gmail.com> wrote:
>
> Exercise fsnotify and inotify interfaces while IO going on.

Hi Murphy,

It's worth documenting in main test script that this test
was created as a regression test for commit
4396a73115fc fsnotify: fix sb_connectors leak

Can you elaborate a bit more about the expectation from this test.
I do not see any error checking in the main script.
Is any perror supposed to fail the test?
Is its purpose to catch kernel dmesg splats of all sorts?

In your original post you wrote that bug makes the test hang:
https://lore.kernel.org/linux-fsdevel/20210907063338.ycaw6wvhzrfsfdlp@xzhoux.usersys.redhat.com/

If that is the case, better mention this to the LTP maintainers.

Thanks,
Amir.




>
> Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> ---
>  testcases/kernel/fs/fs-notify/Makefile        |  18 ++
>  .../fs/fs-notify/fanotify_flush_stress.c      |  52 ++++
>  .../fs/fs-notify/fanotify_init_stress.c       |  32 +++
>  .../fs/fs-notify/fanotify_mark_stress.c       |  58 +++++
>  .../kernel/fs/fs-notify/fanotify_watch.c      | 244 ++++++++++++++++++
>  testcases/kernel/fs/fs-notify/freadfile.c     |  27 ++
>  .../kernel/fs/fs-notify/fsnotify-stress.sh    |  64 +++++
>  testcases/kernel/fs/fs-notify/fwritefile.c    |  27 ++
>  .../fs/fs-notify/inotify_add_rm_stress.c      |  36 +++
>  .../kernel/fs/fs-notify/inotify_init_stress.c |  22 ++
>  .../fs/fs-notify/inotify_inmodify_stress.c    |  17 ++
>  testcases/kernel/fs/fs-notify/inotify_watch.c |  98 +++++++
>  testcases/kernel/fs/fs-notify/readfile.c      |  28 ++
>  testcases/kernel/fs/fs-notify/rw_files.sh     |  90 +++++++
>  testcases/kernel/fs/fs-notify/writefile.c     |  28 ++
>  15 files changed, 841 insertions(+)
>  create mode 100644 testcases/kernel/fs/fs-notify/Makefile
>  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_init_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_watch.c
>  create mode 100644 testcases/kernel/fs/fs-notify/freadfile.c
>  create mode 100755 testcases/kernel/fs/fs-notify/fsnotify-stress.sh
>  create mode 100644 testcases/kernel/fs/fs-notify/fwritefile.c
>  create mode 100644 testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/inotify_init_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
>  create mode 100644 testcases/kernel/fs/fs-notify/inotify_watch.c
>  create mode 100644 testcases/kernel/fs/fs-notify/readfile.c
>  create mode 100755 testcases/kernel/fs/fs-notify/rw_files.sh
>  create mode 100644 testcases/kernel/fs/fs-notify/writefile.c
>
> diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> new file mode 100644
> index 000000000..328783942
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/Makefile
> @@ -0,0 +1,18 @@
> +#
> +#    kernel/fs/fs-notify testcases Makefile.
> +#
> +
> +top_srcdir                     ?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> +fanotify_mark_stress fanotify_watch inotify_watch \
> +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> +writefile freadfile fwritefile readfile
> +
> +INSTALL_TARGETS                        := fsnotify-stress.sh $(BINARIES) rw_files.sh
> +
> +MAKE_TARGETS                   := $(BINARIES)
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> new file mode 100644
> index 000000000..493acfb56
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> @@ -0,0 +1,52 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       char buf;
> +       int fd;
> +
> +       /* Create the file descriptor for accessing the fanotify API */
> +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +                                          O_RDONLY | O_LARGEFILE);
> +       if (fd == -1) {
> +               perror("fanotify_init");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Loop marking all kinds of events and flush */
> +       while (1) {
> +
> +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> +
> +                       perror("fanotify_mark add");
> +
> +               if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> +                                               0, -1, argv[1]) == -1)
> +                       perror("fanotify_mark flush mount");
> +
> +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> +
> +                       perror("fanotify_mark add");
> +
> +               if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> +                       perror("fanotify_mark flush");
> +       }
> +
> +       close(fd);
> +       exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> new file mode 100644
> index 000000000..d11924b57
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> @@ -0,0 +1,32 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       char buf;
> +       int fd;
> +       while (1) {
> +
> +               /* Create the file descriptor for accessing the fanotify API */
> +               fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> +                               FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> +               if (fd == -1)
> +                       perror("fanotify_init");
> +
> +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +                               FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +                               FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +                               FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> +                               argv[1]) == -1)
> +                       perror("fanotify_mark");
> +
> +               close(fd);
> +       }
> +
> +       exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> new file mode 100644
> index 000000000..7f648e103
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> @@ -0,0 +1,58 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +void add_mark(int fd, uint64_t mask, char *path)
> +{
> +       if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> +               perror("fanotify_mark add");
> +}
> +
> +void remove_mark(int fd, uint64_t mask, char *path)
> +{
> +       if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> +               perror("fanotify_mark remove");
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       char buf;
> +       int fd;
> +       /* Create the file descriptor for accessing the fanotify API */
> +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +                                          O_RDONLY | O_LARGEFILE);
> +       if (fd == -1) {
> +               perror("fanotify_init");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Loop marking all kinds of events */
> +       while (1) {
> +               add_mark(fd, FAN_ACCESS, argv[1]);
> +               remove_mark(fd, FAN_ACCESS, argv[1]);
> +               add_mark(fd, FAN_MODIFY, argv[1]);
> +               remove_mark(fd, FAN_MODIFY, argv[1]);
> +               add_mark(fd, FAN_OPEN_PERM, argv[1]);
> +               remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> +               add_mark(fd, FAN_CLOSE, argv[1]);
> +               remove_mark(fd, FAN_CLOSE, argv[1]);
> +               add_mark(fd, FAN_OPEN, argv[1]);
> +               remove_mark(fd, FAN_OPEN, argv[1]);
> +               add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> +               remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> +               add_mark(fd, FAN_ONDIR, argv[1]);
> +               remove_mark(fd, FAN_ONDIR, argv[1]);
> +               add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> +               remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> +       }
> +
> +       close(fd);
> +       exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> new file mode 100644
> index 000000000..1028e0e30
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> @@ -0,0 +1,244 @@
> +/* Example in man 7 fanotify */
> +
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +/* get comm for pid from /proc/pid/comm */
> +static char * get_comm_pid(unsigned int pid)
> +{
> +       char * proc_name;
> +       char * comm;
> +       int comm_fd, cnt;
> +
> +       proc_name = (char *)malloc(50);
> +       if (proc_name != NULL)
> +               sprintf(proc_name, "/proc/%u/comm\0", pid);
> +       else
> +               return NULL;
> +
> +       comm = (char *)malloc(50);
> +       if (comm != NULL)
> +               memset(comm, 0, 50);
> +       else
> +               return NULL;
> +
> +       comm_fd = open(proc_name, O_RDONLY);
> +       if (comm_fd != -1) {
> +               cnt = read(comm_fd, comm, 50);
> +               if (cnt != -1) {
> +                       comm[cnt] = '\0';
> +                       close(comm_fd);
> +                       return comm;
> +               }
> +               close(comm_fd);
> +       }
> +
> +       return NULL;
> +}
> +
> +/* Read all available fanotify events from the file descriptor 'fd' */
> +
> +static void handle_events(int fd)
> +{
> +       const struct fanotify_event_metadata *metadata;
> +       struct fanotify_event_metadata buf[200];
> +       ssize_t len;
> +       char path[PATH_MAX];
> +       ssize_t path_len;
> +       char procfd_path[PATH_MAX];
> +       struct fanotify_response response;
> +
> +       /* Loop while events can be read from fanotify file descriptor */
> +       for(;;) {
> +
> +               /* Read some events */
> +               len = read(fd, (void *) &buf, sizeof(buf));
> +               if (len == -1 && errno != EAGAIN) {
> +                       perror("read");
> +                       exit(EXIT_FAILURE);
> +               }
> +
> +               /* Check if end of available data reached */
> +               if (len <= 0)
> +                       break;
> +
> +               /* Point to the first event in the buffer */
> +               metadata = buf;
> +
> +               /* Loop over all events in the buffer */
> +               while (FAN_EVENT_OK(metadata, len)) {
> +
> +                       /* Check that run-time and compile-time structures match */
> +
> +                       if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> +                               fprintf(stderr,
> +                                   "Mismatch of fanotify metadata version.\n");
> +                               exit(EXIT_FAILURE);
> +                       }
> +
> +                       /* metadata->fd contains either FAN_NOFD, indicating a
> +                          queue overflow, or a file descriptor (a nonnegative
> +                          integer). Here, we simply ignore queue overflow. */
> +
> +                       if (metadata->fd >= 0) {
> +
> +                               /* Handle open permission event */
> +                               if (metadata->mask & FAN_OPEN_PERM) {
> +                                       printf("FAN_OPEN_PERM: ");
> +
> +                                       /* Allow file to be opened */
> +                                       response.fd = metadata->fd;
> +                                       response.response = FAN_ALLOW;
> +                                       write(fd, &response,
> +                                           sizeof(struct fanotify_response));
> +                               }
> +
> +                               /* Handle access permission event */
> +                               if (metadata->mask & FAN_ACCESS_PERM) {
> +                                       printf("FAN_ACCESS_PERM: ");
> +
> +                                       /* Allow file to be accessed */
> +                                       response.fd = metadata->fd;
> +                                       response.response = FAN_ALLOW;
> +                                       write(fd, &response,
> +                                           sizeof(struct fanotify_response));
> +                               }
> +
> +                               /* Handle closing of writable file event */
> +                               if (metadata->mask & FAN_CLOSE_WRITE)
> +                                       printf("FAN_CLOSE_WRITE: ");
> +
> +                               /* Handle closing of not writable file event */
> +                               if (metadata->mask & FAN_CLOSE_NOWRITE)
> +                                       printf("FAN_CLOSE_NOWRITE: ");
> +
> +                               /* Handle modify file event */
> +                               if (metadata->mask & FAN_MODIFY)
> +                                       printf("FAN_MODIFY: ");
> +
> +                               /* Handle open event */
> +                               if (metadata->mask & FAN_OPEN)
> +                                       printf("FAN_OPEN: ");
> +
> +                               /* Handle access event */
> +                               if (metadata->mask & FAN_ACCESS)
> +                                       printf("FAN_ACCESS: ");
> +
> +                               /* Handle access event */
> +                               if (metadata->mask & FAN_ONDIR)
> +                                       printf("FAN_ONDIR: ");
> +
> +                               /* Handle access event */
> +                               if (metadata->mask & FAN_EVENT_ON_CHILD)
> +                                       printf("FAN_EVENT_ON_CHILD: ");
> +
> +                               /* Retrieve/print the accessed file path*/
> +                               snprintf(procfd_path, sizeof(procfd_path),
> +                                           "/proc/self/fd/%d", metadata->fd);
> +                               path_len = readlink(procfd_path, path,
> +                                           sizeof(path) - 1);
> +                               if (path_len == -1) {
> +                                       perror("readlink");
> +                                       exit(EXIT_FAILURE);
> +                               }
> +
> +                               path[path_len] = '\0';
> +                               printf("File %s.\t\t Comm %s", path,
> +                                       get_comm_pid(metadata->pid));
> +
> +                               /* Close the file descriptor of the event */
> +
> +                               close(metadata->fd);
> +                       }
> +
> +                       /* Advance to next event */
> +                       metadata = FAN_EVENT_NEXT(metadata, len);
> +               }
> +       }
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +       char buf;
> +       int fd, poll_num;
> +       nfds_t nfds;
> +       struct pollfd fds[2];
> +       FILE *f;
> +#if 0
> +       /* Check mount point is supplied */
> +       if (argc != 2) {
> +               fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> +               exit(EXIT_FAILURE);
> +       }
> +#endif
> +       printf("%s on %s\n", argv[0], argv[1]);
> +       fflush(stdout);
> +       f = freopen("fanotify.log", "w+", stdout);
> +       if (f == NULL) {
> +               perror("freopen");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Create the file descriptor for accessing the fanotify API */
> +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +                                          O_RDONLY | O_LARGEFILE);
> +       if (fd == -1) {
> +               perror("fanotify_init");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Mark the mount for:
> +          - permission events before opening files
> +          - notification events after closing a write-enabled
> +                file descriptor */
> +       if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +                       FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +                       FAN_ONDIR | FAN_EVENT_ON_CHILD,
> +                       -1, argv[1]) == -1) {
> +
> +               perror("fanotify_mark");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       /* Prepare for polling */
> +       nfds = 1;
> +
> +       /* Fanotify input */
> +       fds[0].fd = fd;
> +       fds[0].events = POLLIN;
> +
> +       /* This is the loop to wait for incoming events */
> +       printf("Listening for events.\n");
> +       while (1) {
> +               poll_num = poll(fds, nfds, -1);
> +               if (poll_num == -1) {
> +                       if (errno == EINTR)     /* Interrupted by a signal */
> +                               continue;           /* Restart poll() */
> +
> +                       perror("poll");         /* Unexpected error */
> +                       exit(EXIT_FAILURE);
> +               }
> +
> +               if (poll_num > 0) {
> +
> +                       if (fds[0].revents & POLLIN) {
> +
> +                               /* Fanotify events are available */
> +                               handle_events(fd);
> +                       }
> +               }
> +       }
> +
> +       fclose(f);
> +       printf("Listening for events stopped.\n");
> +       exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> new file mode 100644
> index 000000000..24bf76bcd
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> @@ -0,0 +1,27 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +int main(int argc, char **argv)
> +{
> +       int fd;
> +       char buf[BUFSIZ];
> +       FILE *f;
> +
> +       memset(buf, 1, BUFSIZ);
> +       while(1) {
> +               f = fopen(argv[1], "r+");
> +               if (f == NULL) {
> +                       perror("freadfile fopen");
> +                       exit(EXIT_FAILURE);
> +               }
> +               fread(buf, sizeof(char), BUFSIZ, f);
> +               usleep(1);
> +       }
> +       fclose(f);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> new file mode 100755
> index 000000000..d6fd5482b
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> @@ -0,0 +1,64 @@
> +#!/bin/bash
> +
> +export TIMEOUT=10
> +
> +STRESSES="fanotify_flush_stress fanotify_init_stress \
> +fanotify_mark_stress fanotify_watch inotify_watch \
> +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> +IO_OPES="writefile freadfile fwritefile readfile"
> +
> +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> +
> +function cleanup_processes()
> +{
> +       while ps -eo pid,comm | \
> +               grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> +               killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> +               sleep 1
> +       done
> +}
> +
> +function cleanup()
> +{
> +       sleep 3 # waiting possible unfinished processes
> +       cleanup_processes
> +       rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> +       cd /
> +       sync
> +}
> +
> +function run_stress()
> +{
> +       local i j rcnt=0
> +       echo -e "* CPU count: $NR_CPUS"
> +       echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> +
> +       echo -e "* Starting fsnotify stress on directory and regular file"
> +       touch $TMPDIR/testfile
> +       >tmp
> +       for i in $STRESSES $IO_OPES rw_files.sh; do
> +               for j in $STRESSES ; do
> +                       [ "$i" == "$j" ] && continue
> +                       # skip duplicate combinations
> +                       grep -w $j tmp | grep -qw $i && continue
> +                       echo -e "* Starting $i and $j, rcnt $rcnt"
> +                       ./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> +                       ./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> +                       ./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> +                       ./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> +                       sleep $TIMEOUT
> +                       cleanup_processes
> +                       echo -e "$i $j" >> tmp
> +                       ((rcnt++))
> +               done
> +       done
> +}
> +
> +trap "cleanup; exit;" 2
> +
> +echo "***** Starting tests *****"
> +run_stress
> +
> +echo -e "\n***** Cleanup fanotify inotify device *****"
> +cleanup
> diff --git a/testcases/kernel/fs/fs-notify/fwritefile.c b/testcases/kernel/fs/fs-notify/fwritefile.c
> new file mode 100644
> index 000000000..a2956d60b
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fwritefile.c
> @@ -0,0 +1,27 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +int main(int argc, char **argv)
> +{
> +       int fd;
> +       char buf[BUFSIZ];
> +       FILE *f;
> +
> +       memset(buf, 1, BUFSIZ);
> +       while(1) {
> +               f = fopen(argv[1], "w+");
> +               if (f == NULL) {
> +                       perror("fwritefile fopen");
> +                       exit(EXIT_FAILURE);
> +               }
> +               fwrite(buf, sizeof(char), BUFSIZ, f);
> +               usleep(1);
> +       }
> +       fclose(f);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> new file mode 100644
> index 000000000..e88fc088e
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> @@ -0,0 +1,36 @@
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <stdio.h>
> +#include <sys/inotify.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       int notify_fd;
> +       int wd, ret;
> +
> +       notify_fd = inotify_init1(IN_CLOEXEC);
> +       if (notify_fd == -1) {
> +               perror("inotify_init1");
> +               return -1;
> +       }
> +
> +       while (1) {
> +               wd = inotify_add_watch(notify_fd, argv[1],
> +                       IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
> +                       IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
> +                       IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
> +                       IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> +               if (wd < 0)
> +                       perror("inotify_add_watch");
> +
> +               ret = inotify_rm_watch(notify_fd, wd);
> +               if (ret < 0)
> +                       perror("inotify_rm_watch");
> +       }
> +       close(notify_fd);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/inotify_init_stress.c b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> new file mode 100644
> index 000000000..62cb7c2e6
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> @@ -0,0 +1,22 @@
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <stdio.h>
> +#include <sys/inotify.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       int notify_fd;
> +       int wd;
> +
> +       while (1) {
> +               notify_fd = inotify_init1(IN_CLOEXEC);
> +               if (notify_fd == -1)
> +                       perror("inotify_init1");
> +               close(notify_fd);
> +       }
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> new file mode 100644
> index 000000000..546ccb76f
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> @@ -0,0 +1,17 @@
> +#include <sys/inotify.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +
> +/*
> + * Calls inotify_rm_watch in a loop.
> + */
> +int main(int argc, char **argv)
> +{
> +       int fd = inotify_init();
> +       while (1) {
> +               int wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
> +               inotify_rm_watch(fd, wd);
> +       }
> +       close(fd);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/inotify_watch.c b/testcases/kernel/fs/fs-notify/inotify_watch.c
> new file mode 100644
> index 000000000..b4b908b6b
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/inotify_watch.c
> @@ -0,0 +1,98 @@
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <stdio.h>
> +#include <sys/inotify.h>
> +
> +int main(int argc, char *argv[])
> +{
> +       int notify_fd;
> +       int wd, ret;
> +       char *buf;
> +       FILE *f;
> +
> +       f = freopen("inotify.log", "w+", stdout);
> +       if (f == NULL) {
> +               perror("freopen");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +       buf = malloc(sizeof(struct inotify_event) + NAME_MAX + 1);
> +       if (buf == NULL) {
> +               perror("malloc");
> +               return -1;
> +       }
> +
> +       notify_fd = inotify_init1(IN_CLOEXEC);
> +       if (notify_fd == -1) {
> +               perror("inotify_init1");
> +               return -1;
> +       }
> +
> +       wd = inotify_add_watch(notify_fd, argv[1],
> +               IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE |
> +               IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
> +               IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> +
> +       if (wd < 0) {
> +               perror("inotify_add_watch");
> +               return -1;
> +       }
> +
> +       while (ret = read(notify_fd, buf, NAME_MAX) != -1) {
> +               struct inotify_event *ip = (struct inotify_event *)buf;
> +               printf("name %s event ", ip->name);
> +               switch (ip->mask) {
> +               case IN_ACCESS:
> +                       printf("access\n");
> +                       break;
> +               case IN_ATTRIB:
> +                       printf("attrib\n");
> +                       break;
> +               case IN_CLOSE_WRITE:
> +                       printf("close write\n");
> +                       break;
> +               case IN_CLOSE_NOWRITE:
> +                       printf("close nowrite\n");
> +                       break;
> +               case IN_CREATE:
> +                       printf("create\n");
> +                       break;
> +               case IN_DELETE:
> +                       printf("delete\n");
> +                       break;
> +               case IN_DELETE_SELF:
> +                       printf("deleteself\n");
> +                       break;
> +               case IN_MODIFY:
> +                       printf("modify\n");
> +                       break;
> +               case IN_MOVE_SELF:
> +                       printf("move self\n");
> +                       break;
> +               case IN_MOVED_FROM:
> +                       printf("move from\n");
> +                       break;
> +               case IN_MOVED_TO:
> +                       printf("move to\n");
> +                       break;
> +               case IN_OPEN:
> +                       printf("open\n");
> +                       break;
> +               default:
> +                       printf("\n");
> +                       break;
> +               };
> +       }
> +
> +       ret = inotify_rm_watch(notify_fd, wd);
> +       if (ret < 0)
> +               perror("inotify_rm_watch");
> +
> +       fclose(f);
> +       close(notify_fd);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/readfile.c b/testcases/kernel/fs/fs-notify/readfile.c
> new file mode 100644
> index 000000000..2ab1a3694
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/readfile.c
> @@ -0,0 +1,28 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +int main(int argc, char **argv)
> +{
> +       int fd, ret;
> +       char buf[BUFSIZ];
> +
> +       memset(buf, 1, BUFSIZ);
> +       while (1) {
> +               fd = open(argv[1], O_RDWR);
> +               if (fd == -1) {
> +                       perror("readfile open");
> +                       exit(EXIT_FAILURE);
> +               }
> +               ret = read(fd, buf, BUFSIZ);
> +               if (ret == -1)
> +                       perror("readfile read");
> +               usleep(1);
> +       }
> +       close(fd);
> +       return 0;
> +}
> diff --git a/testcases/kernel/fs/fs-notify/rw_files.sh b/testcases/kernel/fs/fs-notify/rw_files.sh
> new file mode 100755
> index 000000000..bb3387160
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/rw_files.sh
> @@ -0,0 +1,90 @@
> +#!/bin/bash
> +
> +TDIR=$1
> +TIMEO=$2
> +
> +[ -z "$TDIR" ] && TDIR=/tmp/
> +[ -d $TDIR ] || TDIR=/tmp/
> +[ -z "$TIMEO" ] && TIMEO=1m
> +
> +[ ! -d $TDIR ] && exit
> +
> +function add_files()
> +{
> +       local i=0
> +
> +       while true ; do
> +
> +               touch f-$i
> +               ln -s f-$i f-$i-sym
> +               ln f-$i f-$i-hdl
> +
> +               mkdir d-$i
> +
> +               mknod c-$i c 1 2
> +               mknod b-$i b 1 2
> +               mknod p-$i p
> +
> +               ((i++))
> +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +       done
> +}
> +
> +function mv_files()
> +{
> +       local i=0
> +
> +       while true ; do
> +
> +               mv -f f-$i f-$i-rename
> +               mv -f f-$i-rename f-$i
> +               ((i++))
> +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +       done
> +}
> +
> +function read_files()
> +{
> +       while true ; do
> +
> +               find .
> +               cat f-*
> +               ls d-*
> +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +       done
> +}
> +
> +function write_files()
> +{
> +       while true ; do
> +
> +               for j in f-* d-* c-* b-* p-* ; do
> +                       echo 1 > $j
> +                       echo 2 >> $j
> +                       [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +               done
> +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +       done
> +}
> +
> +function rm_files()
> +{
> +       while true ; do
> +
> +               rm -rf d-* f-* c-* b-* p-*
> +               sleep 3
> +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> +       done
> +}
> +
> +pushd $TDIR > /dev/null 2>&1
> +rm -f stoptest
> +add_files > /dev/null 2>&1 &
> +mv_files > /dev/null 2>&1 &
> +read_files > /dev/null 2>&1 &
> +write_files > /dev/null 2>&1 &
> +rm_files > /dev/null 2>&1 &
> +popd > /dev/null 2>&1
> +
> +sleep $TIMEO
> +touch $TDIR/stoptest
> diff --git a/testcases/kernel/fs/fs-notify/writefile.c b/testcases/kernel/fs/fs-notify/writefile.c
> new file mode 100644
> index 000000000..9025b5d95
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/writefile.c
> @@ -0,0 +1,28 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +int main(int argc, char **argv)
> +{
> +       int fd, ret;
> +       char buf[BUFSIZ];
> +
> +       memset(buf, 1, BUFSIZ);
> +       while (1) {
> +               fd = open(argv[1], O_RDWR);
> +               if (fd == -1) {
> +                       perror("writefile open");
> +                       exit(EXIT_FAILURE);
> +               }
> +               ret = write(fd, buf, BUFSIZ);
> +               if (ret == -1)
> +                       perror("writefile write");
> +               usleep(1);
> +       }
> +       close(fd);
> +       return 0;
> +}
> --
> 2.31.1
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25  7:29 ` Amir Goldstein
@ 2022-01-25  8:30   ` Murphy Zhou
  2022-01-25 10:28     ` Petr Vorel
  2022-01-25 10:56     ` Cyril Hrubis
  0 siblings, 2 replies; 18+ messages in thread
From: Murphy Zhou @ 2022-01-25  8:30 UTC (permalink / raw)
  To: Amir Goldstein; +Cc: Jan Kara, LTP List

Hi Amir,

On Tue, Jan 25, 2022 at 09:29:47AM +0200, Amir Goldstein wrote:
> On Tue, Jan 25, 2022 at 4:47 AM Murphy Zhou <jencce.kernel@gmail.com> wrote:
> >
> > Exercise fsnotify and inotify interfaces while IO going on.
> 
> Hi Murphy,
> 
> It's worth documenting in main test script that this test
> was created as a regression test for commit
> 4396a73115fc fsnotify: fix sb_connectors leak

Ah, the tests were created for the goal of stress test. This leak
was one of the bugs that this test found.

> 
> Can you elaborate a bit more about the expectation from this test.
> I do not see any error checking in the main script.

Sure. Will explain more.

> Is any perror supposed to fail the test?

No.

> Is its purpose to catch kernel dmesg splats of all sorts?

Not needed.

My intend is to use these interfaces insanely a while, if kernel 
does not panic or hang, pass. This test is not checking *notify
function which has already been covered.

> 
> In your original post you wrote that bug makes the test hang:
> https://lore.kernel.org/linux-fsdevel/20210907063338.ycaw6wvhzrfsfdlp@xzhoux.usersys.redhat.com/
> 
> If that is the case, better mention this to the LTP maintainers.

Sure. Thanks for reviewing!

Murphy
> 
> Thanks,
> Amir.
> 
> 
> 
> 
> >
> > Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> > ---
> >  testcases/kernel/fs/fs-notify/Makefile        |  18 ++
> >  .../fs/fs-notify/fanotify_flush_stress.c      |  52 ++++
> >  .../fs/fs-notify/fanotify_init_stress.c       |  32 +++
> >  .../fs/fs-notify/fanotify_mark_stress.c       |  58 +++++
> >  .../kernel/fs/fs-notify/fanotify_watch.c      | 244 ++++++++++++++++++
> >  testcases/kernel/fs/fs-notify/freadfile.c     |  27 ++
> >  .../kernel/fs/fs-notify/fsnotify-stress.sh    |  64 +++++
> >  testcases/kernel/fs/fs-notify/fwritefile.c    |  27 ++
> >  .../fs/fs-notify/inotify_add_rm_stress.c      |  36 +++
> >  .../kernel/fs/fs-notify/inotify_init_stress.c |  22 ++
> >  .../fs/fs-notify/inotify_inmodify_stress.c    |  17 ++
> >  testcases/kernel/fs/fs-notify/inotify_watch.c |  98 +++++++
> >  testcases/kernel/fs/fs-notify/readfile.c      |  28 ++
> >  testcases/kernel/fs/fs-notify/rw_files.sh     |  90 +++++++
> >  testcases/kernel/fs/fs-notify/writefile.c     |  28 ++
> >  15 files changed, 841 insertions(+)
> >  create mode 100644 testcases/kernel/fs/fs-notify/Makefile
> >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_watch.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/freadfile.c
> >  create mode 100755 testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> >  create mode 100644 testcases/kernel/fs/fs-notify/fwritefile.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_init_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_watch.c
> >  create mode 100644 testcases/kernel/fs/fs-notify/readfile.c
> >  create mode 100755 testcases/kernel/fs/fs-notify/rw_files.sh
> >  create mode 100644 testcases/kernel/fs/fs-notify/writefile.c
> >
> > diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> > new file mode 100644
> > index 000000000..328783942
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/Makefile
> > @@ -0,0 +1,18 @@
> > +#
> > +#    kernel/fs/fs-notify testcases Makefile.
> > +#
> > +
> > +top_srcdir                     ?= ../../../..
> > +
> > +include $(top_srcdir)/include/mk/testcases.mk
> > +
> > +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> > +writefile freadfile fwritefile readfile
> > +
> > +INSTALL_TARGETS                        := fsnotify-stress.sh $(BINARIES) rw_files.sh
> > +
> > +MAKE_TARGETS                   := $(BINARIES)
> > +
> > +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > new file mode 100644
> > index 000000000..493acfb56
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > @@ -0,0 +1,52 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       char buf;
> > +       int fd;
> > +
> > +       /* Create the file descriptor for accessing the fanotify API */
> > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                          O_RDONLY | O_LARGEFILE);
> > +       if (fd == -1) {
> > +               perror("fanotify_init");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       /* Loop marking all kinds of events and flush */
> > +       while (1) {
> > +
> > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                       perror("fanotify_mark add");
> > +
> > +               if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> > +                                               0, -1, argv[1]) == -1)
> > +                       perror("fanotify_mark flush mount");
> > +
> > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                       perror("fanotify_mark add");
> > +
> > +               if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> > +                       perror("fanotify_mark flush");
> > +       }
> > +
> > +       close(fd);
> > +       exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > new file mode 100644
> > index 000000000..d11924b57
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > @@ -0,0 +1,32 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       char buf;
> > +       int fd;
> > +       while (1) {
> > +
> > +               /* Create the file descriptor for accessing the fanotify API */
> > +               fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> > +                               FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> > +               if (fd == -1)
> > +                       perror("fanotify_init");
> > +
> > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                               FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                               FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                               FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> > +                               argv[1]) == -1)
> > +                       perror("fanotify_mark");
> > +
> > +               close(fd);
> > +       }
> > +
> > +       exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > new file mode 100644
> > index 000000000..7f648e103
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > @@ -0,0 +1,58 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +void add_mark(int fd, uint64_t mask, char *path)
> > +{
> > +       if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> > +               perror("fanotify_mark add");
> > +}
> > +
> > +void remove_mark(int fd, uint64_t mask, char *path)
> > +{
> > +       if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> > +               perror("fanotify_mark remove");
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       char buf;
> > +       int fd;
> > +       /* Create the file descriptor for accessing the fanotify API */
> > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                          O_RDONLY | O_LARGEFILE);
> > +       if (fd == -1) {
> > +               perror("fanotify_init");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       /* Loop marking all kinds of events */
> > +       while (1) {
> > +               add_mark(fd, FAN_ACCESS, argv[1]);
> > +               remove_mark(fd, FAN_ACCESS, argv[1]);
> > +               add_mark(fd, FAN_MODIFY, argv[1]);
> > +               remove_mark(fd, FAN_MODIFY, argv[1]);
> > +               add_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +               remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +               add_mark(fd, FAN_CLOSE, argv[1]);
> > +               remove_mark(fd, FAN_CLOSE, argv[1]);
> > +               add_mark(fd, FAN_OPEN, argv[1]);
> > +               remove_mark(fd, FAN_OPEN, argv[1]);
> > +               add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +               remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +               add_mark(fd, FAN_ONDIR, argv[1]);
> > +               remove_mark(fd, FAN_ONDIR, argv[1]);
> > +               add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +               remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +       }
> > +
> > +       close(fd);
> > +       exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > new file mode 100644
> > index 000000000..1028e0e30
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > @@ -0,0 +1,244 @@
> > +/* Example in man 7 fanotify */
> > +
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +/* get comm for pid from /proc/pid/comm */
> > +static char * get_comm_pid(unsigned int pid)
> > +{
> > +       char * proc_name;
> > +       char * comm;
> > +       int comm_fd, cnt;
> > +
> > +       proc_name = (char *)malloc(50);
> > +       if (proc_name != NULL)
> > +               sprintf(proc_name, "/proc/%u/comm\0", pid);
> > +       else
> > +               return NULL;
> > +
> > +       comm = (char *)malloc(50);
> > +       if (comm != NULL)
> > +               memset(comm, 0, 50);
> > +       else
> > +               return NULL;
> > +
> > +       comm_fd = open(proc_name, O_RDONLY);
> > +       if (comm_fd != -1) {
> > +               cnt = read(comm_fd, comm, 50);
> > +               if (cnt != -1) {
> > +                       comm[cnt] = '\0';
> > +                       close(comm_fd);
> > +                       return comm;
> > +               }
> > +               close(comm_fd);
> > +       }
> > +
> > +       return NULL;
> > +}
> > +
> > +/* Read all available fanotify events from the file descriptor 'fd' */
> > +
> > +static void handle_events(int fd)
> > +{
> > +       const struct fanotify_event_metadata *metadata;
> > +       struct fanotify_event_metadata buf[200];
> > +       ssize_t len;
> > +       char path[PATH_MAX];
> > +       ssize_t path_len;
> > +       char procfd_path[PATH_MAX];
> > +       struct fanotify_response response;
> > +
> > +       /* Loop while events can be read from fanotify file descriptor */
> > +       for(;;) {
> > +
> > +               /* Read some events */
> > +               len = read(fd, (void *) &buf, sizeof(buf));
> > +               if (len == -1 && errno != EAGAIN) {
> > +                       perror("read");
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +
> > +               /* Check if end of available data reached */
> > +               if (len <= 0)
> > +                       break;
> > +
> > +               /* Point to the first event in the buffer */
> > +               metadata = buf;
> > +
> > +               /* Loop over all events in the buffer */
> > +               while (FAN_EVENT_OK(metadata, len)) {
> > +
> > +                       /* Check that run-time and compile-time structures match */
> > +
> > +                       if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> > +                               fprintf(stderr,
> > +                                   "Mismatch of fanotify metadata version.\n");
> > +                               exit(EXIT_FAILURE);
> > +                       }
> > +
> > +                       /* metadata->fd contains either FAN_NOFD, indicating a
> > +                          queue overflow, or a file descriptor (a nonnegative
> > +                          integer). Here, we simply ignore queue overflow. */
> > +
> > +                       if (metadata->fd >= 0) {
> > +
> > +                               /* Handle open permission event */
> > +                               if (metadata->mask & FAN_OPEN_PERM) {
> > +                                       printf("FAN_OPEN_PERM: ");
> > +
> > +                                       /* Allow file to be opened */
> > +                                       response.fd = metadata->fd;
> > +                                       response.response = FAN_ALLOW;
> > +                                       write(fd, &response,
> > +                                           sizeof(struct fanotify_response));
> > +                               }
> > +
> > +                               /* Handle access permission event */
> > +                               if (metadata->mask & FAN_ACCESS_PERM) {
> > +                                       printf("FAN_ACCESS_PERM: ");
> > +
> > +                                       /* Allow file to be accessed */
> > +                                       response.fd = metadata->fd;
> > +                                       response.response = FAN_ALLOW;
> > +                                       write(fd, &response,
> > +                                           sizeof(struct fanotify_response));
> > +                               }
> > +
> > +                               /* Handle closing of writable file event */
> > +                               if (metadata->mask & FAN_CLOSE_WRITE)
> > +                                       printf("FAN_CLOSE_WRITE: ");
> > +
> > +                               /* Handle closing of not writable file event */
> > +                               if (metadata->mask & FAN_CLOSE_NOWRITE)
> > +                                       printf("FAN_CLOSE_NOWRITE: ");
> > +
> > +                               /* Handle modify file event */
> > +                               if (metadata->mask & FAN_MODIFY)
> > +                                       printf("FAN_MODIFY: ");
> > +
> > +                               /* Handle open event */
> > +                               if (metadata->mask & FAN_OPEN)
> > +                                       printf("FAN_OPEN: ");
> > +
> > +                               /* Handle access event */
> > +                               if (metadata->mask & FAN_ACCESS)
> > +                                       printf("FAN_ACCESS: ");
> > +
> > +                               /* Handle access event */
> > +                               if (metadata->mask & FAN_ONDIR)
> > +                                       printf("FAN_ONDIR: ");
> > +
> > +                               /* Handle access event */
> > +                               if (metadata->mask & FAN_EVENT_ON_CHILD)
> > +                                       printf("FAN_EVENT_ON_CHILD: ");
> > +
> > +                               /* Retrieve/print the accessed file path*/
> > +                               snprintf(procfd_path, sizeof(procfd_path),
> > +                                           "/proc/self/fd/%d", metadata->fd);
> > +                               path_len = readlink(procfd_path, path,
> > +                                           sizeof(path) - 1);
> > +                               if (path_len == -1) {
> > +                                       perror("readlink");
> > +                                       exit(EXIT_FAILURE);
> > +                               }
> > +
> > +                               path[path_len] = '\0';
> > +                               printf("File %s.\t\t Comm %s", path,
> > +                                       get_comm_pid(metadata->pid));
> > +
> > +                               /* Close the file descriptor of the event */
> > +
> > +                               close(metadata->fd);
> > +                       }
> > +
> > +                       /* Advance to next event */
> > +                       metadata = FAN_EVENT_NEXT(metadata, len);
> > +               }
> > +       }
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       char buf;
> > +       int fd, poll_num;
> > +       nfds_t nfds;
> > +       struct pollfd fds[2];
> > +       FILE *f;
> > +#if 0
> > +       /* Check mount point is supplied */
> > +       if (argc != 2) {
> > +               fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> > +               exit(EXIT_FAILURE);
> > +       }
> > +#endif
> > +       printf("%s on %s\n", argv[0], argv[1]);
> > +       fflush(stdout);
> > +       f = freopen("fanotify.log", "w+", stdout);
> > +       if (f == NULL) {
> > +               perror("freopen");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       /* Create the file descriptor for accessing the fanotify API */
> > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                          O_RDONLY | O_LARGEFILE);
> > +       if (fd == -1) {
> > +               perror("fanotify_init");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       /* Mark the mount for:
> > +          - permission events before opening files
> > +          - notification events after closing a write-enabled
> > +                file descriptor */
> > +       if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                       FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                       FAN_ONDIR | FAN_EVENT_ON_CHILD,
> > +                       -1, argv[1]) == -1) {
> > +
> > +               perror("fanotify_mark");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       /* Prepare for polling */
> > +       nfds = 1;
> > +
> > +       /* Fanotify input */
> > +       fds[0].fd = fd;
> > +       fds[0].events = POLLIN;
> > +
> > +       /* This is the loop to wait for incoming events */
> > +       printf("Listening for events.\n");
> > +       while (1) {
> > +               poll_num = poll(fds, nfds, -1);
> > +               if (poll_num == -1) {
> > +                       if (errno == EINTR)     /* Interrupted by a signal */
> > +                               continue;           /* Restart poll() */
> > +
> > +                       perror("poll");         /* Unexpected error */
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +
> > +               if (poll_num > 0) {
> > +
> > +                       if (fds[0].revents & POLLIN) {
> > +
> > +                               /* Fanotify events are available */
> > +                               handle_events(fd);
> > +                       }
> > +               }
> > +       }
> > +
> > +       fclose(f);
> > +       printf("Listening for events stopped.\n");
> > +       exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> > new file mode 100644
> > index 000000000..24bf76bcd
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> > @@ -0,0 +1,27 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       int fd;
> > +       char buf[BUFSIZ];
> > +       FILE *f;
> > +
> > +       memset(buf, 1, BUFSIZ);
> > +       while(1) {
> > +               f = fopen(argv[1], "r+");
> > +               if (f == NULL) {
> > +                       perror("freadfile fopen");
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +               fread(buf, sizeof(char), BUFSIZ, f);
> > +               usleep(1);
> > +       }
> > +       fclose(f);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > new file mode 100755
> > index 000000000..d6fd5482b
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > @@ -0,0 +1,64 @@
> > +#!/bin/bash
> > +
> > +export TIMEOUT=10
> > +
> > +STRESSES="fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> > +IO_OPES="writefile freadfile fwritefile readfile"
> > +
> > +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> > +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> > +
> > +function cleanup_processes()
> > +{
> > +       while ps -eo pid,comm | \
> > +               grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> > +               killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> > +               sleep 1
> > +       done
> > +}
> > +
> > +function cleanup()
> > +{
> > +       sleep 3 # waiting possible unfinished processes
> > +       cleanup_processes
> > +       rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> > +       cd /
> > +       sync
> > +}
> > +
> > +function run_stress()
> > +{
> > +       local i j rcnt=0
> > +       echo -e "* CPU count: $NR_CPUS"
> > +       echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> > +
> > +       echo -e "* Starting fsnotify stress on directory and regular file"
> > +       touch $TMPDIR/testfile
> > +       >tmp
> > +       for i in $STRESSES $IO_OPES rw_files.sh; do
> > +               for j in $STRESSES ; do
> > +                       [ "$i" == "$j" ] && continue
> > +                       # skip duplicate combinations
> > +                       grep -w $j tmp | grep -qw $i && continue
> > +                       echo -e "* Starting $i and $j, rcnt $rcnt"
> > +                       ./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                       ./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                       ./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                       ./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                       sleep $TIMEOUT
> > +                       cleanup_processes
> > +                       echo -e "$i $j" >> tmp
> > +                       ((rcnt++))
> > +               done
> > +       done
> > +}
> > +
> > +trap "cleanup; exit;" 2
> > +
> > +echo "***** Starting tests *****"
> > +run_stress
> > +
> > +echo -e "\n***** Cleanup fanotify inotify device *****"
> > +cleanup
> > diff --git a/testcases/kernel/fs/fs-notify/fwritefile.c b/testcases/kernel/fs/fs-notify/fwritefile.c
> > new file mode 100644
> > index 000000000..a2956d60b
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fwritefile.c
> > @@ -0,0 +1,27 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       int fd;
> > +       char buf[BUFSIZ];
> > +       FILE *f;
> > +
> > +       memset(buf, 1, BUFSIZ);
> > +       while(1) {
> > +               f = fopen(argv[1], "w+");
> > +               if (f == NULL) {
> > +                       perror("fwritefile fopen");
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +               fwrite(buf, sizeof(char), BUFSIZ, f);
> > +               usleep(1);
> > +       }
> > +       fclose(f);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> > new file mode 100644
> > index 000000000..e88fc088e
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> > @@ -0,0 +1,36 @@
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <stdlib.h>
> > +#include <dirent.h>
> > +#include <stdio.h>
> > +#include <sys/inotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       int notify_fd;
> > +       int wd, ret;
> > +
> > +       notify_fd = inotify_init1(IN_CLOEXEC);
> > +       if (notify_fd == -1) {
> > +               perror("inotify_init1");
> > +               return -1;
> > +       }
> > +
> > +       while (1) {
> > +               wd = inotify_add_watch(notify_fd, argv[1],
> > +                       IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
> > +                       IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
> > +                       IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
> > +                       IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> > +               if (wd < 0)
> > +                       perror("inotify_add_watch");
> > +
> > +               ret = inotify_rm_watch(notify_fd, wd);
> > +               if (ret < 0)
> > +                       perror("inotify_rm_watch");
> > +       }
> > +       close(notify_fd);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/inotify_init_stress.c b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> > new file mode 100644
> > index 000000000..62cb7c2e6
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> > @@ -0,0 +1,22 @@
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <stdlib.h>
> > +#include <dirent.h>
> > +#include <stdio.h>
> > +#include <sys/inotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       int notify_fd;
> > +       int wd;
> > +
> > +       while (1) {
> > +               notify_fd = inotify_init1(IN_CLOEXEC);
> > +               if (notify_fd == -1)
> > +                       perror("inotify_init1");
> > +               close(notify_fd);
> > +       }
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> > new file mode 100644
> > index 000000000..546ccb76f
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> > @@ -0,0 +1,17 @@
> > +#include <sys/inotify.h>
> > +#include <stdio.h>
> > +#include <unistd.h>
> > +
> > +/*
> > + * Calls inotify_rm_watch in a loop.
> > + */
> > +int main(int argc, char **argv)
> > +{
> > +       int fd = inotify_init();
> > +       while (1) {
> > +               int wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
> > +               inotify_rm_watch(fd, wd);
> > +       }
> > +       close(fd);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/inotify_watch.c b/testcases/kernel/fs/fs-notify/inotify_watch.c
> > new file mode 100644
> > index 000000000..b4b908b6b
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/inotify_watch.c
> > @@ -0,0 +1,98 @@
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <stdlib.h>
> > +#include <dirent.h>
> > +#include <stdio.h>
> > +#include <sys/inotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +       int notify_fd;
> > +       int wd, ret;
> > +       char *buf;
> > +       FILE *f;
> > +
> > +       f = freopen("inotify.log", "w+", stdout);
> > +       if (f == NULL) {
> > +               perror("freopen");
> > +               exit(EXIT_FAILURE);
> > +       }
> > +
> > +       buf = malloc(sizeof(struct inotify_event) + NAME_MAX + 1);
> > +       if (buf == NULL) {
> > +               perror("malloc");
> > +               return -1;
> > +       }
> > +
> > +       notify_fd = inotify_init1(IN_CLOEXEC);
> > +       if (notify_fd == -1) {
> > +               perror("inotify_init1");
> > +               return -1;
> > +       }
> > +
> > +       wd = inotify_add_watch(notify_fd, argv[1],
> > +               IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE |
> > +               IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
> > +               IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> > +
> > +       if (wd < 0) {
> > +               perror("inotify_add_watch");
> > +               return -1;
> > +       }
> > +
> > +       while (ret = read(notify_fd, buf, NAME_MAX) != -1) {
> > +               struct inotify_event *ip = (struct inotify_event *)buf;
> > +               printf("name %s event ", ip->name);
> > +               switch (ip->mask) {
> > +               case IN_ACCESS:
> > +                       printf("access\n");
> > +                       break;
> > +               case IN_ATTRIB:
> > +                       printf("attrib\n");
> > +                       break;
> > +               case IN_CLOSE_WRITE:
> > +                       printf("close write\n");
> > +                       break;
> > +               case IN_CLOSE_NOWRITE:
> > +                       printf("close nowrite\n");
> > +                       break;
> > +               case IN_CREATE:
> > +                       printf("create\n");
> > +                       break;
> > +               case IN_DELETE:
> > +                       printf("delete\n");
> > +                       break;
> > +               case IN_DELETE_SELF:
> > +                       printf("deleteself\n");
> > +                       break;
> > +               case IN_MODIFY:
> > +                       printf("modify\n");
> > +                       break;
> > +               case IN_MOVE_SELF:
> > +                       printf("move self\n");
> > +                       break;
> > +               case IN_MOVED_FROM:
> > +                       printf("move from\n");
> > +                       break;
> > +               case IN_MOVED_TO:
> > +                       printf("move to\n");
> > +                       break;
> > +               case IN_OPEN:
> > +                       printf("open\n");
> > +                       break;
> > +               default:
> > +                       printf("\n");
> > +                       break;
> > +               };
> > +       }
> > +
> > +       ret = inotify_rm_watch(notify_fd, wd);
> > +       if (ret < 0)
> > +               perror("inotify_rm_watch");
> > +
> > +       fclose(f);
> > +       close(notify_fd);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/readfile.c b/testcases/kernel/fs/fs-notify/readfile.c
> > new file mode 100644
> > index 000000000..2ab1a3694
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/readfile.c
> > @@ -0,0 +1,28 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       int fd, ret;
> > +       char buf[BUFSIZ];
> > +
> > +       memset(buf, 1, BUFSIZ);
> > +       while (1) {
> > +               fd = open(argv[1], O_RDWR);
> > +               if (fd == -1) {
> > +                       perror("readfile open");
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +               ret = read(fd, buf, BUFSIZ);
> > +               if (ret == -1)
> > +                       perror("readfile read");
> > +               usleep(1);
> > +       }
> > +       close(fd);
> > +       return 0;
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/rw_files.sh b/testcases/kernel/fs/fs-notify/rw_files.sh
> > new file mode 100755
> > index 000000000..bb3387160
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/rw_files.sh
> > @@ -0,0 +1,90 @@
> > +#!/bin/bash
> > +
> > +TDIR=$1
> > +TIMEO=$2
> > +
> > +[ -z "$TDIR" ] && TDIR=/tmp/
> > +[ -d $TDIR ] || TDIR=/tmp/
> > +[ -z "$TIMEO" ] && TIMEO=1m
> > +
> > +[ ! -d $TDIR ] && exit
> > +
> > +function add_files()
> > +{
> > +       local i=0
> > +
> > +       while true ; do
> > +
> > +               touch f-$i
> > +               ln -s f-$i f-$i-sym
> > +               ln f-$i f-$i-hdl
> > +
> > +               mkdir d-$i
> > +
> > +               mknod c-$i c 1 2
> > +               mknod b-$i b 1 2
> > +               mknod p-$i p
> > +
> > +               ((i++))
> > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +       done
> > +}
> > +
> > +function mv_files()
> > +{
> > +       local i=0
> > +
> > +       while true ; do
> > +
> > +               mv -f f-$i f-$i-rename
> > +               mv -f f-$i-rename f-$i
> > +               ((i++))
> > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +       done
> > +}
> > +
> > +function read_files()
> > +{
> > +       while true ; do
> > +
> > +               find .
> > +               cat f-*
> > +               ls d-*
> > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +       done
> > +}
> > +
> > +function write_files()
> > +{
> > +       while true ; do
> > +
> > +               for j in f-* d-* c-* b-* p-* ; do
> > +                       echo 1 > $j
> > +                       echo 2 >> $j
> > +                       [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +               done
> > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +       done
> > +}
> > +
> > +function rm_files()
> > +{
> > +       while true ; do
> > +
> > +               rm -rf d-* f-* c-* b-* p-*
> > +               sleep 3
> > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > +       done
> > +}
> > +
> > +pushd $TDIR > /dev/null 2>&1
> > +rm -f stoptest
> > +add_files > /dev/null 2>&1 &
> > +mv_files > /dev/null 2>&1 &
> > +read_files > /dev/null 2>&1 &
> > +write_files > /dev/null 2>&1 &
> > +rm_files > /dev/null 2>&1 &
> > +popd > /dev/null 2>&1
> > +
> > +sleep $TIMEO
> > +touch $TDIR/stoptest
> > diff --git a/testcases/kernel/fs/fs-notify/writefile.c b/testcases/kernel/fs/fs-notify/writefile.c
> > new file mode 100644
> > index 000000000..9025b5d95
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/writefile.c
> > @@ -0,0 +1,28 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +       int fd, ret;
> > +       char buf[BUFSIZ];
> > +
> > +       memset(buf, 1, BUFSIZ);
> > +       while (1) {
> > +               fd = open(argv[1], O_RDWR);
> > +               if (fd == -1) {
> > +                       perror("writefile open");
> > +                       exit(EXIT_FAILURE);
> > +               }
> > +               ret = write(fd, buf, BUFSIZ);
> > +               if (ret == -1)
> > +                       perror("writefile write");
> > +               usleep(1);
> > +       }
> > +       close(fd);
> > +       return 0;
> > +}
> > --
> > 2.31.1
> >
> >
> > --
> > Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Murphy

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25  8:30   ` Murphy Zhou
@ 2022-01-25 10:28     ` Petr Vorel
  2022-01-25 10:56     ` Cyril Hrubis
  1 sibling, 0 replies; 18+ messages in thread
From: Petr Vorel @ 2022-01-25 10:28 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: Jan Kara, LTP List

Hi Murphy, Amir,

Amir, thanks a lot for your review, comments.

> Hi Amir,

> On Tue, Jan 25, 2022 at 09:29:47AM +0200, Amir Goldstein wrote:
> > On Tue, Jan 25, 2022 at 4:47 AM Murphy Zhou <jencce.kernel@gmail.com> wrote:

> > > Exercise fsnotify and inotify interfaces while IO going on.

> > Hi Murphy,

> > It's worth documenting in main test script that this test
> > was created as a regression test for commit
> > 4396a73115fc fsnotify: fix sb_connectors leak

> Ah, the tests were created for the goal of stress test. This leak
> was one of the bugs that this test found.
But still, it can be worth to mention it for affected systems without this bug
(v5.14?).

> > Can you elaborate a bit more about the expectation from this test.
> > I do not see any error checking in the main script.

> Sure. Will explain more.

> > Is any perror supposed to fail the test?

> No.

> > Is its purpose to catch kernel dmesg splats of all sorts?

> Not needed.

> My intend is to use these interfaces insanely a while, if kernel 
> does not panic or hang, pass. This test is not checking *notify
> function which has already been covered.

FYI for hitting a race conditions we have fuzzy sync library, it can reduce
running time a lot specially for two race windows (I see you use 2x infinite loop)
https://github.com/linux-test-project/ltp/wiki/C-Test-API#133-reproducing-race-conditions


> > In your original post you wrote that bug makes the test hang:
> > https://lore.kernel.org/linux-fsdevel/20210907063338.ycaw6wvhzrfsfdlp@xzhoux.usersys.redhat.com/

> > If that is the case, better mention this to the LTP maintainers.
+1, helps to understand the context.


Few general notes for porting reproducer to LTP. Takes time, but the result is
support for wide range of kernel versions and different user space (including
different init systems, test are used also on AOSP).
Maybe quite a lot of new things, thus don't hesitate to ask for help.

1) using C API (tst_test.h) helps to have more compact code
i.e. instead of
fd = open(argv[1], O_RDWR);
	if (fd == -1) {
		perror("writefile open");
		exit(EXIT_FAILURE);
	}
	ret = write(fd, buf, BUFSIZ);
	if (ret == -1)
		perror("writefile write");
	usleep(1);

you can have just 2 lines:

fd = SAFE_OPEN(argv[1], O_RDWR);
SAFE_WRITE(fd, buf, BUFSIZ);

See
https://github.com/linux-test-project/ltp/wiki/C-Test-API

2) shell must be POSIX compliant (some systems use other shells e.g. dash), i.e.
no bashisms (e.g. function, pushd, popd).
And for sure no /bin/bash, but /bin/sh

and use LTP shell API (tst_test.sh)

See
https://github.com/linux-test-project/ltp/wiki/Shell-Test-API

fsnotify-stress.sh looks like a custom runner, please don't reinvent a wheel.
We have in the API support for timeout, temporary directories etc.

Also I'd write only necessary minimum in shell (C is much more portable than
shell, also C does not bring unnecessary userspace dependencies).
C API has many helper, you don't have to write things from scratch.

3) make check
You can use make check in testcases/kernel/fs/fs-notify/ directory to
see common C and shell errors.

Below is the output for current code.

Kind regards,
Petr

CHECK testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
fanotify_flush_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-fanotify_flush_stress] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/fanotify_init_stress.c
fanotify_init_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
fanotify_init_stress.c:13: WARNING: Missing a blank line after declarations
make: [../../../../include/mk/rules.mk:48: check-fanotify_init_stress] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
fanotify_mark_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-fanotify_mark_stress] Error 1 (ignored)
fanotify_mark_stress.c:12:6: warning: Symbol 'add_mark' has no prototype or library ('tst_') prefix. Should it be static?
fanotify_mark_stress.c:18:6: warning: Symbol 'remove_mark' has no prototype or library ('tst_') prefix. Should it be static?
CHECK testcases/kernel/fs/fs-notify/fanotify_watch.c
fanotify_watch.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
fanotify_watch.c:15: ERROR: "foo * bar" should be "foo *bar"
fanotify_watch.c:17: ERROR: "foo * bar" should be "foo *bar"
fanotify_watch.c:18: ERROR: "foo * bar" should be "foo *bar"
fanotify_watch.c:60: ERROR: space required before the open parenthesis '('
fanotify_watch.c:88: WARNING: Block comments use * on subsequent lines
fanotify_watch.c:89: WARNING: Block comments use a trailing */ on a separate line
fanotify_watch.c:175: WARNING: Consider removing the code enclosed by this #if 0 and its #endif
fanotify_watch.c:199: WARNING: Block comments use * on subsequent lines
fanotify_watch.c:201: WARNING: Block comments use a trailing */ on a separate line
make: [../../../../include/mk/rules.mk:48: check-fanotify_watch] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/freadfile.c
freadfile.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
freadfile.c:16: ERROR: space required before the open parenthesis '('
make: [../../../../include/mk/rules.mk:48: check-freadfile] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/fwritefile.c
fwritefile.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
fwritefile.c:16: ERROR: space required before the open parenthesis '('
make: [../../../../include/mk/rules.mk:48: check-fwritefile] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
inotify_add_rm_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-inotify_add_rm_stress] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/inotify_init_stress.c
inotify_init_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-inotify_init_stress] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
inotify_inmodify_stress.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
inotify_inmodify_stress.c:11: WARNING: Missing a blank line after declarations
inotify_inmodify_stress.c:13: WARNING: Missing a blank line after declarations
make: [../../../../include/mk/rules.mk:48: check-inotify_inmodify_stress] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/inotify_watch.c
inotify_watch.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
inotify_watch.c:47: WARNING: Missing a blank line after declarations
make: [../../../../include/mk/rules.mk:48: check-inotify_watch] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/readfile.c
readfile.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-readfile] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/writefile.c
writefile.c:1: WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
make: [../../../../include/mk/rules.mk:48: check-writefile] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/fsnotify-stress.sh
possible bashism in fsnotify-stress.sh line 13 ('function' is useless):
function cleanup_processes()
possible bashism in fsnotify-stress.sh line 22 ('function' is useless):
function cleanup()
possible bashism in fsnotify-stress.sh line 31 ('function' is useless):
function run_stress()
possible bashism in fsnotify-stress.sh line 34 (echo -e):
	echo -e "* CPU count: $NR_CPUS"
possible bashism in fsnotify-stress.sh line 35 (echo -e):
	echo -e "* TIMEOUT for each subcase: $TIMEOUT"
possible bashism in fsnotify-stress.sh line 37 (echo -e):
	echo -e "* Starting fsnotify stress on directory and regular file"
possible bashism in fsnotify-stress.sh line 42 (should be 'b = a'):
			[ "$i" == "$j" ] && continue
possible bashism in fsnotify-stress.sh line 45 (echo -e):
			echo -e "* Starting $i and $j, rcnt $rcnt"
possible bashism in fsnotify-stress.sh line 52 (echo -e):
			echo -e "$i $j" >> tmp
possible bashism in fsnotify-stress.sh line 53 ('((' should be '$(('):
			((rcnt++))
possible bashism in fsnotify-stress.sh line 63 (echo -e):
echo -e "\n***** Cleanup fanotify inotify device *****"
make: [../../../../include/mk/rules.mk:58: check-fsnotify-stress.sh] Error 1 (ignored)
CHECK testcases/kernel/fs/fs-notify/rw_files.sh
possible bashism in rw_files.sh line 12 ('function' is useless):
function add_files()
possible bashism in rw_files.sh line 28 ('((' should be '$(('):
		((i++))
possible bashism in rw_files.sh line 29 ($FUNCNAME):
		[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 33 ('function' is useless):
function mv_files()
possible bashism in rw_files.sh line 41 ('((' should be '$(('):
		((i++))
possible bashism in rw_files.sh line 42 ($FUNCNAME):
		[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 46 ('function' is useless):
function read_files()
possible bashism in rw_files.sh line 53 ($FUNCNAME):
		[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 57 ('function' is useless):
function write_files()
possible bashism in rw_files.sh line 64 ($FUNCNAME):
			[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 66 ($FUNCNAME):
		[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 70 ('function' is useless):
function rm_files()
possible bashism in rw_files.sh line 76 ($FUNCNAME):
		[ -e stoptest ] && { echo $FUNCNAME; exit; }
possible bashism in rw_files.sh line 80 ((push|pop)d):
pushd $TDIR > /dev/null 2>&1
possible bashism in rw_files.sh line 87 ((push|pop)d):
popd > /dev/null 2>&1

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25  8:30   ` Murphy Zhou
  2022-01-25 10:28     ` Petr Vorel
@ 2022-01-25 10:56     ` Cyril Hrubis
  1 sibling, 0 replies; 18+ messages in thread
From: Cyril Hrubis @ 2022-01-25 10:56 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: Jan Kara, LTP List

Hi!
> My intend is to use these interfaces insanely a while, if kernel 
> does not panic or hang, pass. This test is not checking *notify
> function which has already been covered.

So looking at the original bug it seems that the end result is a kernel
hang, that sounds like we can adopt the usual approach where LTP tests
that may crash the system print passing message at the end
unconditionally like:

tst_res(TPASS, "Nothing bad happened, probably");

And we have even API for checking kernel taint flags which is used
extensively in CVE tests that can trigger all kinds of undefined
behavior.

> > 
> > In your original post you wrote that bug makes the test hang:
> > https://lore.kernel.org/linux-fsdevel/20210907063338.ycaw6wvhzrfsfdlp@xzhoux.usersys.redhat.com/
> > 
> > If that is the case, better mention this to the LTP maintainers.
> 
> Sure. Thanks for reviewing!
> 
> Murphy
> > 
> > Thanks,
> > Amir.
> > 
> > 
> > 
> > 
> > >
> > > Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> > > ---
> > >  testcases/kernel/fs/fs-notify/Makefile        |  18 ++
> > >  .../fs/fs-notify/fanotify_flush_stress.c      |  52 ++++
> > >  .../fs/fs-notify/fanotify_init_stress.c       |  32 +++
> > >  .../fs/fs-notify/fanotify_mark_stress.c       |  58 +++++
> > >  .../kernel/fs/fs-notify/fanotify_watch.c      | 244 ++++++++++++++++++
> > >  testcases/kernel/fs/fs-notify/freadfile.c     |  27 ++
> > >  .../kernel/fs/fs-notify/fsnotify-stress.sh    |  64 +++++
> > >  testcases/kernel/fs/fs-notify/fwritefile.c    |  27 ++
> > >  .../fs/fs-notify/inotify_add_rm_stress.c      |  36 +++
> > >  .../kernel/fs/fs-notify/inotify_init_stress.c |  22 ++
> > >  .../fs/fs-notify/inotify_inmodify_stress.c    |  17 ++
> > >  testcases/kernel/fs/fs-notify/inotify_watch.c |  98 +++++++
> > >  testcases/kernel/fs/fs-notify/readfile.c      |  28 ++
> > >  testcases/kernel/fs/fs-notify/rw_files.sh     |  90 +++++++
> > >  testcases/kernel/fs/fs-notify/writefile.c     |  28 ++
> > >  15 files changed, 841 insertions(+)
> > >  create mode 100644 testcases/kernel/fs/fs-notify/Makefile
> > >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/fanotify_watch.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/freadfile.c
> > >  create mode 100755 testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > >  create mode 100644 testcases/kernel/fs/fs-notify/fwritefile.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_init_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/inotify_watch.c
> > >  create mode 100644 testcases/kernel/fs/fs-notify/readfile.c
> > >  create mode 100755 testcases/kernel/fs/fs-notify/rw_files.sh
> > >  create mode 100644 testcases/kernel/fs/fs-notify/writefile.c
> > >
> > > diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> > > new file mode 100644
> > > index 000000000..328783942
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/Makefile
> > > @@ -0,0 +1,18 @@
> > > +#
> > > +#    kernel/fs/fs-notify testcases Makefile.
> > > +#
> > > +
> > > +top_srcdir                     ?= ../../../..
> > > +
> > > +include $(top_srcdir)/include/mk/testcases.mk
> > > +
> > > +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> > > +fanotify_mark_stress fanotify_watch inotify_watch \
> > > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> > > +writefile freadfile fwritefile readfile
> > > +
> > > +INSTALL_TARGETS                        := fsnotify-stress.sh $(BINARIES) rw_files.sh
> > > +
> > > +MAKE_TARGETS                   := $(BINARIES)
> > > +
> > > +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> > > diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > > new file mode 100644
> > > index 000000000..493acfb56
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > > @@ -0,0 +1,52 @@
> > > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > > +#include <errno.h>
> > > +#include <fcntl.h>
> > > +#include <limits.h>
> > > +#include <poll.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <sys/fanotify.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       char buf;
> > > +       int fd;
> > > +
> > > +       /* Create the file descriptor for accessing the fanotify API */
> > > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > > +                                          O_RDONLY | O_LARGEFILE);
> > > +       if (fd == -1) {
> > > +               perror("fanotify_init");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       /* Loop marking all kinds of events and flush */
> > > +       while (1) {
> > > +
> > > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > > +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > > +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > > +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > > +
> > > +                       perror("fanotify_mark add");
> > > +
> > > +               if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> > > +                                               0, -1, argv[1]) == -1)
> > > +                       perror("fanotify_mark flush mount");
> > > +
> > > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > > +                         FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > > +                         FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > > +                         FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > > +
> > > +                       perror("fanotify_mark add");
> > > +
> > > +               if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> > > +                       perror("fanotify_mark flush");
> > > +       }
> > > +
> > > +       close(fd);
> > > +       exit(EXIT_SUCCESS);
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > > new file mode 100644
> > > index 000000000..d11924b57
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > > @@ -0,0 +1,32 @@
> > > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > > +#include <errno.h>
> > > +#include <stdio.h>
> > > +#include <unistd.h>
> > > +#include <fcntl.h>
> > > +#include <stdlib.h>
> > > +#include <sys/fanotify.h>
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       char buf;
> > > +       int fd;
> > > +       while (1) {
> > > +
> > > +               /* Create the file descriptor for accessing the fanotify API */
> > > +               fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> > > +                               FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> > > +               if (fd == -1)
> > > +                       perror("fanotify_init");
> > > +
> > > +               if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > > +                               FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > > +                               FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > > +                               FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> > > +                               argv[1]) == -1)
> > > +                       perror("fanotify_mark");
> > > +
> > > +               close(fd);
> > > +       }
> > > +
> > > +       exit(EXIT_SUCCESS);
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > > new file mode 100644
> > > index 000000000..7f648e103
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > > @@ -0,0 +1,58 @@
> > > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > > +#include <errno.h>
> > > +#include <fcntl.h>
> > > +#include <limits.h>
> > > +#include <poll.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <sys/fanotify.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +
> > > +void add_mark(int fd, uint64_t mask, char *path)
> > > +{
> > > +       if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> > > +               perror("fanotify_mark add");
> > > +}
> > > +
> > > +void remove_mark(int fd, uint64_t mask, char *path)
> > > +{
> > > +       if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> > > +               perror("fanotify_mark remove");
> > > +}
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       char buf;
> > > +       int fd;
> > > +       /* Create the file descriptor for accessing the fanotify API */
> > > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > > +                                          O_RDONLY | O_LARGEFILE);
> > > +       if (fd == -1) {
> > > +               perror("fanotify_init");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       /* Loop marking all kinds of events */
> > > +       while (1) {
> > > +               add_mark(fd, FAN_ACCESS, argv[1]);
> > > +               remove_mark(fd, FAN_ACCESS, argv[1]);
> > > +               add_mark(fd, FAN_MODIFY, argv[1]);
> > > +               remove_mark(fd, FAN_MODIFY, argv[1]);
> > > +               add_mark(fd, FAN_OPEN_PERM, argv[1]);
> > > +               remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> > > +               add_mark(fd, FAN_CLOSE, argv[1]);
> > > +               remove_mark(fd, FAN_CLOSE, argv[1]);
> > > +               add_mark(fd, FAN_OPEN, argv[1]);
> > > +               remove_mark(fd, FAN_OPEN, argv[1]);
> > > +               add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > > +               remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > > +               add_mark(fd, FAN_ONDIR, argv[1]);
> > > +               remove_mark(fd, FAN_ONDIR, argv[1]);
> > > +               add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > > +               remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > > +       }
> > > +
> > > +       close(fd);
> > > +       exit(EXIT_SUCCESS);
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > > new file mode 100644
> > > index 000000000..1028e0e30
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > > @@ -0,0 +1,244 @@
> > > +/* Example in man 7 fanotify */
> > > +
> > > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > > +#include <errno.h>
> > > +#include <fcntl.h>
> > > +#include <limits.h>
> > > +#include <poll.h>
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <sys/fanotify.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +
> > > +/* get comm for pid from /proc/pid/comm */
> > > +static char * get_comm_pid(unsigned int pid)
> > > +{
> > > +       char * proc_name;
> > > +       char * comm;
> > > +       int comm_fd, cnt;
> > > +
> > > +       proc_name = (char *)malloc(50);
> > > +       if (proc_name != NULL)
> > > +               sprintf(proc_name, "/proc/%u/comm\0", pid);
> > > +       else
> > > +               return NULL;
> > > +
> > > +       comm = (char *)malloc(50);
> > > +       if (comm != NULL)
> > > +               memset(comm, 0, 50);
> > > +       else
> > > +               return NULL;
> > > +
> > > +       comm_fd = open(proc_name, O_RDONLY);
> > > +       if (comm_fd != -1) {
> > > +               cnt = read(comm_fd, comm, 50);
> > > +               if (cnt != -1) {
> > > +                       comm[cnt] = '\0';
> > > +                       close(comm_fd);
> > > +                       return comm;
> > > +               }
> > > +               close(comm_fd);
> > > +       }
> > > +
> > > +       return NULL;
> > > +}
> > > +
> > > +/* Read all available fanotify events from the file descriptor 'fd' */
> > > +
> > > +static void handle_events(int fd)
> > > +{
> > > +       const struct fanotify_event_metadata *metadata;
> > > +       struct fanotify_event_metadata buf[200];
> > > +       ssize_t len;
> > > +       char path[PATH_MAX];
> > > +       ssize_t path_len;
> > > +       char procfd_path[PATH_MAX];
> > > +       struct fanotify_response response;
> > > +
> > > +       /* Loop while events can be read from fanotify file descriptor */
> > > +       for(;;) {
> > > +
> > > +               /* Read some events */
> > > +               len = read(fd, (void *) &buf, sizeof(buf));
> > > +               if (len == -1 && errno != EAGAIN) {
> > > +                       perror("read");
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +
> > > +               /* Check if end of available data reached */
> > > +               if (len <= 0)
> > > +                       break;
> > > +
> > > +               /* Point to the first event in the buffer */
> > > +               metadata = buf;
> > > +
> > > +               /* Loop over all events in the buffer */
> > > +               while (FAN_EVENT_OK(metadata, len)) {
> > > +
> > > +                       /* Check that run-time and compile-time structures match */
> > > +
> > > +                       if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> > > +                               fprintf(stderr,
> > > +                                   "Mismatch of fanotify metadata version.\n");
> > > +                               exit(EXIT_FAILURE);
> > > +                       }
> > > +
> > > +                       /* metadata->fd contains either FAN_NOFD, indicating a
> > > +                          queue overflow, or a file descriptor (a nonnegative
> > > +                          integer). Here, we simply ignore queue overflow. */
> > > +
> > > +                       if (metadata->fd >= 0) {
> > > +
> > > +                               /* Handle open permission event */
> > > +                               if (metadata->mask & FAN_OPEN_PERM) {
> > > +                                       printf("FAN_OPEN_PERM: ");
> > > +
> > > +                                       /* Allow file to be opened */
> > > +                                       response.fd = metadata->fd;
> > > +                                       response.response = FAN_ALLOW;
> > > +                                       write(fd, &response,
> > > +                                           sizeof(struct fanotify_response));
> > > +                               }
> > > +
> > > +                               /* Handle access permission event */
> > > +                               if (metadata->mask & FAN_ACCESS_PERM) {
> > > +                                       printf("FAN_ACCESS_PERM: ");
> > > +
> > > +                                       /* Allow file to be accessed */
> > > +                                       response.fd = metadata->fd;
> > > +                                       response.response = FAN_ALLOW;
> > > +                                       write(fd, &response,
> > > +                                           sizeof(struct fanotify_response));
> > > +                               }
> > > +
> > > +                               /* Handle closing of writable file event */
> > > +                               if (metadata->mask & FAN_CLOSE_WRITE)
> > > +                                       printf("FAN_CLOSE_WRITE: ");
> > > +
> > > +                               /* Handle closing of not writable file event */
> > > +                               if (metadata->mask & FAN_CLOSE_NOWRITE)
> > > +                                       printf("FAN_CLOSE_NOWRITE: ");
> > > +
> > > +                               /* Handle modify file event */
> > > +                               if (metadata->mask & FAN_MODIFY)
> > > +                                       printf("FAN_MODIFY: ");
> > > +
> > > +                               /* Handle open event */
> > > +                               if (metadata->mask & FAN_OPEN)
> > > +                                       printf("FAN_OPEN: ");
> > > +
> > > +                               /* Handle access event */
> > > +                               if (metadata->mask & FAN_ACCESS)
> > > +                                       printf("FAN_ACCESS: ");
> > > +
> > > +                               /* Handle access event */
> > > +                               if (metadata->mask & FAN_ONDIR)
> > > +                                       printf("FAN_ONDIR: ");
> > > +
> > > +                               /* Handle access event */
> > > +                               if (metadata->mask & FAN_EVENT_ON_CHILD)
> > > +                                       printf("FAN_EVENT_ON_CHILD: ");
> > > +
> > > +                               /* Retrieve/print the accessed file path*/
> > > +                               snprintf(procfd_path, sizeof(procfd_path),
> > > +                                           "/proc/self/fd/%d", metadata->fd);
> > > +                               path_len = readlink(procfd_path, path,
> > > +                                           sizeof(path) - 1);
> > > +                               if (path_len == -1) {
> > > +                                       perror("readlink");
> > > +                                       exit(EXIT_FAILURE);
> > > +                               }
> > > +
> > > +                               path[path_len] = '\0';
> > > +                               printf("File %s.\t\t Comm %s", path,
> > > +                                       get_comm_pid(metadata->pid));
> > > +
> > > +                               /* Close the file descriptor of the event */
> > > +
> > > +                               close(metadata->fd);
> > > +                       }
> > > +
> > > +                       /* Advance to next event */
> > > +                       metadata = FAN_EVENT_NEXT(metadata, len);
> > > +               }
> > > +       }
> > > +}
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       char buf;
> > > +       int fd, poll_num;
> > > +       nfds_t nfds;
> > > +       struct pollfd fds[2];
> > > +       FILE *f;
> > > +#if 0
> > > +       /* Check mount point is supplied */
> > > +       if (argc != 2) {
> > > +               fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +#endif
> > > +       printf("%s on %s\n", argv[0], argv[1]);
> > > +       fflush(stdout);
> > > +       f = freopen("fanotify.log", "w+", stdout);
> > > +       if (f == NULL) {
> > > +               perror("freopen");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       /* Create the file descriptor for accessing the fanotify API */
> > > +       fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > > +                                          O_RDONLY | O_LARGEFILE);
> > > +       if (fd == -1) {
> > > +               perror("fanotify_init");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       /* Mark the mount for:
> > > +          - permission events before opening files
> > > +          - notification events after closing a write-enabled
> > > +                file descriptor */
> > > +       if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > > +                       FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > > +                       FAN_ONDIR | FAN_EVENT_ON_CHILD,
> > > +                       -1, argv[1]) == -1) {
> > > +
> > > +               perror("fanotify_mark");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       /* Prepare for polling */
> > > +       nfds = 1;
> > > +
> > > +       /* Fanotify input */
> > > +       fds[0].fd = fd;
> > > +       fds[0].events = POLLIN;
> > > +
> > > +       /* This is the loop to wait for incoming events */
> > > +       printf("Listening for events.\n");
> > > +       while (1) {
> > > +               poll_num = poll(fds, nfds, -1);
> > > +               if (poll_num == -1) {
> > > +                       if (errno == EINTR)     /* Interrupted by a signal */
> > > +                               continue;           /* Restart poll() */
> > > +
> > > +                       perror("poll");         /* Unexpected error */
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +
> > > +               if (poll_num > 0) {
> > > +
> > > +                       if (fds[0].revents & POLLIN) {
> > > +
> > > +                               /* Fanotify events are available */
> > > +                               handle_events(fd);
> > > +                       }
> > > +               }
> > > +       }
> > > +
> > > +       fclose(f);
> > > +       printf("Listening for events stopped.\n");
> > > +       exit(EXIT_SUCCESS);
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> > > new file mode 100644
> > > index 000000000..24bf76bcd
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> > > @@ -0,0 +1,27 @@
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       int fd;
> > > +       char buf[BUFSIZ];
> > > +       FILE *f;
> > > +
> > > +       memset(buf, 1, BUFSIZ);
> > > +       while(1) {
> > > +               f = fopen(argv[1], "r+");
> > > +               if (f == NULL) {
> > > +                       perror("freadfile fopen");
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +               fread(buf, sizeof(char), BUFSIZ, f);
> > > +               usleep(1);
> > > +       }
> > > +       fclose(f);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > > new file mode 100755
> > > index 000000000..d6fd5482b
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > > @@ -0,0 +1,64 @@
> > > +#!/bin/bash
> > > +
> > > +export TIMEOUT=10
> > > +
> > > +STRESSES="fanotify_flush_stress fanotify_init_stress \
> > > +fanotify_mark_stress fanotify_watch inotify_watch \
> > > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> > > +IO_OPES="writefile freadfile fwritefile readfile"
> > > +
> > > +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> > > +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> > > +
> > > +function cleanup_processes()
> > > +{
> > > +       while ps -eo pid,comm | \
> > > +               grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> > > +               killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> > > +               sleep 1
> > > +       done
> > > +}
> > > +
> > > +function cleanup()
> > > +{
> > > +       sleep 3 # waiting possible unfinished processes
> > > +       cleanup_processes
> > > +       rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> > > +       cd /
> > > +       sync
> > > +}
> > > +
> > > +function run_stress()
> > > +{
> > > +       local i j rcnt=0
> > > +       echo -e "* CPU count: $NR_CPUS"
> > > +       echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> > > +
> > > +       echo -e "* Starting fsnotify stress on directory and regular file"
> > > +       touch $TMPDIR/testfile
> > > +       >tmp
> > > +       for i in $STRESSES $IO_OPES rw_files.sh; do
> > > +               for j in $STRESSES ; do
> > > +                       [ "$i" == "$j" ] && continue
> > > +                       # skip duplicate combinations
> > > +                       grep -w $j tmp | grep -qw $i && continue
> > > +                       echo -e "* Starting $i and $j, rcnt $rcnt"
> > > +                       ./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > > +                       ./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > > +                       ./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > > +                       ./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > > +                       sleep $TIMEOUT
> > > +                       cleanup_processes
> > > +                       echo -e "$i $j" >> tmp
> > > +                       ((rcnt++))
> > > +               done
> > > +       done
> > > +}
> > > +
> > > +trap "cleanup; exit;" 2
> > > +
> > > +echo "***** Starting tests *****"
> > > +run_stress
> > > +
> > > +echo -e "\n***** Cleanup fanotify inotify device *****"
> > > +cleanup
> > > diff --git a/testcases/kernel/fs/fs-notify/fwritefile.c b/testcases/kernel/fs/fs-notify/fwritefile.c
> > > new file mode 100644
> > > index 000000000..a2956d60b
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/fwritefile.c
> > > @@ -0,0 +1,27 @@
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       int fd;
> > > +       char buf[BUFSIZ];
> > > +       FILE *f;
> > > +
> > > +       memset(buf, 1, BUFSIZ);
> > > +       while(1) {
> > > +               f = fopen(argv[1], "w+");
> > > +               if (f == NULL) {
> > > +                       perror("fwritefile fopen");
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +               fwrite(buf, sizeof(char), BUFSIZ, f);
> > > +               usleep(1);
> > > +       }
> > > +       fclose(f);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> > > new file mode 100644
> > > index 000000000..e88fc088e
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/inotify_add_rm_stress.c
> > > @@ -0,0 +1,36 @@
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +#include <unistd.h>
> > > +#include <stdlib.h>
> > > +#include <dirent.h>
> > > +#include <stdio.h>
> > > +#include <sys/inotify.h>
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       int notify_fd;
> > > +       int wd, ret;
> > > +
> > > +       notify_fd = inotify_init1(IN_CLOEXEC);
> > > +       if (notify_fd == -1) {
> > > +               perror("inotify_init1");
> > > +               return -1;
> > > +       }
> > > +
> > > +       while (1) {
> > > +               wd = inotify_add_watch(notify_fd, argv[1],
> > > +                       IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
> > > +                       IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
> > > +                       IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
> > > +                       IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> > > +               if (wd < 0)
> > > +                       perror("inotify_add_watch");
> > > +
> > > +               ret = inotify_rm_watch(notify_fd, wd);
> > > +               if (ret < 0)
> > > +                       perror("inotify_rm_watch");
> > > +       }
> > > +       close(notify_fd);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/inotify_init_stress.c b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> > > new file mode 100644
> > > index 000000000..62cb7c2e6
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/inotify_init_stress.c
> > > @@ -0,0 +1,22 @@
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +#include <unistd.h>
> > > +#include <stdlib.h>
> > > +#include <dirent.h>
> > > +#include <stdio.h>
> > > +#include <sys/inotify.h>
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       int notify_fd;
> > > +       int wd;
> > > +
> > > +       while (1) {
> > > +               notify_fd = inotify_init1(IN_CLOEXEC);
> > > +               if (notify_fd == -1)
> > > +                       perror("inotify_init1");
> > > +               close(notify_fd);
> > > +       }
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> > > new file mode 100644
> > > index 000000000..546ccb76f
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/inotify_inmodify_stress.c
> > > @@ -0,0 +1,17 @@
> > > +#include <sys/inotify.h>
> > > +#include <stdio.h>
> > > +#include <unistd.h>
> > > +
> > > +/*
> > > + * Calls inotify_rm_watch in a loop.
> > > + */
> > > +int main(int argc, char **argv)
> > > +{
> > > +       int fd = inotify_init();
> > > +       while (1) {
> > > +               int wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
> > > +               inotify_rm_watch(fd, wd);
> > > +       }
> > > +       close(fd);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/inotify_watch.c b/testcases/kernel/fs/fs-notify/inotify_watch.c
> > > new file mode 100644
> > > index 000000000..b4b908b6b
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/inotify_watch.c
> > > @@ -0,0 +1,98 @@
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +#include <unistd.h>
> > > +#include <stdlib.h>
> > > +#include <dirent.h>
> > > +#include <stdio.h>
> > > +#include <sys/inotify.h>
> > > +
> > > +int main(int argc, char *argv[])
> > > +{
> > > +       int notify_fd;
> > > +       int wd, ret;
> > > +       char *buf;
> > > +       FILE *f;
> > > +
> > > +       f = freopen("inotify.log", "w+", stdout);
> > > +       if (f == NULL) {
> > > +               perror("freopen");
> > > +               exit(EXIT_FAILURE);
> > > +       }
> > > +
> > > +       buf = malloc(sizeof(struct inotify_event) + NAME_MAX + 1);
> > > +       if (buf == NULL) {
> > > +               perror("malloc");
> > > +               return -1;
> > > +       }
> > > +
> > > +       notify_fd = inotify_init1(IN_CLOEXEC);
> > > +       if (notify_fd == -1) {
> > > +               perror("inotify_init1");
> > > +               return -1;
> > > +       }
> > > +
> > > +       wd = inotify_add_watch(notify_fd, argv[1],
> > > +               IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE |
> > > +               IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
> > > +               IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> > > +
> > > +       if (wd < 0) {
> > > +               perror("inotify_add_watch");
> > > +               return -1;
> > > +       }
> > > +
> > > +       while (ret = read(notify_fd, buf, NAME_MAX) != -1) {
> > > +               struct inotify_event *ip = (struct inotify_event *)buf;
> > > +               printf("name %s event ", ip->name);
> > > +               switch (ip->mask) {
> > > +               case IN_ACCESS:
> > > +                       printf("access\n");
> > > +                       break;
> > > +               case IN_ATTRIB:
> > > +                       printf("attrib\n");
> > > +                       break;
> > > +               case IN_CLOSE_WRITE:
> > > +                       printf("close write\n");
> > > +                       break;
> > > +               case IN_CLOSE_NOWRITE:
> > > +                       printf("close nowrite\n");
> > > +                       break;
> > > +               case IN_CREATE:
> > > +                       printf("create\n");
> > > +                       break;
> > > +               case IN_DELETE:
> > > +                       printf("delete\n");
> > > +                       break;
> > > +               case IN_DELETE_SELF:
> > > +                       printf("deleteself\n");
> > > +                       break;
> > > +               case IN_MODIFY:
> > > +                       printf("modify\n");
> > > +                       break;
> > > +               case IN_MOVE_SELF:
> > > +                       printf("move self\n");
> > > +                       break;
> > > +               case IN_MOVED_FROM:
> > > +                       printf("move from\n");
> > > +                       break;
> > > +               case IN_MOVED_TO:
> > > +                       printf("move to\n");
> > > +                       break;
> > > +               case IN_OPEN:
> > > +                       printf("open\n");
> > > +                       break;
> > > +               default:
> > > +                       printf("\n");
> > > +                       break;
> > > +               };
> > > +       }
> > > +
> > > +       ret = inotify_rm_watch(notify_fd, wd);
> > > +       if (ret < 0)
> > > +               perror("inotify_rm_watch");
> > > +
> > > +       fclose(f);
> > > +       close(notify_fd);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/readfile.c b/testcases/kernel/fs/fs-notify/readfile.c
> > > new file mode 100644
> > > index 000000000..2ab1a3694
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/readfile.c
> > > @@ -0,0 +1,28 @@
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       int fd, ret;
> > > +       char buf[BUFSIZ];
> > > +
> > > +       memset(buf, 1, BUFSIZ);
> > > +       while (1) {
> > > +               fd = open(argv[1], O_RDWR);
> > > +               if (fd == -1) {
> > > +                       perror("readfile open");
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +               ret = read(fd, buf, BUFSIZ);
> > > +               if (ret == -1)
> > > +                       perror("readfile read");
> > > +               usleep(1);
> > > +       }
> > > +       close(fd);
> > > +       return 0;
> > > +}
> > > diff --git a/testcases/kernel/fs/fs-notify/rw_files.sh b/testcases/kernel/fs/fs-notify/rw_files.sh
> > > new file mode 100755
> > > index 000000000..bb3387160
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/rw_files.sh
> > > @@ -0,0 +1,90 @@
> > > +#!/bin/bash
> > > +
> > > +TDIR=$1
> > > +TIMEO=$2
> > > +
> > > +[ -z "$TDIR" ] && TDIR=/tmp/
> > > +[ -d $TDIR ] || TDIR=/tmp/
> > > +[ -z "$TIMEO" ] && TIMEO=1m
> > > +
> > > +[ ! -d $TDIR ] && exit
> > > +
> > > +function add_files()
> > > +{
> > > +       local i=0
> > > +
> > > +       while true ; do
> > > +
> > > +               touch f-$i
> > > +               ln -s f-$i f-$i-sym
> > > +               ln f-$i f-$i-hdl
> > > +
> > > +               mkdir d-$i
> > > +
> > > +               mknod c-$i c 1 2
> > > +               mknod b-$i b 1 2
> > > +               mknod p-$i p
> > > +
> > > +               ((i++))
> > > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +       done
> > > +}
> > > +
> > > +function mv_files()
> > > +{
> > > +       local i=0
> > > +
> > > +       while true ; do
> > > +
> > > +               mv -f f-$i f-$i-rename
> > > +               mv -f f-$i-rename f-$i
> > > +               ((i++))
> > > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +       done
> > > +}
> > > +
> > > +function read_files()
> > > +{
> > > +       while true ; do
> > > +
> > > +               find .
> > > +               cat f-*
> > > +               ls d-*
> > > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +       done
> > > +}
> > > +
> > > +function write_files()
> > > +{
> > > +       while true ; do
> > > +
> > > +               for j in f-* d-* c-* b-* p-* ; do
> > > +                       echo 1 > $j
> > > +                       echo 2 >> $j
> > > +                       [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +               done
> > > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +       done
> > > +}
> > > +
> > > +function rm_files()
> > > +{
> > > +       while true ; do
> > > +
> > > +               rm -rf d-* f-* c-* b-* p-*
> > > +               sleep 3
> > > +               [ -e stoptest ] && { echo $FUNCNAME; exit; }
> > > +       done
> > > +}
> > > +
> > > +pushd $TDIR > /dev/null 2>&1
> > > +rm -f stoptest
> > > +add_files > /dev/null 2>&1 &
> > > +mv_files > /dev/null 2>&1 &
> > > +read_files > /dev/null 2>&1 &
> > > +write_files > /dev/null 2>&1 &
> > > +rm_files > /dev/null 2>&1 &
> > > +popd > /dev/null 2>&1
> > > +
> > > +sleep $TIMEO
> > > +touch $TDIR/stoptest
> > > diff --git a/testcases/kernel/fs/fs-notify/writefile.c b/testcases/kernel/fs/fs-notify/writefile.c
> > > new file mode 100644
> > > index 000000000..9025b5d95
> > > --- /dev/null
> > > +++ b/testcases/kernel/fs/fs-notify/writefile.c
> > > @@ -0,0 +1,28 @@
> > > +#include <stdio.h>
> > > +#include <stdlib.h>
> > > +#include <unistd.h>
> > > +#include <string.h>
> > > +#include <sys/types.h>
> > > +#include <sys/stat.h>
> > > +#include <fcntl.h>
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > +       int fd, ret;
> > > +       char buf[BUFSIZ];
> > > +
> > > +       memset(buf, 1, BUFSIZ);
> > > +       while (1) {
> > > +               fd = open(argv[1], O_RDWR);
> > > +               if (fd == -1) {
> > > +                       perror("writefile open");
> > > +                       exit(EXIT_FAILURE);
> > > +               }
> > > +               ret = write(fd, buf, BUFSIZ);
> > > +               if (ret == -1)
> > > +                       perror("writefile write");
> > > +               usleep(1);
> > > +       }
> > > +       close(fd);
> > > +       return 0;
> > > +}
> > > --
> > > 2.31.1
> > >
> > >
> > > --
> > > Mailing list info: https://lists.linux.it/listinfo/ltp
> 
> -- 
> Murphy
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp
> 

-- 
Cyril Hrubis
chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25  2:47 [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests Murphy Zhou
  2022-01-25  7:29 ` Amir Goldstein
@ 2022-01-25 11:04 ` Cyril Hrubis
  2022-01-27  4:42   ` Murphy Zhou
  2022-02-28  6:29   ` Murphy Zhou
  1 sibling, 2 replies; 18+ messages in thread
From: Cyril Hrubis @ 2022-01-25 11:04 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: ltp

Hi!
> diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> new file mode 100644
> index 000000000..328783942
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/Makefile
> @@ -0,0 +1,18 @@
> +#
> +#    kernel/fs/fs-notify testcases Makefile.
> +#
> +
> +top_srcdir			?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> +fanotify_mark_stress fanotify_watch inotify_watch \
> +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> +writefile freadfile fwritefile readfile
> +
> +INSTALL_TARGETS			:= fsnotify-stress.sh $(BINARIES) rw_files.sh
> +
> +MAKE_TARGETS			:= $(BINARIES)
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> new file mode 100644
> index 000000000..493acfb56
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> @@ -0,0 +1,52 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +int main(int argc, char *argv[])
> +{
> +	char buf;
> +	int fd;
> +
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +	if (fd == -1) {
> +		perror("fanotify_init");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Loop marking all kinds of events and flush */
> +	while (1) {
> +
> +		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +			  FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> +
> +			perror("fanotify_mark add");
> +
> +		if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> +						0, -1, argv[1]) == -1)
> +			perror("fanotify_mark flush mount");
> +
> +		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +			  FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> +
> +			perror("fanotify_mark add");
> +
> +		if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> +			perror("fanotify_mark flush");
> +	}
> +
> +	close(fd);
> +	exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> new file mode 100644
> index 000000000..d11924b57
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> @@ -0,0 +1,32 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +
> +int main(int argc, char *argv[])
> +{
> +	char buf;
> +	int fd;
> +	while (1) {
> +
> +		/* Create the file descriptor for accessing the fanotify API */
> +		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> +				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> +		if (fd == -1)
> +			perror("fanotify_init");
> +
> +		if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> +				argv[1]) == -1)
> +			perror("fanotify_mark");
> +
> +		close(fd);
> +	}
> +
> +	exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> new file mode 100644
> index 000000000..7f648e103
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> @@ -0,0 +1,58 @@
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +void add_mark(int fd, uint64_t mask, char *path)
> +{
> +	if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> +		perror("fanotify_mark add");
> +}
> +
> +void remove_mark(int fd, uint64_t mask, char *path)
> +{
> +	if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> +		perror("fanotify_mark remove");
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	char buf;
> +	int fd;
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +	if (fd == -1) {
> +		perror("fanotify_init");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Loop marking all kinds of events */
> +	while (1) {
> +		add_mark(fd, FAN_ACCESS, argv[1]);
> +		remove_mark(fd, FAN_ACCESS, argv[1]);
> +		add_mark(fd, FAN_MODIFY, argv[1]);
> +		remove_mark(fd, FAN_MODIFY, argv[1]);
> +		add_mark(fd, FAN_OPEN_PERM, argv[1]);
> +		remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> +		add_mark(fd, FAN_CLOSE, argv[1]);
> +		remove_mark(fd, FAN_CLOSE, argv[1]);
> +		add_mark(fd, FAN_OPEN, argv[1]);
> +		remove_mark(fd, FAN_OPEN, argv[1]);
> +		add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> +		remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> +		add_mark(fd, FAN_ONDIR, argv[1]);
> +		remove_mark(fd, FAN_ONDIR, argv[1]);
> +		add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> +		remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> +	}
> +
> +	close(fd);
> +	exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> new file mode 100644
> index 000000000..1028e0e30
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> @@ -0,0 +1,244 @@
> +/* Example in man 7 fanotify */
> +
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/fanotify.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +/* get comm for pid from /proc/pid/comm */
> +static char * get_comm_pid(unsigned int pid)
> +{
> +	char * proc_name;
> +	char * comm;
> +	int comm_fd, cnt;
> +
> +	proc_name = (char *)malloc(50);
> +	if (proc_name != NULL)
> +		sprintf(proc_name, "/proc/%u/comm\0", pid);
> +	else
> +		return NULL;
> +
> +	comm = (char *)malloc(50);
> +	if (comm != NULL)
> +		memset(comm, 0, 50);
> +	else
> +		return NULL;
> +
> +	comm_fd = open(proc_name, O_RDONLY);
> +	if (comm_fd != -1) {
> +		cnt = read(comm_fd, comm, 50);
> +		if (cnt != -1) {
> +			comm[cnt] = '\0';
> +			close(comm_fd);
> +			return comm;
> +		}
> +		close(comm_fd);
> +	}
> +
> +	return NULL;
> +}
> +
> +/* Read all available fanotify events from the file descriptor 'fd' */
> +
> +static void handle_events(int fd)
> +{
> +	const struct fanotify_event_metadata *metadata;
> +	struct fanotify_event_metadata buf[200];
> +	ssize_t len;
> +	char path[PATH_MAX];
> +	ssize_t path_len;
> +	char procfd_path[PATH_MAX];
> +	struct fanotify_response response;
> +
> +	/* Loop while events can be read from fanotify file descriptor */
> +	for(;;) {
> +
> +		/* Read some events */
> +		len = read(fd, (void *) &buf, sizeof(buf));
> +		if (len == -1 && errno != EAGAIN) {
> +			perror("read");
> +			exit(EXIT_FAILURE);
> +		}
> +
> +		/* Check if end of available data reached */
> +		if (len <= 0)
> +			break;
> +
> +		/* Point to the first event in the buffer */
> +		metadata = buf;
> +
> +		/* Loop over all events in the buffer */
> +		while (FAN_EVENT_OK(metadata, len)) {
> +
> +			/* Check that run-time and compile-time structures match */
> +
> +			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> +				fprintf(stderr,
> +				    "Mismatch of fanotify metadata version.\n");
> +				exit(EXIT_FAILURE);
> +			}
> +
> +			/* metadata->fd contains either FAN_NOFD, indicating a
> +			   queue overflow, or a file descriptor (a nonnegative
> +			   integer). Here, we simply ignore queue overflow. */
> +
> +			if (metadata->fd >= 0) {
> +
> +				/* Handle open permission event */
> +				if (metadata->mask & FAN_OPEN_PERM) {
> +					printf("FAN_OPEN_PERM: ");
> +
> +					/* Allow file to be opened */
> +					response.fd = metadata->fd;
> +					response.response = FAN_ALLOW;
> +					write(fd, &response,
> +					    sizeof(struct fanotify_response));
> +				}
> +
> +				/* Handle access permission event */
> +				if (metadata->mask & FAN_ACCESS_PERM) {
> +					printf("FAN_ACCESS_PERM: ");
> +
> +					/* Allow file to be accessed */
> +					response.fd = metadata->fd;
> +					response.response = FAN_ALLOW;
> +					write(fd, &response,
> +					    sizeof(struct fanotify_response));
> +				}
> +
> +				/* Handle closing of writable file event */
> +				if (metadata->mask & FAN_CLOSE_WRITE)
> +					printf("FAN_CLOSE_WRITE: ");
> +
> +				/* Handle closing of not writable file event */
> +				if (metadata->mask & FAN_CLOSE_NOWRITE)
> +					printf("FAN_CLOSE_NOWRITE: ");
> +
> +				/* Handle modify file event */
> +				if (metadata->mask & FAN_MODIFY)
> +					printf("FAN_MODIFY: ");
> +
> +				/* Handle open event */
> +				if (metadata->mask & FAN_OPEN)
> +					printf("FAN_OPEN: ");
> +
> +				/* Handle access event */
> +				if (metadata->mask & FAN_ACCESS)
> +					printf("FAN_ACCESS: ");
> +
> +				/* Handle access event */
> +				if (metadata->mask & FAN_ONDIR)
> +					printf("FAN_ONDIR: ");
> +
> +				/* Handle access event */
> +				if (metadata->mask & FAN_EVENT_ON_CHILD)
> +					printf("FAN_EVENT_ON_CHILD: ");
> +
> +				/* Retrieve/print the accessed file path*/
> +				snprintf(procfd_path, sizeof(procfd_path),
> +					    "/proc/self/fd/%d", metadata->fd);
> +				path_len = readlink(procfd_path, path,
> +					    sizeof(path) - 1);
> +				if (path_len == -1) {
> +					perror("readlink");
> +					exit(EXIT_FAILURE);
> +				}
> +
> +				path[path_len] = '\0';
> +				printf("File %s.\t\t Comm %s", path,
> +					get_comm_pid(metadata->pid));
> +
> +				/* Close the file descriptor of the event */
> +
> +				close(metadata->fd);
> +			}
> +
> +			/* Advance to next event */
> +			metadata = FAN_EVENT_NEXT(metadata, len);
> +		}
> +	}
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	char buf;
> +	int fd, poll_num;
> +	nfds_t nfds;
> +	struct pollfd fds[2];
> +	FILE *f;
> +#if 0
> +	/* Check mount point is supplied */
> +	if (argc != 2) {
> +		fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> +		exit(EXIT_FAILURE);
> +	}
> +#endif
> +	printf("%s on %s\n", argv[0], argv[1]);
> +	fflush(stdout);
> +	f = freopen("fanotify.log", "w+", stdout);
> +	if (f == NULL) {
> +		perror("freopen");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +	if (fd == -1) {
> +		perror("fanotify_init");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Mark the mount for:
> +	   - permission events before opening files
> +	   - notification events after closing a write-enabled
> +		 file descriptor */
> +	if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +			FAN_ONDIR | FAN_EVENT_ON_CHILD,
> +			-1, argv[1]) == -1) {
> +
> +		perror("fanotify_mark");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	/* Prepare for polling */
> +	nfds = 1;
> +
> +	/* Fanotify input */
> +	fds[0].fd = fd;
> +	fds[0].events = POLLIN;
> +
> +	/* This is the loop to wait for incoming events */
> +	printf("Listening for events.\n");
> +	while (1) {
> +		poll_num = poll(fds, nfds, -1);
> +		if (poll_num == -1) {
> +			if (errno == EINTR)     /* Interrupted by a signal */
> +				continue;           /* Restart poll() */
> +
> +			perror("poll");         /* Unexpected error */
> +			exit(EXIT_FAILURE);
> +		}
> +
> +		if (poll_num > 0) {
> +
> +			if (fds[0].revents & POLLIN) {
> +
> +				/* Fanotify events are available */
> +				handle_events(fd);
> +			}
> +		}
> +	}
> +
> +	fclose(f);
> +	printf("Listening for events stopped.\n");
> +	exit(EXIT_SUCCESS);
> +}
> diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> new file mode 100644
> index 000000000..24bf76bcd
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> @@ -0,0 +1,27 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +int main(int argc, char **argv)
> +{
> +	int fd;
> +	char buf[BUFSIZ];
> +	FILE *f;
> +
> +	memset(buf, 1, BUFSIZ);
> +	while(1) {
> +		f = fopen(argv[1], "r+");
> +		if (f == NULL) {
> +			perror("freadfile fopen");
> +			exit(EXIT_FAILURE);
> +		}
> +		fread(buf, sizeof(char), BUFSIZ, f);
> +		usleep(1);
> +	}
> +	fclose(f);
> +	return 0;
> +}

There is no real reason to keep these as several separate files. We can
actually put the whole test into one single C source that forks severa
times and each child runs one of loops from these helpers.

> diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> new file mode 100755
> index 000000000..d6fd5482b
> --- /dev/null
> +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> @@ -0,0 +1,64 @@
> +#!/bin/bash
> +
> +export TIMEOUT=10
> +
> +STRESSES="fanotify_flush_stress fanotify_init_stress \
> +fanotify_mark_stress fanotify_watch inotify_watch \
> +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> +IO_OPES="writefile freadfile fwritefile readfile"
> +
> +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> +
> +function cleanup_processes()
> +{
> +	while ps -eo pid,comm | \
> +		grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> +		killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> +		sleep 1
> +	done
> +}
> +
> +function cleanup()
> +{
> +	sleep 3 # waiting possible unfinished processes
> +	cleanup_processes
> +	rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> +	cd /
> +	sync
> +}
> +
> +function run_stress()
> +{
> +	local i j rcnt=0
> +	echo -e "* CPU count: $NR_CPUS"
> +	echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> +
> +	echo -e "* Starting fsnotify stress on directory and regular file"
> +	touch $TMPDIR/testfile
> +	>tmp
> +	for i in $STRESSES $IO_OPES rw_files.sh; do
> +		for j in $STRESSES ; do
> +			[ "$i" == "$j" ] && continue
> +			# skip duplicate combinations
> +			grep -w $j tmp | grep -qw $i && continue
> +			echo -e "* Starting $i and $j, rcnt $rcnt"
> +			./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> +			./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> +			./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> +			./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> +			sleep $TIMEOUT
> +			cleanup_processes
> +			echo -e "$i $j" >> tmp
> +			((rcnt++))
> +		done
> +	done
> +}
> +
> +trap "cleanup; exit;" 2
> +
> +echo "***** Starting tests *****"
> +run_stress
> +
> +echo -e "\n***** Cleanup fanotify inotify device *****"
> +cleanup

This could be easily rewritten in the LTP C API. We do have helper sofr
most of the functionality that is reimplemented poorly in shell here.

So the whole test should really be a single C source where the testrun
forks children that runs the stress and I/O rutines that are currently
in the $BINARIES and the main pid would watch over the execution.

-- 
Cyril Hrubis
chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25 11:04 ` Cyril Hrubis
@ 2022-01-27  4:42   ` Murphy Zhou
  2022-02-28  6:29   ` Murphy Zhou
  1 sibling, 0 replies; 18+ messages in thread
From: Murphy Zhou @ 2022-01-27  4:42 UTC (permalink / raw)
  To: Cyril Hrubis; +Cc: LTP List

Thanks all for reviewing! Very good points.

I'll try my best.

On Tue, Jan 25, 2022 at 7:02 PM Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> > new file mode 100644
> > index 000000000..328783942
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/Makefile
> > @@ -0,0 +1,18 @@
> > +#
> > +#    kernel/fs/fs-notify testcases Makefile.
> > +#
> > +
> > +top_srcdir                   ?= ../../../..
> > +
> > +include $(top_srcdir)/include/mk/testcases.mk
> > +
> > +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> > +writefile freadfile fwritefile readfile
> > +
> > +INSTALL_TARGETS                      := fsnotify-stress.sh $(BINARIES) rw_files.sh
> > +
> > +MAKE_TARGETS                 := $(BINARIES)
> > +
> > +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > new file mode 100644
> > index 000000000..493acfb56
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > @@ -0,0 +1,52 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Loop marking all kinds of events and flush */
> > +     while (1) {
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                       FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                       FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                     perror("fanotify_mark add");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> > +                                             0, -1, argv[1]) == -1)
> > +                     perror("fanotify_mark flush mount");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                       FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                       FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                     perror("fanotify_mark add");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> > +                     perror("fanotify_mark flush");
> > +     }
> > +
> > +     close(fd);
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > new file mode 100644
> > index 000000000..d11924b57
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > @@ -0,0 +1,32 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +     while (1) {
> > +
> > +             /* Create the file descriptor for accessing the fanotify API */
> > +             fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> > +                             FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> > +             if (fd == -1)
> > +                     perror("fanotify_init");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                             FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                             FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                             FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> > +                             argv[1]) == -1)
> > +                     perror("fanotify_mark");
> > +
> > +             close(fd);
> > +     }
> > +
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > new file mode 100644
> > index 000000000..7f648e103
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > @@ -0,0 +1,58 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +void add_mark(int fd, uint64_t mask, char *path)
> > +{
> > +     if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> > +             perror("fanotify_mark add");
> > +}
> > +
> > +void remove_mark(int fd, uint64_t mask, char *path)
> > +{
> > +     if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> > +             perror("fanotify_mark remove");
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Loop marking all kinds of events */
> > +     while (1) {
> > +             add_mark(fd, FAN_ACCESS, argv[1]);
> > +             remove_mark(fd, FAN_ACCESS, argv[1]);
> > +             add_mark(fd, FAN_MODIFY, argv[1]);
> > +             remove_mark(fd, FAN_MODIFY, argv[1]);
> > +             add_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +             remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +             add_mark(fd, FAN_CLOSE, argv[1]);
> > +             remove_mark(fd, FAN_CLOSE, argv[1]);
> > +             add_mark(fd, FAN_OPEN, argv[1]);
> > +             remove_mark(fd, FAN_OPEN, argv[1]);
> > +             add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +             remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +             add_mark(fd, FAN_ONDIR, argv[1]);
> > +             remove_mark(fd, FAN_ONDIR, argv[1]);
> > +             add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +             remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +     }
> > +
> > +     close(fd);
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > new file mode 100644
> > index 000000000..1028e0e30
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > @@ -0,0 +1,244 @@
> > +/* Example in man 7 fanotify */
> > +
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +/* get comm for pid from /proc/pid/comm */
> > +static char * get_comm_pid(unsigned int pid)
> > +{
> > +     char * proc_name;
> > +     char * comm;
> > +     int comm_fd, cnt;
> > +
> > +     proc_name = (char *)malloc(50);
> > +     if (proc_name != NULL)
> > +             sprintf(proc_name, "/proc/%u/comm\0", pid);
> > +     else
> > +             return NULL;
> > +
> > +     comm = (char *)malloc(50);
> > +     if (comm != NULL)
> > +             memset(comm, 0, 50);
> > +     else
> > +             return NULL;
> > +
> > +     comm_fd = open(proc_name, O_RDONLY);
> > +     if (comm_fd != -1) {
> > +             cnt = read(comm_fd, comm, 50);
> > +             if (cnt != -1) {
> > +                     comm[cnt] = '\0';
> > +                     close(comm_fd);
> > +                     return comm;
> > +             }
> > +             close(comm_fd);
> > +     }
> > +
> > +     return NULL;
> > +}
> > +
> > +/* Read all available fanotify events from the file descriptor 'fd' */
> > +
> > +static void handle_events(int fd)
> > +{
> > +     const struct fanotify_event_metadata *metadata;
> > +     struct fanotify_event_metadata buf[200];
> > +     ssize_t len;
> > +     char path[PATH_MAX];
> > +     ssize_t path_len;
> > +     char procfd_path[PATH_MAX];
> > +     struct fanotify_response response;
> > +
> > +     /* Loop while events can be read from fanotify file descriptor */
> > +     for(;;) {
> > +
> > +             /* Read some events */
> > +             len = read(fd, (void *) &buf, sizeof(buf));
> > +             if (len == -1 && errno != EAGAIN) {
> > +                     perror("read");
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +
> > +             /* Check if end of available data reached */
> > +             if (len <= 0)
> > +                     break;
> > +
> > +             /* Point to the first event in the buffer */
> > +             metadata = buf;
> > +
> > +             /* Loop over all events in the buffer */
> > +             while (FAN_EVENT_OK(metadata, len)) {
> > +
> > +                     /* Check that run-time and compile-time structures match */
> > +
> > +                     if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> > +                             fprintf(stderr,
> > +                                 "Mismatch of fanotify metadata version.\n");
> > +                             exit(EXIT_FAILURE);
> > +                     }
> > +
> > +                     /* metadata->fd contains either FAN_NOFD, indicating a
> > +                        queue overflow, or a file descriptor (a nonnegative
> > +                        integer). Here, we simply ignore queue overflow. */
> > +
> > +                     if (metadata->fd >= 0) {
> > +
> > +                             /* Handle open permission event */
> > +                             if (metadata->mask & FAN_OPEN_PERM) {
> > +                                     printf("FAN_OPEN_PERM: ");
> > +
> > +                                     /* Allow file to be opened */
> > +                                     response.fd = metadata->fd;
> > +                                     response.response = FAN_ALLOW;
> > +                                     write(fd, &response,
> > +                                         sizeof(struct fanotify_response));
> > +                             }
> > +
> > +                             /* Handle access permission event */
> > +                             if (metadata->mask & FAN_ACCESS_PERM) {
> > +                                     printf("FAN_ACCESS_PERM: ");
> > +
> > +                                     /* Allow file to be accessed */
> > +                                     response.fd = metadata->fd;
> > +                                     response.response = FAN_ALLOW;
> > +                                     write(fd, &response,
> > +                                         sizeof(struct fanotify_response));
> > +                             }
> > +
> > +                             /* Handle closing of writable file event */
> > +                             if (metadata->mask & FAN_CLOSE_WRITE)
> > +                                     printf("FAN_CLOSE_WRITE: ");
> > +
> > +                             /* Handle closing of not writable file event */
> > +                             if (metadata->mask & FAN_CLOSE_NOWRITE)
> > +                                     printf("FAN_CLOSE_NOWRITE: ");
> > +
> > +                             /* Handle modify file event */
> > +                             if (metadata->mask & FAN_MODIFY)
> > +                                     printf("FAN_MODIFY: ");
> > +
> > +                             /* Handle open event */
> > +                             if (metadata->mask & FAN_OPEN)
> > +                                     printf("FAN_OPEN: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_ACCESS)
> > +                                     printf("FAN_ACCESS: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_ONDIR)
> > +                                     printf("FAN_ONDIR: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_EVENT_ON_CHILD)
> > +                                     printf("FAN_EVENT_ON_CHILD: ");
> > +
> > +                             /* Retrieve/print the accessed file path*/
> > +                             snprintf(procfd_path, sizeof(procfd_path),
> > +                                         "/proc/self/fd/%d", metadata->fd);
> > +                             path_len = readlink(procfd_path, path,
> > +                                         sizeof(path) - 1);
> > +                             if (path_len == -1) {
> > +                                     perror("readlink");
> > +                                     exit(EXIT_FAILURE);
> > +                             }
> > +
> > +                             path[path_len] = '\0';
> > +                             printf("File %s.\t\t Comm %s", path,
> > +                                     get_comm_pid(metadata->pid));
> > +
> > +                             /* Close the file descriptor of the event */
> > +
> > +                             close(metadata->fd);
> > +                     }
> > +
> > +                     /* Advance to next event */
> > +                     metadata = FAN_EVENT_NEXT(metadata, len);
> > +             }
> > +     }
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd, poll_num;
> > +     nfds_t nfds;
> > +     struct pollfd fds[2];
> > +     FILE *f;
> > +#if 0
> > +     /* Check mount point is supplied */
> > +     if (argc != 2) {
> > +             fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> > +             exit(EXIT_FAILURE);
> > +     }
> > +#endif
> > +     printf("%s on %s\n", argv[0], argv[1]);
> > +     fflush(stdout);
> > +     f = freopen("fanotify.log", "w+", stdout);
> > +     if (f == NULL) {
> > +             perror("freopen");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Mark the mount for:
> > +        - permission events before opening files
> > +        - notification events after closing a write-enabled
> > +              file descriptor */
> > +     if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                     FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                     FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                     FAN_ONDIR | FAN_EVENT_ON_CHILD,
> > +                     -1, argv[1]) == -1) {
> > +
> > +             perror("fanotify_mark");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Prepare for polling */
> > +     nfds = 1;
> > +
> > +     /* Fanotify input */
> > +     fds[0].fd = fd;
> > +     fds[0].events = POLLIN;
> > +
> > +     /* This is the loop to wait for incoming events */
> > +     printf("Listening for events.\n");
> > +     while (1) {
> > +             poll_num = poll(fds, nfds, -1);
> > +             if (poll_num == -1) {
> > +                     if (errno == EINTR)     /* Interrupted by a signal */
> > +                             continue;           /* Restart poll() */
> > +
> > +                     perror("poll");         /* Unexpected error */
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +
> > +             if (poll_num > 0) {
> > +
> > +                     if (fds[0].revents & POLLIN) {
> > +
> > +                             /* Fanotify events are available */
> > +                             handle_events(fd);
> > +                     }
> > +             }
> > +     }
> > +
> > +     fclose(f);
> > +     printf("Listening for events stopped.\n");
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> > new file mode 100644
> > index 000000000..24bf76bcd
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> > @@ -0,0 +1,27 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     int fd;
> > +     char buf[BUFSIZ];
> > +     FILE *f;
> > +
> > +     memset(buf, 1, BUFSIZ);
> > +     while(1) {
> > +             f = fopen(argv[1], "r+");
> > +             if (f == NULL) {
> > +                     perror("freadfile fopen");
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +             fread(buf, sizeof(char), BUFSIZ, f);
> > +             usleep(1);
> > +     }
> > +     fclose(f);
> > +     return 0;
> > +}
>
> There is no real reason to keep these as several separate files. We can
> actually put the whole test into one single C source that forks severa
> times and each child runs one of loops from these helpers.
>
> > diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > new file mode 100755
> > index 000000000..d6fd5482b
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > @@ -0,0 +1,64 @@
> > +#!/bin/bash
> > +
> > +export TIMEOUT=10
> > +
> > +STRESSES="fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> > +IO_OPES="writefile freadfile fwritefile readfile"
> > +
> > +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> > +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> > +
> > +function cleanup_processes()
> > +{
> > +     while ps -eo pid,comm | \
> > +             grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> > +             killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> > +             sleep 1
> > +     done
> > +}
> > +
> > +function cleanup()
> > +{
> > +     sleep 3 # waiting possible unfinished processes
> > +     cleanup_processes
> > +     rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> > +     cd /
> > +     sync
> > +}
> > +
> > +function run_stress()
> > +{
> > +     local i j rcnt=0
> > +     echo -e "* CPU count: $NR_CPUS"
> > +     echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> > +
> > +     echo -e "* Starting fsnotify stress on directory and regular file"
> > +     touch $TMPDIR/testfile
> > +     >tmp
> > +     for i in $STRESSES $IO_OPES rw_files.sh; do
> > +             for j in $STRESSES ; do
> > +                     [ "$i" == "$j" ] && continue
> > +                     # skip duplicate combinations
> > +                     grep -w $j tmp | grep -qw $i && continue
> > +                     echo -e "* Starting $i and $j, rcnt $rcnt"
> > +                     ./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                     sleep $TIMEOUT
> > +                     cleanup_processes
> > +                     echo -e "$i $j" >> tmp
> > +                     ((rcnt++))
> > +             done
> > +     done
> > +}
> > +
> > +trap "cleanup; exit;" 2
> > +
> > +echo "***** Starting tests *****"
> > +run_stress
> > +
> > +echo -e "\n***** Cleanup fanotify inotify device *****"
> > +cleanup
>
> This could be easily rewritten in the LTP C API. We do have helper sofr
> most of the functionality that is reimplemented poorly in shell here.
>
> So the whole test should really be a single C source where the testrun
> forks children that runs the stress and I/O rutines that are currently
> in the $BINARIES and the main pid would watch over the execution.
>
> --
> Cyril Hrubis
> chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-01-25 11:04 ` Cyril Hrubis
  2022-01-27  4:42   ` Murphy Zhou
@ 2022-02-28  6:29   ` Murphy Zhou
  2022-02-28  6:42     ` Petr Vorel
  1 sibling, 1 reply; 18+ messages in thread
From: Murphy Zhou @ 2022-02-28  6:29 UTC (permalink / raw)
  To: Cyril Hrubis, Petr Vorel; +Cc: LTP List

Hi all,

How to run another LTP test case fs_racer.sh or read_all in a new C
programme testcase?

I tried but hit some issues with the PATH configuration.

Is this recommended by you guys?

Thanks,
Murphy

On Tue, Jan 25, 2022 at 7:02 PM Cyril Hrubis <chrubis@suse.cz> wrote:
>
> Hi!
> > diff --git a/testcases/kernel/fs/fs-notify/Makefile b/testcases/kernel/fs/fs-notify/Makefile
> > new file mode 100644
> > index 000000000..328783942
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/Makefile
> > @@ -0,0 +1,18 @@
> > +#
> > +#    kernel/fs/fs-notify testcases Makefile.
> > +#
> > +
> > +top_srcdir                   ?= ../../../..
> > +
> > +include $(top_srcdir)/include/mk/testcases.mk
> > +
> > +BINARIES:=fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress\
> > +writefile freadfile fwritefile readfile
> > +
> > +INSTALL_TARGETS                      := fsnotify-stress.sh $(BINARIES) rw_files.sh
> > +
> > +MAKE_TARGETS                 := $(BINARIES)
> > +
> > +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > new file mode 100644
> > index 000000000..493acfb56
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_flush_stress.c
> > @@ -0,0 +1,52 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Loop marking all kinds of events and flush */
> > +     while (1) {
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                       FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                       FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                     perror("fanotify_mark add");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> > +                                             0, -1, argv[1]) == -1)
> > +                     perror("fanotify_mark flush mount");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                       FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> > +                       FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> > +                       FAN_EVENT_ON_CHILD, -1, argv[1]) == -1)
> > +
> > +                     perror("fanotify_mark add");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, argv[1]) == -1)
> > +                     perror("fanotify_mark flush");
> > +     }
> > +
> > +     close(fd);
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_init_stress.c b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > new file mode 100644
> > index 000000000..d11924b57
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_init_stress.c
> > @@ -0,0 +1,32 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <stdio.h>
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +     while (1) {
> > +
> > +             /* Create the file descriptor for accessing the fanotify API */
> > +             fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> > +                             FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> > +             if (fd == -1)
> > +                     perror("fanotify_init");
> > +
> > +             if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                             FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                             FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                             FAN_ONDIR | FAN_EVENT_ON_CHILD, -1,
> > +                             argv[1]) == -1)
> > +                     perror("fanotify_mark");
> > +
> > +             close(fd);
> > +     }
> > +
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > new file mode 100644
> > index 000000000..7f648e103
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_mark_stress.c
> > @@ -0,0 +1,58 @@
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +void add_mark(int fd, uint64_t mask, char *path)
> > +{
> > +     if (fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path) == -1)
> > +             perror("fanotify_mark add");
> > +}
> > +
> > +void remove_mark(int fd, uint64_t mask, char *path)
> > +{
> > +     if (fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path) == -1)
> > +             perror("fanotify_mark remove");
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd;
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Loop marking all kinds of events */
> > +     while (1) {
> > +             add_mark(fd, FAN_ACCESS, argv[1]);
> > +             remove_mark(fd, FAN_ACCESS, argv[1]);
> > +             add_mark(fd, FAN_MODIFY, argv[1]);
> > +             remove_mark(fd, FAN_MODIFY, argv[1]);
> > +             add_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +             remove_mark(fd, FAN_OPEN_PERM, argv[1]);
> > +             add_mark(fd, FAN_CLOSE, argv[1]);
> > +             remove_mark(fd, FAN_CLOSE, argv[1]);
> > +             add_mark(fd, FAN_OPEN, argv[1]);
> > +             remove_mark(fd, FAN_OPEN, argv[1]);
> > +             add_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +             remove_mark(fd, FAN_ACCESS_PERM, argv[1]);
> > +             add_mark(fd, FAN_ONDIR, argv[1]);
> > +             remove_mark(fd, FAN_ONDIR, argv[1]);
> > +             add_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +             remove_mark(fd, FAN_EVENT_ON_CHILD, argv[1]);
> > +     }
> > +
> > +     close(fd);
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/fanotify_watch.c b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > new file mode 100644
> > index 000000000..1028e0e30
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fanotify_watch.c
> > @@ -0,0 +1,244 @@
> > +/* Example in man 7 fanotify */
> > +
> > +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +#include <limits.h>
> > +#include <poll.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <sys/fanotify.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +
> > +/* get comm for pid from /proc/pid/comm */
> > +static char * get_comm_pid(unsigned int pid)
> > +{
> > +     char * proc_name;
> > +     char * comm;
> > +     int comm_fd, cnt;
> > +
> > +     proc_name = (char *)malloc(50);
> > +     if (proc_name != NULL)
> > +             sprintf(proc_name, "/proc/%u/comm\0", pid);
> > +     else
> > +             return NULL;
> > +
> > +     comm = (char *)malloc(50);
> > +     if (comm != NULL)
> > +             memset(comm, 0, 50);
> > +     else
> > +             return NULL;
> > +
> > +     comm_fd = open(proc_name, O_RDONLY);
> > +     if (comm_fd != -1) {
> > +             cnt = read(comm_fd, comm, 50);
> > +             if (cnt != -1) {
> > +                     comm[cnt] = '\0';
> > +                     close(comm_fd);
> > +                     return comm;
> > +             }
> > +             close(comm_fd);
> > +     }
> > +
> > +     return NULL;
> > +}
> > +
> > +/* Read all available fanotify events from the file descriptor 'fd' */
> > +
> > +static void handle_events(int fd)
> > +{
> > +     const struct fanotify_event_metadata *metadata;
> > +     struct fanotify_event_metadata buf[200];
> > +     ssize_t len;
> > +     char path[PATH_MAX];
> > +     ssize_t path_len;
> > +     char procfd_path[PATH_MAX];
> > +     struct fanotify_response response;
> > +
> > +     /* Loop while events can be read from fanotify file descriptor */
> > +     for(;;) {
> > +
> > +             /* Read some events */
> > +             len = read(fd, (void *) &buf, sizeof(buf));
> > +             if (len == -1 && errno != EAGAIN) {
> > +                     perror("read");
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +
> > +             /* Check if end of available data reached */
> > +             if (len <= 0)
> > +                     break;
> > +
> > +             /* Point to the first event in the buffer */
> > +             metadata = buf;
> > +
> > +             /* Loop over all events in the buffer */
> > +             while (FAN_EVENT_OK(metadata, len)) {
> > +
> > +                     /* Check that run-time and compile-time structures match */
> > +
> > +                     if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> > +                             fprintf(stderr,
> > +                                 "Mismatch of fanotify metadata version.\n");
> > +                             exit(EXIT_FAILURE);
> > +                     }
> > +
> > +                     /* metadata->fd contains either FAN_NOFD, indicating a
> > +                        queue overflow, or a file descriptor (a nonnegative
> > +                        integer). Here, we simply ignore queue overflow. */
> > +
> > +                     if (metadata->fd >= 0) {
> > +
> > +                             /* Handle open permission event */
> > +                             if (metadata->mask & FAN_OPEN_PERM) {
> > +                                     printf("FAN_OPEN_PERM: ");
> > +
> > +                                     /* Allow file to be opened */
> > +                                     response.fd = metadata->fd;
> > +                                     response.response = FAN_ALLOW;
> > +                                     write(fd, &response,
> > +                                         sizeof(struct fanotify_response));
> > +                             }
> > +
> > +                             /* Handle access permission event */
> > +                             if (metadata->mask & FAN_ACCESS_PERM) {
> > +                                     printf("FAN_ACCESS_PERM: ");
> > +
> > +                                     /* Allow file to be accessed */
> > +                                     response.fd = metadata->fd;
> > +                                     response.response = FAN_ALLOW;
> > +                                     write(fd, &response,
> > +                                         sizeof(struct fanotify_response));
> > +                             }
> > +
> > +                             /* Handle closing of writable file event */
> > +                             if (metadata->mask & FAN_CLOSE_WRITE)
> > +                                     printf("FAN_CLOSE_WRITE: ");
> > +
> > +                             /* Handle closing of not writable file event */
> > +                             if (metadata->mask & FAN_CLOSE_NOWRITE)
> > +                                     printf("FAN_CLOSE_NOWRITE: ");
> > +
> > +                             /* Handle modify file event */
> > +                             if (metadata->mask & FAN_MODIFY)
> > +                                     printf("FAN_MODIFY: ");
> > +
> > +                             /* Handle open event */
> > +                             if (metadata->mask & FAN_OPEN)
> > +                                     printf("FAN_OPEN: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_ACCESS)
> > +                                     printf("FAN_ACCESS: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_ONDIR)
> > +                                     printf("FAN_ONDIR: ");
> > +
> > +                             /* Handle access event */
> > +                             if (metadata->mask & FAN_EVENT_ON_CHILD)
> > +                                     printf("FAN_EVENT_ON_CHILD: ");
> > +
> > +                             /* Retrieve/print the accessed file path*/
> > +                             snprintf(procfd_path, sizeof(procfd_path),
> > +                                         "/proc/self/fd/%d", metadata->fd);
> > +                             path_len = readlink(procfd_path, path,
> > +                                         sizeof(path) - 1);
> > +                             if (path_len == -1) {
> > +                                     perror("readlink");
> > +                                     exit(EXIT_FAILURE);
> > +                             }
> > +
> > +                             path[path_len] = '\0';
> > +                             printf("File %s.\t\t Comm %s", path,
> > +                                     get_comm_pid(metadata->pid));
> > +
> > +                             /* Close the file descriptor of the event */
> > +
> > +                             close(metadata->fd);
> > +                     }
> > +
> > +                     /* Advance to next event */
> > +                     metadata = FAN_EVENT_NEXT(metadata, len);
> > +             }
> > +     }
> > +}
> > +
> > +int main(int argc, char *argv[])
> > +{
> > +     char buf;
> > +     int fd, poll_num;
> > +     nfds_t nfds;
> > +     struct pollfd fds[2];
> > +     FILE *f;
> > +#if 0
> > +     /* Check mount point is supplied */
> > +     if (argc != 2) {
> > +             fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
> > +             exit(EXIT_FAILURE);
> > +     }
> > +#endif
> > +     printf("%s on %s\n", argv[0], argv[1]);
> > +     fflush(stdout);
> > +     f = freopen("fanotify.log", "w+", stdout);
> > +     if (f == NULL) {
> > +             perror("freopen");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Create the file descriptor for accessing the fanotify API */
> > +     fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> > +                                        O_RDONLY | O_LARGEFILE);
> > +     if (fd == -1) {
> > +             perror("fanotify_init");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Mark the mount for:
> > +        - permission events before opening files
> > +        - notification events after closing a write-enabled
> > +              file descriptor */
> > +     if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> > +                     FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> > +                     FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> > +                     FAN_ONDIR | FAN_EVENT_ON_CHILD,
> > +                     -1, argv[1]) == -1) {
> > +
> > +             perror("fanotify_mark");
> > +             exit(EXIT_FAILURE);
> > +     }
> > +
> > +     /* Prepare for polling */
> > +     nfds = 1;
> > +
> > +     /* Fanotify input */
> > +     fds[0].fd = fd;
> > +     fds[0].events = POLLIN;
> > +
> > +     /* This is the loop to wait for incoming events */
> > +     printf("Listening for events.\n");
> > +     while (1) {
> > +             poll_num = poll(fds, nfds, -1);
> > +             if (poll_num == -1) {
> > +                     if (errno == EINTR)     /* Interrupted by a signal */
> > +                             continue;           /* Restart poll() */
> > +
> > +                     perror("poll");         /* Unexpected error */
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +
> > +             if (poll_num > 0) {
> > +
> > +                     if (fds[0].revents & POLLIN) {
> > +
> > +                             /* Fanotify events are available */
> > +                             handle_events(fd);
> > +                     }
> > +             }
> > +     }
> > +
> > +     fclose(f);
> > +     printf("Listening for events stopped.\n");
> > +     exit(EXIT_SUCCESS);
> > +}
> > diff --git a/testcases/kernel/fs/fs-notify/freadfile.c b/testcases/kernel/fs/fs-notify/freadfile.c
> > new file mode 100644
> > index 000000000..24bf76bcd
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/freadfile.c
> > @@ -0,0 +1,27 @@
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     int fd;
> > +     char buf[BUFSIZ];
> > +     FILE *f;
> > +
> > +     memset(buf, 1, BUFSIZ);
> > +     while(1) {
> > +             f = fopen(argv[1], "r+");
> > +             if (f == NULL) {
> > +                     perror("freadfile fopen");
> > +                     exit(EXIT_FAILURE);
> > +             }
> > +             fread(buf, sizeof(char), BUFSIZ, f);
> > +             usleep(1);
> > +     }
> > +     fclose(f);
> > +     return 0;
> > +}
>
> There is no real reason to keep these as several separate files. We can
> actually put the whole test into one single C source that forks severa
> times and each child runs one of loops from these helpers.
>
> > diff --git a/testcases/kernel/fs/fs-notify/fsnotify-stress.sh b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > new file mode 100755
> > index 000000000..d6fd5482b
> > --- /dev/null
> > +++ b/testcases/kernel/fs/fs-notify/fsnotify-stress.sh
> > @@ -0,0 +1,64 @@
> > +#!/bin/bash
> > +
> > +export TIMEOUT=10
> > +
> > +STRESSES="fanotify_flush_stress fanotify_init_stress \
> > +fanotify_mark_stress fanotify_watch inotify_watch \
> > +inotify_add_rm_stress inotify_init_stress inotify_inmodify_stress"
> > +IO_OPES="writefile freadfile fwritefile readfile"
> > +
> > +NR_CPUS=$(grep -c processor /proc/cpuinfo)
> > +[ $NR_CPUS -lt 4 ] && NR_CPUS=4
> > +
> > +function cleanup_processes()
> > +{
> > +     while ps -eo pid,comm | \
> > +             grep -q -E "fanotify|inotify|rw_files|readfile|writefile" ; do
> > +             killall rw_files.sh $STRESSES $IO_OPES > /dev/null 2>&1
> > +             sleep 1
> > +     done
> > +}
> > +
> > +function cleanup()
> > +{
> > +     sleep 3 # waiting possible unfinished processes
> > +     cleanup_processes
> > +     rm -f $STRESSES $IO_OPES fanotify.log inotify.log tmp
> > +     cd /
> > +     sync
> > +}
> > +
> > +function run_stress()
> > +{
> > +     local i j rcnt=0
> > +     echo -e "* CPU count: $NR_CPUS"
> > +     echo -e "* TIMEOUT for each subcase: $TIMEOUT"
> > +
> > +     echo -e "* Starting fsnotify stress on directory and regular file"
> > +     touch $TMPDIR/testfile
> > +     >tmp
> > +     for i in $STRESSES $IO_OPES rw_files.sh; do
> > +             for j in $STRESSES ; do
> > +                     [ "$i" == "$j" ] && continue
> > +                     # skip duplicate combinations
> > +                     grep -w $j tmp | grep -qw $i && continue
> > +                     echo -e "* Starting $i and $j, rcnt $rcnt"
> > +                     ./$i $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$i $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$j $TMPDIR $TIMEOUT > /dev/null 2>&1 &
> > +                     ./$j $TMPDIR/testfile $TIMEOUT > /dev/null 2>&1 &
> > +                     sleep $TIMEOUT
> > +                     cleanup_processes
> > +                     echo -e "$i $j" >> tmp
> > +                     ((rcnt++))
> > +             done
> > +     done
> > +}
> > +
> > +trap "cleanup; exit;" 2
> > +
> > +echo "***** Starting tests *****"
> > +run_stress
> > +
> > +echo -e "\n***** Cleanup fanotify inotify device *****"
> > +cleanup
>
> This could be easily rewritten in the LTP C API. We do have helper sofr
> most of the functionality that is reimplemented poorly in shell here.
>
> So the whole test should really be a single C source where the testrun
> forks children that runs the stress and I/O rutines that are currently
> in the $BINARIES and the main pid would watch over the execution.
>
> --
> Cyril Hrubis
> chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests
  2022-02-28  6:29   ` Murphy Zhou
@ 2022-02-28  6:42     ` Petr Vorel
  2022-03-03  3:06       ` [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test Murphy Zhou
  0 siblings, 1 reply; 18+ messages in thread
From: Petr Vorel @ 2022-02-28  6:42 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: LTP List

Hi Murphy,

> Hi all,

> How to run another LTP test case fs_racer.sh or read_all in a new C
> programme testcase?
Both read_all and fs_racer.sh are in runtest/fs.

You can run them manually with corrctly set PATH [1] (best is to fully install
LTP and add $LTPROOT/testcases/bin:$LTPROOT/bin (where LTPROOT is prefix for
installed LTP, i.e. /opt/ltp [2]).

Or you just run whole runtest/fs with the old runltp.

Note, we're working hard on new runner, but not finished yet.

Kind regards,
Petr

> I tried but hit some issues with the PATH configuration.

> Is this recommended by you guys?

> Thanks,
> Murphy

[1] https://github.com/linux-test-project/ltp#shortcut-to-running-a-single-test
[2] https://github.com/linux-test-project/ltp/wiki/User-Guidelines

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-02-28  6:42     ` Petr Vorel
@ 2022-03-03  3:06       ` Murphy Zhou
  2022-03-03 15:22         ` Petr Vorel
  0 siblings, 1 reply; 18+ messages in thread
From: Murphy Zhou @ 2022-03-03  3:06 UTC (permalink / raw)
  To: Petr Vorel; +Cc: LTP List

This is a stress tests that exercises fanotify and inotify interfaces
while IO going on. It ignores some failures of syscalls to let the
stress go on. If the kernel does not panic or hang after a certain
period of time of testing, test pass.

Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
---
v2:
	Merge all into one C programme.
	Changed dirname of the testcase.
	Run fs_racer.sh from this.

 runtest/fs                                    |   2 +
 testcases/kernel/fs/fsnotify-stress/Makefile  |  13 +
 .../fs/fsnotify-stress/fsnotify-stress.c      | 671 ++++++++++++++++++
 3 files changed, 686 insertions(+)
 create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
 create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c

diff --git a/runtest/fs b/runtest/fs
index 1d753e0dd..beb43aae4 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
 binfmt_misc02 binfmt_misc02.sh
 
 squashfs01 squashfs01
+
+fsnotify-stress fsnotify-stress
diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
new file mode 100644
index 000000000..cf4a28e42
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/Makefile
@@ -0,0 +1,13 @@
+#
+#    kernel/fs/fs-notify testcases Makefile.
+#
+
+top_srcdir	?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+INSTALL_TARGETS	:= fsnotify-stress
+
+MAKE_TARGETS	:= fsnotify-stress
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
new file mode 100644
index 000000000..229b10c17
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
@@ -0,0 +1,671 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This is an irregular stress test for Linux kernel fanotify/inotify
+ * interfaces. It calls thoese interfaces with possible best coverage
+ * arguments, in a loop. It ignores some return values in the loop to
+ * let the stress going on. At the same time, it initiates IO traffics
+ * by calling IO functions and fs_racer.sh in LTP.
+ *
+ * If kernel does no panic or hang after the test, test pass.
+ *
+ * It detected a leak in fsnotify code which was fixed by Amir through
+ * this Linux commit:
+ *     4396a731 fsnotify: fix sb_connectors leak
+ *
+ * Author: Murphy Zhou <jencce.kernel@gmail.com>
+ *
+ */
+
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/fanotify.h>
+#include <sys/inotify.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tst_test.h"
+#include "../../syscalls/fanotify/fanotify.h"
+#include "../../syscalls/inotify/inotify.h"
+
+static int fd0;
+static char *str_timeout;
+static int arg_timeout = 60;
+
+#define TESTDIR "testdir"
+#define TESTFILE "testdir/file"
+
+static void cleanup(void)
+{
+	if (fd0 > 0) {
+		SAFE_CLOSE(fd0);
+		SAFE_UNLINK(TESTFILE);
+	}
+	SAFE_RMDIR(TESTDIR);
+}
+
+static void setup(void)
+{
+	if (tst_parse_int(str_timeout, &arg_timeout, 1, INT_MAX))
+		tst_brk(TBROK, "Invalid timeout '%s'", str_timeout);
+	SAFE_MKDIR(TESTDIR, 0777);
+	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
+}
+
+static void fanotify_flushes(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* Loop marking all kinds of events and flush */
+	while (1) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
+						0, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, fn);
+
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+static void fanotify_inits(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
+				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, fn);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	exit(EXIT_SUCCESS);
+}
+
+static void add_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path);
+}
+
+static void remove_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path);
+}
+
+static void fanotify_marks(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+	/* Loop marking all kinds of events */
+	while (1) {
+		add_mark(fd, FAN_ACCESS, fn);
+		remove_mark(fd, FAN_ACCESS, fn);
+		add_mark(fd, FAN_MODIFY, fn);
+		remove_mark(fd, FAN_MODIFY, fn);
+		add_mark(fd, FAN_OPEN_PERM, fn);
+		remove_mark(fd, FAN_OPEN_PERM, fn);
+		add_mark(fd, FAN_CLOSE, fn);
+		remove_mark(fd, FAN_CLOSE, fn);
+		add_mark(fd, FAN_OPEN, fn);
+		remove_mark(fd, FAN_OPEN, fn);
+		add_mark(fd, FAN_ACCESS_PERM, fn);
+		remove_mark(fd, FAN_ACCESS_PERM, fn);
+		add_mark(fd, FAN_ONDIR, fn);
+		remove_mark(fd, FAN_ONDIR, fn);
+		add_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		remove_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+/* Read all available fanotify events from the file descriptor 'fd' */
+static void fa_handle_events(int fd)
+{
+	const struct fanotify_event_metadata *metadata;
+	struct fanotify_event_metadata buf[200];
+	ssize_t len;
+	struct fanotify_response response;
+
+	/* Loop while events can be read from fanotify file descriptor */
+	for (;;) {
+		/* Read some events */
+		len = read(fd, (void *) &buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "fanotify read events failed");
+		/* Check if end of available data reached */
+		if (len <= 0)
+			break;
+		/* Point to the first event in the buffer */
+		metadata = buf;
+		/* Loop over all events in the buffer */
+		while (FAN_EVENT_OK(metadata, len)) {
+			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+				tst_brk(TBROK | TERRNO,
+				"Mismatch of fanotify metadata version.\n");
+			}
+			/* metadata->fd contains either FAN_NOFD, indicating a
+			 * queue overflow, or a file descriptor (a nonnegative
+			 * integer). Here, we simply ignore queue overflow.
+			 */
+			if (metadata->fd >= 0) {
+				/* Handle open permission event */
+				if (metadata->mask & FAN_OPEN_PERM) {
+					/* Allow file to be opened */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle access permission event */
+				if (metadata->mask & FAN_ACCESS_PERM) {
+					/* Allow file to be accessed */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+				/* Ignore read/write access events */
+				/* Close the file descriptor of the event */
+				close(metadata->fd);
+			}
+			/* Advance to next event */
+			metadata = FAN_EVENT_NEXT(metadata, len);
+		}
+	}
+}
+
+/* This is from fanotify(7) man page example */
+static void fanotify_watch(char *fn, unsigned int timeout)
+{
+	int fd, poll_num, ret, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	/* Mark the mount for:
+	 * - permission events before opening files
+	 * - notification events after closing a write-enabled file descriptor
+	 */
+	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+			FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, "/");
+
+	/* Prepare for polling */
+	nfds = 1;
+	/* Fanotify input */
+	fds[0].fd = fd;
+	fds[0].events = POLLIN;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* This is the loop to wait for incoming events */
+	while (1) {
+		poll_num = poll(fds, nfds, timeout/2);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "fanotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[0].revents & POLLIN) {
+				/* Fanotify events are available */
+				fa_handle_events(fd);
+				ecnt++;
+			}
+		}
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	tst_printf("Got %d fanotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+static void freadfiles(char *fn, unsigned int timeout)
+{
+	int ret;
+	char buf[BUFSIZ];
+	FILE *f;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		f = fopen(fn, "r+");
+		if (f == NULL) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = fread(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void fwritefiles(char *fn, unsigned int timeout)
+{
+	int ret;
+	char buf[BUFSIZ];
+	FILE *f;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		f = fopen(fn, "w+");
+		if (f == NULL) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		fwrite(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void readfiles(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	char buf[BUFSIZ];
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(fn, O_RDONLY);
+		if (fd == -1) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = read(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void writefiles(char *fn, unsigned int timeout)
+{
+	int ret, fd;
+	char buf[BUFSIZ];
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(fn, O_RDWR);
+		if (fd == -1) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = write(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void inotify_add_rm(char *fn, unsigned int timeout)
+{
+	int notify_fd;
+	int wd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	notify_fd = SAFE_MYINOTIFY_INIT1(IN_CLOEXEC);
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		wd = inotify_add_watch(notify_fd, fn,
+			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
+			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
+			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
+			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+
+		inotify_rm_watch(notify_fd, wd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(notify_fd);
+}
+
+static void inotify_inits(char *fn, unsigned int timeout)
+{
+	int notify_fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		notify_fd = inotify_init1(IN_CLOEXEC);
+		close(notify_fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void inotify_add_rm_watches(char *fn, unsigned int timeout)
+{
+	int ret, fd, wd;
+	struct timeval ts;
+	struct timeval te;
+
+	fd = SAFE_MYINOTIFY_INIT();
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		wd = inotify_add_watch(fd, fn, IN_MODIFY);
+		inotify_rm_watch(fd, wd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(fd);
+}
+
+static void i_handle_events(int fd)
+{
+	char buf[4096]
+		__attribute__((aligned(__alignof__(struct inotify_event))));
+	ssize_t len;
+
+	/* Loop while events can be read from inotify file descriptor. */
+	for (;;) {
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "inotify read event failed");
+		/* If the nonblocking read() found no events to read, then
+		 * it returns -1 with errno set to EAGAIN. In that case,
+		 * we exit the loop.
+		 */
+		if (len <= 0)
+			break;
+	}
+}
+
+static void inotify_watch(char *fn, unsigned int timeout)
+{
+	int fd, poll_num, wd, ret, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the inotify API. */
+	fd = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK);
+
+	/* Mark directories for events
+	 * - file was opened
+	 * - file was closed
+	 */
+	wd = SAFE_MYINOTIFY_ADD_WATCH(fd, fn, IN_OPEN | IN_CLOSE);
+
+	/* Prepare for polling. */
+	nfds = 2;
+	fds[0].fd = STDIN_FILENO;       /* Console input */
+	fds[0].events = POLLIN;
+	fds[1].fd = fd;                 /* Inotify input */
+	fds[1].events = POLLIN;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* Wait for events and/or terminal input. */
+	while (1) {
+		poll_num = poll(fds, nfds, timeout/2);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "inotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[1].revents & POLLIN) {
+				/* Inotify events are available. */
+				i_handle_events(fd);
+				ecnt++;
+			}
+		}
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+
+	inotify_rm_watch(fd, wd);
+	close(fd);
+	tst_printf("Got %d inotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+struct tcase {
+	char *desc;
+	void (*func_test)(char *fn, unsigned int timeout);
+	int ondir;  /* run stress on directory */
+	int onfile;  /* run stress on file */
+};
+static struct tcase tcases[] = {
+	{"fanotify_flush stress", fanotify_flushes, 1, 1},
+	{"fanotify_init stress", fanotify_inits, 1, 1},
+	{"fanotify_mark stress", fanotify_marks, 1, 1},
+	{"fanotify watching stress", fanotify_watch, 1, 0},
+	{"fread stress", freadfiles, 0, 1},
+	{"fwrite stress", fwritefiles, 0, 1},
+	{"inotify add rm stress", inotify_add_rm, 1, 1},
+	{"inotify init stress", inotify_inits, 1, 1},
+	{"inotify add rm watch stress", inotify_add_rm_watches, 1, 1},
+	{"inotify watching stress", inotify_watch, 1, 0},
+	{"read stress", readfiles, 0, 1},
+	{"write stress", writefiles, 0, 1}
+};
+
+static void run(void)
+{
+	int tcnt = ARRAY_SIZE(tcases);
+	int i = 0;
+	const struct tst_clone_args args = {
+		.exit_signal = SIGCHLD,
+	};
+
+	setup();
+	tst_printf("Starting %d stresses\n", tcnt+1);
+	tst_flush();
+
+	while (i < tcnt) {
+		if (tcases[i].ondir && !SAFE_CLONE(&args)) {
+			tst_printf("Starting %s on dir\n", tcases[i].desc);
+			tst_flush();
+			tcases[i].func_test(TESTDIR, arg_timeout);
+			tst_printf("Ending %s on dir\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		if (tcases[i].onfile && !SAFE_CLONE(&args)) {
+			tst_printf("Starting %s on file\n", tcases[i].desc);
+			tst_flush();
+			tcases[i].func_test(TESTFILE, arg_timeout);
+			tst_printf("Ending %s on file\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		i++;
+	}
+	if (!SAFE_CLONE(&args)) {
+		char *path = getenv("PATH");
+		char *str = SAFE_MALLOC(4096);
+		char *sto = SAFE_MALLOC(1024);
+
+		snprintf(str, 4096, "PATH=%s fs_racer.sh", path);
+		snprintf(sto, 1024, "%d", arg_timeout);
+
+		const char *const cmd[] = {str, "-t", sto, NULL};
+
+		tst_printf("Starting %s -t %d\n", str, arg_timeout);
+		tst_flush();
+		tst_cmd(cmd, NULL, NULL, 0);
+		free(str);
+		free(sto);
+		exit(EXIT_SUCCESS);
+	}
+	tst_reap_children();
+	cleanup();
+	tst_res(TPASS, "No panic or hang, test pass!");
+}
+
+static struct tst_test test = {
+	.tcnt = 1,
+	.forks_child = 1,
+	.needs_root = 1,
+	.needs_tmpdir = 1,
+	.needs_cmds = 0,
+	.needs_cgroup_ver = 0,
+	.needs_cgroup_ctrls = 0,
+	.test = run,
+	.options = (struct tst_option[]) {
+		{"t:", &str_timeout, "Seconds for tiemout (default 60)"},
+		{}
+	},
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "4396a731 "},
+		{},
+	},
+};
-- 
2.27.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-03  3:06       ` [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test Murphy Zhou
@ 2022-03-03 15:22         ` Petr Vorel
  2022-03-04  3:28           ` Murphy Zhou
  2022-03-08  1:38           ` [LTP] [PATCH v3] " Murphy Zhou
  0 siblings, 2 replies; 18+ messages in thread
From: Petr Vorel @ 2022-03-03 15:22 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: LTP List

> This is a stress tests that exercises fanotify and inotify interfaces
> while IO going on. It ignores some failures of syscalls to let the
> stress go on. If the kernel does not panic or hang after a certain
> period of time of testing, test pass.

> Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> ---
> v2:
> 	Merge all into one C programme.
> 	Changed dirname of the testcase.
> 	Run fs_racer.sh from this.
Also tests in testcases/kernel/fs/racer/ are very old scripts with questionable
code quality. I would not use them with new tests.  Not sure whether these tests
are useful at all, but if yes they please take the idea and implement it in new
C API, so that they can be called by forked thread.

Why C? Running shell test from C is probably not acceptable. We have enough
problems with shell tests which use C binaries. Doing it vice versa can bring
even more problems.

fs_racer.sh runs many times fs_racer_file_create.sh, which mainly does:
dd if=/dev/zero of=$DIR/$file bs=1k count=$SIZE

=> nothing hard to implement in C.

Kind regards,
Petr

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-03 15:22         ` Petr Vorel
@ 2022-03-04  3:28           ` Murphy Zhou
  2022-03-08  1:38           ` [LTP] [PATCH v3] " Murphy Zhou
  1 sibling, 0 replies; 18+ messages in thread
From: Murphy Zhou @ 2022-03-04  3:28 UTC (permalink / raw)
  To: Petr Vorel; +Cc: LTP List

Hi Petr,

On Thu, Mar 3, 2022 at 11:22 PM Petr Vorel <pvorel@suse.cz> wrote:
>
> > This is a stress tests that exercises fanotify and inotify interfaces
> > while IO going on. It ignores some failures of syscalls to let the
> > stress go on. If the kernel does not panic or hang after a certain
> > period of time of testing, test pass.
>
> > Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> > ---
> > v2:
> >       Merge all into one C programme.
> >       Changed dirname of the testcase.
> >       Run fs_racer.sh from this.
> Also tests in testcases/kernel/fs/racer/ are very old scripts with questionable
> code quality. I would not use them with new tests.  Not sure whether these tests
> are useful at all, but if yes they please take the idea and implement it in new
> C API, so that they can be called by forked thread.

Fair enough. I'll test to see if this part is necessary.

Thanks!

>
> Why C? Running shell test from C is probably not acceptable. We have enough
> problems with shell tests which use C binaries. Doing it vice versa can bring
> even more problems.
>
> fs_racer.sh runs many times fs_racer_file_create.sh, which mainly does:
> dd if=/dev/zero of=$DIR/$file bs=1k count=$SIZE
>
> => nothing hard to implement in C.
>
> Kind regards,
> Petr

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [LTP] [PATCH v3] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-03 15:22         ` Petr Vorel
  2022-03-04  3:28           ` Murphy Zhou
@ 2022-03-08  1:38           ` Murphy Zhou
  2022-03-09 10:38             ` Cyril Hrubis
  1 sibling, 1 reply; 18+ messages in thread
From: Murphy Zhou @ 2022-03-08  1:38 UTC (permalink / raw)
  To: Petr Vorel; +Cc: LTP List

This is a stress tests that exercises fanotify and inotify interfaces
while IO going on. It ignores some failures of syscalls to let the
stress go on. If the kernel does not panic or hang after a certain
period of time of testing, test pass.

Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
---
v3:
	remove fs_racer.sh part.

 runtest/fs                                    |   2 +
 testcases/kernel/fs/fsnotify-stress/Makefile  |  13 +
 .../fs/fsnotify-stress/fsnotify-stress.c      | 654 ++++++++++++++++++
 3 files changed, 669 insertions(+)
 create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
 create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c

diff --git a/runtest/fs b/runtest/fs
index 1d753e0dd..beb43aae4 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
 binfmt_misc02 binfmt_misc02.sh
 
 squashfs01 squashfs01
+
+fsnotify-stress fsnotify-stress
diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
new file mode 100644
index 000000000..cf4a28e42
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/Makefile
@@ -0,0 +1,13 @@
+#
+#    kernel/fs/fs-notify testcases Makefile.
+#
+
+top_srcdir	?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+INSTALL_TARGETS	:= fsnotify-stress
+
+MAKE_TARGETS	:= fsnotify-stress
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
new file mode 100644
index 000000000..8297cad25
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This is an irregular stress test for Linux kernel fanotify/inotify
+ * interfaces. It calls thoese interfaces with possible best coverage
+ * arguments, in a loop. It ignores some return values in the loop to
+ * let the stress going on. At the same time, it initiates IO traffics
+ * by calling IO syscalls.
+ *
+ * If kernel does no panic or hang after the test, test pass.
+ *
+ * It detected a leak in fsnotify code which was fixed by Amir through
+ * this Linux commit:
+ *     4396a731 fsnotify: fix sb_connectors leak
+ *
+ * Author: Murphy Zhou <jencce.kernel@gmail.com>
+ *
+ */
+
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/fanotify.h>
+#include <sys/inotify.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tst_test.h"
+#include "../../syscalls/fanotify/fanotify.h"
+#include "../../syscalls/inotify/inotify.h"
+
+static int fd0;
+static char *str_timeout;
+static int arg_timeout = 60;
+
+#define TESTDIR "testdir"
+#define TESTFILE "testdir/file"
+
+static void cleanup(void)
+{
+	if (fd0 > 0) {
+		SAFE_CLOSE(fd0);
+		SAFE_UNLINK(TESTFILE);
+	}
+	SAFE_RMDIR(TESTDIR);
+}
+
+static void setup(void)
+{
+	if (tst_parse_int(str_timeout, &arg_timeout, 1, INT_MAX))
+		tst_brk(TBROK, "Invalid timeout '%s'", str_timeout);
+	SAFE_MKDIR(TESTDIR, 0777);
+	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
+}
+
+static void fanotify_flushes(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* Loop marking all kinds of events and flush */
+	while (1) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
+						0, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, fn);
+
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+static void fanotify_inits(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
+				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, fn);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	exit(EXIT_SUCCESS);
+}
+
+static void add_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path);
+}
+
+static void remove_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path);
+}
+
+static void fanotify_marks(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+	/* Loop marking all kinds of events */
+	while (1) {
+		add_mark(fd, FAN_ACCESS, fn);
+		remove_mark(fd, FAN_ACCESS, fn);
+		add_mark(fd, FAN_MODIFY, fn);
+		remove_mark(fd, FAN_MODIFY, fn);
+		add_mark(fd, FAN_OPEN_PERM, fn);
+		remove_mark(fd, FAN_OPEN_PERM, fn);
+		add_mark(fd, FAN_CLOSE, fn);
+		remove_mark(fd, FAN_CLOSE, fn);
+		add_mark(fd, FAN_OPEN, fn);
+		remove_mark(fd, FAN_OPEN, fn);
+		add_mark(fd, FAN_ACCESS_PERM, fn);
+		remove_mark(fd, FAN_ACCESS_PERM, fn);
+		add_mark(fd, FAN_ONDIR, fn);
+		remove_mark(fd, FAN_ONDIR, fn);
+		add_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		remove_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+/* Read all available fanotify events from the file descriptor 'fd' */
+static void fa_handle_events(int fd)
+{
+	const struct fanotify_event_metadata *metadata;
+	struct fanotify_event_metadata buf[200];
+	ssize_t len;
+	struct fanotify_response response;
+
+	/* Loop while events can be read from fanotify file descriptor */
+	for (;;) {
+		/* Read some events */
+		len = read(fd, (void *) &buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "fanotify read events failed");
+		/* Check if end of available data reached */
+		if (len <= 0)
+			break;
+		/* Point to the first event in the buffer */
+		metadata = buf;
+		/* Loop over all events in the buffer */
+		while (FAN_EVENT_OK(metadata, len)) {
+			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+				tst_brk(TBROK | TERRNO,
+				"Mismatch of fanotify metadata version.\n");
+			}
+			/* metadata->fd contains either FAN_NOFD, indicating a
+			 * queue overflow, or a file descriptor (a nonnegative
+			 * integer). Here, we simply ignore queue overflow.
+			 */
+			if (metadata->fd >= 0) {
+				/* Handle open permission event */
+				if (metadata->mask & FAN_OPEN_PERM) {
+					/* Allow file to be opened */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle access permission event */
+				if (metadata->mask & FAN_ACCESS_PERM) {
+					/* Allow file to be accessed */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+				/* Ignore read/write access events */
+				/* Close the file descriptor of the event */
+				close(metadata->fd);
+			}
+			/* Advance to next event */
+			metadata = FAN_EVENT_NEXT(metadata, len);
+		}
+	}
+}
+
+/* This is from fanotify(7) man page example */
+static void fanotify_watch(char *fn, unsigned int timeout)
+{
+	int fd, poll_num, ret, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the fanotify API */
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	/* Mark the mount for:
+	 * - permission events before opening files
+	 * - notification events after closing a write-enabled file descriptor
+	 */
+	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+			FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, "/");
+
+	/* Prepare for polling */
+	nfds = 1;
+	/* Fanotify input */
+	fds[0].fd = fd;
+	fds[0].events = POLLIN;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* This is the loop to wait for incoming events */
+	while (1) {
+		poll_num = poll(fds, nfds, timeout/2);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "fanotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[0].revents & POLLIN) {
+				/* Fanotify events are available */
+				fa_handle_events(fd);
+				ecnt++;
+			}
+		}
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	tst_printf("Got %d fanotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+static void freadfiles(char *fn, unsigned int timeout)
+{
+	int ret;
+	char buf[BUFSIZ];
+	FILE *f;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		f = fopen(fn, "r+");
+		if (f == NULL) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = fread(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void fwritefiles(char *fn, unsigned int timeout)
+{
+	int ret;
+	char buf[BUFSIZ];
+	FILE *f;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		f = fopen(fn, "w+");
+		if (f == NULL) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		fwrite(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void readfiles(char *fn, unsigned int timeout)
+{
+	int fd, ret;
+	char buf[BUFSIZ];
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(fn, O_RDONLY);
+		if (fd == -1) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = read(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void writefiles(char *fn, unsigned int timeout)
+{
+	int ret, fd;
+	char buf[BUFSIZ];
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	memset(buf, 1, BUFSIZ);
+	while (1) {
+		fd = open(fn, O_RDWR);
+		if (fd == -1) {
+			ret = gettimeofday(&te, NULL);
+			if (ret == -1)
+				tst_brk(TBROK | TERRNO, "gettimeofday failed");
+			if (te.tv_sec - ts.tv_sec > timeout)
+				break;
+			continue;
+		}
+		ret = write(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void inotify_add_rm(char *fn, unsigned int timeout)
+{
+	int notify_fd;
+	int wd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	notify_fd = SAFE_MYINOTIFY_INIT1(IN_CLOEXEC);
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		wd = inotify_add_watch(notify_fd, fn,
+			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
+			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
+			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
+			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+
+		inotify_rm_watch(notify_fd, wd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(notify_fd);
+}
+
+static void inotify_inits(char *fn, unsigned int timeout)
+{
+	int notify_fd, ret;
+	struct timeval ts;
+	struct timeval te;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		notify_fd = inotify_init1(IN_CLOEXEC);
+		close(notify_fd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+}
+
+static void inotify_add_rm_watches(char *fn, unsigned int timeout)
+{
+	int ret, fd, wd;
+	struct timeval ts;
+	struct timeval te;
+
+	fd = SAFE_MYINOTIFY_INIT();
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	while (1) {
+		wd = inotify_add_watch(fd, fn, IN_MODIFY);
+		inotify_rm_watch(fd, wd);
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+	close(fd);
+}
+
+static void i_handle_events(int fd)
+{
+	char buf[4096]
+		__attribute__((aligned(__alignof__(struct inotify_event))));
+	ssize_t len;
+
+	/* Loop while events can be read from inotify file descriptor. */
+	for (;;) {
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "inotify read event failed");
+		/* If the nonblocking read() found no events to read, then
+		 * it returns -1 with errno set to EAGAIN. In that case,
+		 * we exit the loop.
+		 */
+		if (len <= 0)
+			break;
+	}
+}
+
+static void inotify_watch(char *fn, unsigned int timeout)
+{
+	int fd, poll_num, wd, ret, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+	struct timeval ts;
+	struct timeval te;
+
+	/* Create the file descriptor for accessing the inotify API. */
+	fd = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK);
+
+	/* Mark directories for events
+	 * - file was opened
+	 * - file was closed
+	 */
+	wd = SAFE_MYINOTIFY_ADD_WATCH(fd, fn, IN_OPEN | IN_CLOSE);
+
+	/* Prepare for polling. */
+	nfds = 2;
+	fds[0].fd = STDIN_FILENO;       /* Console input */
+	fds[0].events = POLLIN;
+	fds[1].fd = fd;                 /* Inotify input */
+	fds[1].events = POLLIN;
+
+	ret = gettimeofday(&ts, NULL);
+	if (ret == -1)
+		tst_brk(TBROK | TERRNO, "gettimeofday failed");
+
+	/* Wait for events and/or terminal input. */
+	while (1) {
+		poll_num = poll(fds, nfds, timeout/2);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "inotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[1].revents & POLLIN) {
+				/* Inotify events are available. */
+				i_handle_events(fd);
+				ecnt++;
+			}
+		}
+		ret = gettimeofday(&te, NULL);
+		if (ret == -1)
+			tst_brk(TBROK | TERRNO, "gettimeofday failed");
+		if (te.tv_sec - ts.tv_sec > timeout)
+			break;
+	}
+
+	inotify_rm_watch(fd, wd);
+	close(fd);
+	tst_printf("Got %d inotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+struct tcase {
+	char *desc;
+	void (*func_test)(char *fn, unsigned int timeout);
+	int ondir;  /* run stress on directory */
+	int onfile;  /* run stress on file */
+};
+static struct tcase tcases[] = {
+	{"fanotify_flush stress", fanotify_flushes, 1, 1},
+	{"fanotify_init stress", fanotify_inits, 1, 1},
+	{"fanotify_mark stress", fanotify_marks, 1, 1},
+	{"fanotify watching stress", fanotify_watch, 1, 0},
+	{"fread stress", freadfiles, 0, 1},
+	{"fwrite stress", fwritefiles, 0, 1},
+	{"inotify add rm stress", inotify_add_rm, 1, 1},
+	{"inotify init stress", inotify_inits, 1, 1},
+	{"inotify add rm watch stress", inotify_add_rm_watches, 1, 1},
+	{"inotify watching stress", inotify_watch, 1, 0},
+	{"read stress", readfiles, 0, 1},
+	{"write stress", writefiles, 0, 1}
+};
+
+static void run(void)
+{
+	int tcnt = ARRAY_SIZE(tcases);
+	int i = 0;
+	const struct tst_clone_args args = {
+		.exit_signal = SIGCHLD,
+	};
+
+	setup();
+	tst_printf("Starting %d stresses\n", tcnt);
+	tst_flush();
+
+	while (i < tcnt) {
+		if (tcases[i].ondir && !SAFE_CLONE(&args)) {
+			tst_printf("Starting %s on dir\n", tcases[i].desc);
+			tst_flush();
+			tcases[i].func_test(TESTDIR, arg_timeout);
+			tst_printf("Ending %s on dir\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		if (tcases[i].onfile && !SAFE_CLONE(&args)) {
+			tst_printf("Starting %s on file\n", tcases[i].desc);
+			tst_flush();
+			tcases[i].func_test(TESTFILE, arg_timeout);
+			tst_printf("Ending %s on file\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		i++;
+	}
+	tst_reap_children();
+	cleanup();
+	tst_res(TPASS, "No panic or hang, test pass!");
+}
+
+static struct tst_test test = {
+	.tcnt = 1,
+	.forks_child = 1,
+	.needs_root = 1,
+	.needs_tmpdir = 1,
+	.needs_cmds = 0,
+	.needs_cgroup_ver = 0,
+	.needs_cgroup_ctrls = 0,
+	.test = run,
+	.options = (struct tst_option[]) {
+		{"t:", &str_timeout, "Seconds for tiemout (default 60)"},
+		{}
+	},
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "4396a731 "},
+		{},
+	},
+};
-- 
2.27.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH v3] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-08  1:38           ` [LTP] [PATCH v3] " Murphy Zhou
@ 2022-03-09 10:38             ` Cyril Hrubis
  2022-03-10 20:48               ` Petr Vorel
  0 siblings, 1 reply; 18+ messages in thread
From: Cyril Hrubis @ 2022-03-09 10:38 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: LTP List

Hi!
> This is a stress tests that exercises fanotify and inotify interfaces
> while IO going on. It ignores some failures of syscalls to let the
> stress go on. If the kernel does not panic or hang after a certain
> period of time of testing, test pass.
> 
> Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> ---
> v3:
> 	remove fs_racer.sh part.
> 
>  runtest/fs                                    |   2 +
>  testcases/kernel/fs/fsnotify-stress/Makefile  |  13 +
>  .../fs/fsnotify-stress/fsnotify-stress.c      | 654 ++++++++++++++++++
>  3 files changed, 669 insertions(+)
>  create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
>  create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
> 
> diff --git a/runtest/fs b/runtest/fs
> index 1d753e0dd..beb43aae4 100644
> --- a/runtest/fs
> +++ b/runtest/fs
> @@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
>  binfmt_misc02 binfmt_misc02.sh
>  
>  squashfs01 squashfs01
> +
> +fsnotify-stress fsnotify-stress
> diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
> new file mode 100644
> index 000000000..cf4a28e42
> --- /dev/null
> +++ b/testcases/kernel/fs/fsnotify-stress/Makefile
> @@ -0,0 +1,13 @@
> +#
> +#    kernel/fs/fs-notify testcases Makefile.
> +#
> +
> +top_srcdir	?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +INSTALL_TARGETS	:= fsnotify-stress
> +
> +MAKE_TARGETS	:= fsnotify-stress

I do not think that we have to set these two variables at all, the test
source should be compiled and installed automatically.

> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
> new file mode 100644
> index 000000000..8297cad25
> --- /dev/null
> +++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
> @@ -0,0 +1,654 @@
> +// SPDX-License-Identifier: GPL-2.0

The default license for new code should be GPL-2.0-or-later, but if you
think that the code should be strictly GPL-2.0 it's your choice.

> +/*
> + * This is an irregular stress test for Linux kernel fanotify/inotify
> + * interfaces. It calls thoese interfaces with possible best coverage
> + * arguments, in a loop. It ignores some return values in the loop to
> + * let the stress going on. At the same time, it initiates IO traffics
> + * by calling IO syscalls.
> + *
> + * If kernel does no panic or hang after the test, test pass.
> + *
> + * It detected a leak in fsnotify code which was fixed by Amir through
> + * this Linux commit:
> + *     4396a731 fsnotify: fix sb_connectors leak
> + *
> + * Author: Murphy Zhou <jencce.kernel@gmail.com>
> + *
> + */

Please convert this into the docparse format. We do parse special types
of comments during LTP build to render html documentation for the
testsuite. These comments start with:

/*\
 * [Description]
 *

And are formatted in asciidoc.

> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <sys/fanotify.h>
> +#include <sys/inotify.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +#include "tst_test.h"
> +#include "../../syscalls/fanotify/fanotify.h"
> +#include "../../syscalls/inotify/inotify.h"
> +
> +static int fd0;
> +static char *str_timeout;
> +static int arg_timeout = 60;
> +
> +#define TESTDIR "testdir"
> +#define TESTFILE "testdir/file"
> +
> +static void cleanup(void)
> +{
> +	if (fd0 > 0) {
> +		SAFE_CLOSE(fd0);
> +		SAFE_UNLINK(TESTFILE);
> +	}
> +	SAFE_RMDIR(TESTDIR);

The files and directories will be removed automatically at the end of
the test, all that has to be done here is to close the file descriptor.

> +}
> +
> +static void setup(void)
> +{
> +	if (tst_parse_int(str_timeout, &arg_timeout, 1, INT_MAX))
> +		tst_brk(TBROK, "Invalid timeout '%s'", str_timeout);
> +	SAFE_MKDIR(TESTDIR, 0777);
> +	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
> +}
> +
> +static void fanotify_flushes(char *fn, unsigned int timeout)
> +{
> +	int fd, ret;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");

First of all using gettimeofday() for any time measurement is wrong, as
the system wall clock is not monotonic and may jump at any time.

Secondly we do have a system in place to run tests for a certain amount
of time already. All you have to do is:


	while (tst_timeout_remaining() > 10) {
		/* do the work here */
	}

And then set the .timeout in the tst_test structure to a some sane
default value. No need to reinvent the wheel.

> +	/* Loop marking all kinds of events and flush */

This comment comments obvious fact, please do not add comments like
these.

> +	while (1) {
> +		/* As a stress test, we ignore the return values here to
> +		 * proceed with the stress.
> +		 */
> +		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +			  FAN_EVENT_ON_CHILD, -1, fn);
> +
> +		fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
> +						0, -1, fn);
> +
> +		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +			  FAN_EVENT_ON_CHILD, -1, fn);
> +
> +		fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, fn);
> +
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +
> +	close(fd);
> +	exit(EXIT_SUCCESS);
> +}
> +
> +static void fanotify_inits(char *fn, unsigned int timeout)
> +{
> +	int fd, ret;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	while (1) {
> +		/* As a stress test, we ignore the return values here to
> +		 * proceed with the stress.
> +		 */
> +		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
> +				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
> +		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, fn);
> +		close(fd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +	exit(EXIT_SUCCESS);
> +}
> +
> +static void add_mark(int fd, uint64_t mask, char *path)
> +{
> +	fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path);
> +}
> +
> +static void remove_mark(int fd, uint64_t mask, char *path)
> +{
> +	fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path);
> +}
> +
> +static void fanotify_marks(char *fn, unsigned int timeout)
> +{
> +	int fd, ret;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +	/* Loop marking all kinds of events */
> +	while (1) {
> +		add_mark(fd, FAN_ACCESS, fn);
> +		remove_mark(fd, FAN_ACCESS, fn);
> +		add_mark(fd, FAN_MODIFY, fn);
> +		remove_mark(fd, FAN_MODIFY, fn);
> +		add_mark(fd, FAN_OPEN_PERM, fn);
> +		remove_mark(fd, FAN_OPEN_PERM, fn);
> +		add_mark(fd, FAN_CLOSE, fn);
> +		remove_mark(fd, FAN_CLOSE, fn);
> +		add_mark(fd, FAN_OPEN, fn);
> +		remove_mark(fd, FAN_OPEN, fn);
> +		add_mark(fd, FAN_ACCESS_PERM, fn);
> +		remove_mark(fd, FAN_ACCESS_PERM, fn);
> +		add_mark(fd, FAN_ONDIR, fn);
> +		remove_mark(fd, FAN_ONDIR, fn);
> +		add_mark(fd, FAN_EVENT_ON_CHILD, fn);
> +		remove_mark(fd, FAN_EVENT_ON_CHILD, fn);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +	close(fd);
> +	exit(EXIT_SUCCESS);
> +}
> +
> +/* Read all available fanotify events from the file descriptor 'fd' */
> +static void fa_handle_events(int fd)
> +{
> +	const struct fanotify_event_metadata *metadata;
> +	struct fanotify_event_metadata buf[200];
> +	ssize_t len;
> +	struct fanotify_response response;
> +
> +	/* Loop while events can be read from fanotify file descriptor */
> +	for (;;) {
> +		/* Read some events */
> +		len = read(fd, (void *) &buf, sizeof(buf));
> +		if (len == -1 && errno != EAGAIN)
> +			tst_brk(TBROK | TERRNO, "fanotify read events failed");
> +		/* Check if end of available data reached */
> +		if (len <= 0)
> +			break;
> +		/* Point to the first event in the buffer */
> +		metadata = buf;
> +		/* Loop over all events in the buffer */
> +		while (FAN_EVENT_OK(metadata, len)) {
> +			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
> +				tst_brk(TBROK | TERRNO,
> +				"Mismatch of fanotify metadata version.\n");
> +			}
> +			/* metadata->fd contains either FAN_NOFD, indicating a
> +			 * queue overflow, or a file descriptor (a nonnegative
> +			 * integer). Here, we simply ignore queue overflow.
> +			 */
> +			if (metadata->fd >= 0) {
> +				/* Handle open permission event */
> +				if (metadata->mask & FAN_OPEN_PERM) {
> +					/* Allow file to be opened */
> +					response.fd = metadata->fd;
> +					response.response = FAN_ALLOW;
> +					write(fd, &response,
> +					    sizeof(struct fanotify_response));
> +				}
> +
> +				/* Handle access permission event */
> +				if (metadata->mask & FAN_ACCESS_PERM) {
> +					/* Allow file to be accessed */
> +					response.fd = metadata->fd;
> +					response.response = FAN_ALLOW;
> +					write(fd, &response,
> +					    sizeof(struct fanotify_response));
> +				}
> +				/* Ignore read/write access events */
> +				/* Close the file descriptor of the event */
> +				close(metadata->fd);
> +			}
> +			/* Advance to next event */
> +			metadata = FAN_EVENT_NEXT(metadata, len);
> +		}
> +	}
> +}
> +
> +/* This is from fanotify(7) man page example */
> +static void fanotify_watch(char *fn, unsigned int timeout)
> +{
> +	int fd, poll_num, ret, ecnt = 0;
> +	nfds_t nfds;
> +	struct pollfd fds[2];
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	/* Create the file descriptor for accessing the fanotify API */
> +	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +	/* Mark the mount for:
> +	 * - permission events before opening files
> +	 * - notification events after closing a write-enabled file descriptor
> +	 */
> +	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
> +			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
> +			FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, "/");
> +
> +	/* Prepare for polling */
> +	nfds = 1;
> +	/* Fanotify input */
> +	fds[0].fd = fd;
> +	fds[0].events = POLLIN;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	/* This is the loop to wait for incoming events */
> +	while (1) {
> +		poll_num = poll(fds, nfds, timeout/2);
> +		if (poll_num == -1)
> +			tst_brk(TBROK | TERRNO, "fanotify watch poll failed");
> +		if (poll_num > 0) {
> +			if (fds[0].revents & POLLIN) {
> +				/* Fanotify events are available */
> +				fa_handle_events(fd);
> +				ecnt++;
> +			}
> +		}
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +	tst_printf("Got %d fanotify events\n", ecnt);
> +	tst_flush();
> +	exit(EXIT_SUCCESS);
> +}
> +
> +static void freadfiles(char *fn, unsigned int timeout)
> +{
> +	int ret;
> +	char buf[BUFSIZ];
> +	FILE *f;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	memset(buf, 1, BUFSIZ);
> +	while (1) {
> +		f = fopen(fn, "r+");
> +		if (f == NULL) {
> +			ret = gettimeofday(&te, NULL);
> +			if (ret == -1)
> +				tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +			if (te.tv_sec - ts.tv_sec > timeout)
> +				break;
> +			continue;
> +		}
> +		ret = fread(buf, sizeof(char), BUFSIZ, f);
> +		usleep(1);
> +		fclose(f);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +}
> +
> +static void fwritefiles(char *fn, unsigned int timeout)
> +{
> +	int ret;
> +	char buf[BUFSIZ];
> +	FILE *f;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	memset(buf, 1, BUFSIZ);
> +	while (1) {
> +		f = fopen(fn, "w+");
> +		if (f == NULL) {
> +			ret = gettimeofday(&te, NULL);
> +			if (ret == -1)
> +				tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +			if (te.tv_sec - ts.tv_sec > timeout)
> +				break;
> +			continue;
> +		}
> +		fwrite(buf, sizeof(char), BUFSIZ, f);
> +		usleep(1);
> +		fclose(f);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +}
> +
> +static void readfiles(char *fn, unsigned int timeout)
> +{
> +	int fd, ret;
> +	char buf[BUFSIZ];
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	memset(buf, 1, BUFSIZ);
> +	while (1) {
> +		fd = open(fn, O_RDONLY);
> +		if (fd == -1) {
> +			ret = gettimeofday(&te, NULL);
> +			if (ret == -1)
> +				tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +			if (te.tv_sec - ts.tv_sec > timeout)
> +				break;
> +			continue;
> +		}
> +		ret = read(fd, buf, BUFSIZ);
> +		usleep(1);
> +		close(fd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +}
> +
> +static void writefiles(char *fn, unsigned int timeout)
> +{
> +	int ret, fd;
> +	char buf[BUFSIZ];
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	memset(buf, 1, BUFSIZ);
> +	while (1) {
> +		fd = open(fn, O_RDWR);
> +		if (fd == -1) {
> +			ret = gettimeofday(&te, NULL);
> +			if (ret == -1)
> +				tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +			if (te.tv_sec - ts.tv_sec > timeout)
> +				break;
> +			continue;
> +		}
> +		ret = write(fd, buf, BUFSIZ);
> +		usleep(1);
> +		close(fd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +}
> +
> +static void inotify_add_rm(char *fn, unsigned int timeout)
> +{
> +	int notify_fd;
> +	int wd, ret;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	notify_fd = SAFE_MYINOTIFY_INIT1(IN_CLOEXEC);
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	while (1) {
> +		wd = inotify_add_watch(notify_fd, fn,
> +			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
> +			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
> +			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
> +			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
> +
> +		inotify_rm_watch(notify_fd, wd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +	close(notify_fd);
> +}
> +
> +static void inotify_inits(char *fn, unsigned int timeout)
> +{
> +	int notify_fd, ret;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	while (1) {
> +		notify_fd = inotify_init1(IN_CLOEXEC);
> +		close(notify_fd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +}
> +
> +static void inotify_add_rm_watches(char *fn, unsigned int timeout)
> +{
> +	int ret, fd, wd;
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	fd = SAFE_MYINOTIFY_INIT();
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	while (1) {
> +		wd = inotify_add_watch(fd, fn, IN_MODIFY);
> +		inotify_rm_watch(fd, wd);
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +	close(fd);
> +}
> +
> +static void i_handle_events(int fd)
> +{
> +	char buf[4096]
> +		__attribute__((aligned(__alignof__(struct inotify_event))));
> +	ssize_t len;
> +
> +	/* Loop while events can be read from inotify file descriptor. */
> +	for (;;) {
> +		len = read(fd, buf, sizeof(buf));
> +		if (len == -1 && errno != EAGAIN)
> +			tst_brk(TBROK | TERRNO, "inotify read event failed");
> +		/* If the nonblocking read() found no events to read, then
> +		 * it returns -1 with errno set to EAGAIN. In that case,
> +		 * we exit the loop.
> +		 */
> +		if (len <= 0)
> +			break;
> +	}
> +}
> +
> +static void inotify_watch(char *fn, unsigned int timeout)
> +{
> +	int fd, poll_num, wd, ret, ecnt = 0;
> +	nfds_t nfds;
> +	struct pollfd fds[2];
> +	struct timeval ts;
> +	struct timeval te;
> +
> +	/* Create the file descriptor for accessing the inotify API. */
> +	fd = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK);
> +
> +	/* Mark directories for events
> +	 * - file was opened
> +	 * - file was closed
> +	 */
> +	wd = SAFE_MYINOTIFY_ADD_WATCH(fd, fn, IN_OPEN | IN_CLOSE);
> +
> +	/* Prepare for polling. */
> +	nfds = 2;
> +	fds[0].fd = STDIN_FILENO;       /* Console input */
> +	fds[0].events = POLLIN;
> +	fds[1].fd = fd;                 /* Inotify input */
> +	fds[1].events = POLLIN;
> +
> +	ret = gettimeofday(&ts, NULL);
> +	if (ret == -1)
> +		tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +
> +	/* Wait for events and/or terminal input. */
> +	while (1) {
> +		poll_num = poll(fds, nfds, timeout/2);
> +		if (poll_num == -1)
> +			tst_brk(TBROK | TERRNO, "inotify watch poll failed");
> +		if (poll_num > 0) {
> +			if (fds[1].revents & POLLIN) {
> +				/* Inotify events are available. */
> +				i_handle_events(fd);
> +				ecnt++;
> +			}
> +		}
> +		ret = gettimeofday(&te, NULL);
> +		if (ret == -1)
> +			tst_brk(TBROK | TERRNO, "gettimeofday failed");
> +		if (te.tv_sec - ts.tv_sec > timeout)
> +			break;
> +	}
> +
> +	inotify_rm_watch(fd, wd);
> +	close(fd);
> +	tst_printf("Got %d inotify events\n", ecnt);
> +	tst_flush();
> +	exit(EXIT_SUCCESS);
> +}
> +
> +struct tcase {
> +	char *desc;
> +	void (*func_test)(char *fn, unsigned int timeout);
> +	int ondir;  /* run stress on directory */
> +	int onfile;  /* run stress on file */
> +};
> +static struct tcase tcases[] = {
> +	{"fanotify_flush stress", fanotify_flushes, 1, 1},
> +	{"fanotify_init stress", fanotify_inits, 1, 1},
> +	{"fanotify_mark stress", fanotify_marks, 1, 1},
> +	{"fanotify watching stress", fanotify_watch, 1, 0},
> +	{"fread stress", freadfiles, 0, 1},
> +	{"fwrite stress", fwritefiles, 0, 1},
> +	{"inotify add rm stress", inotify_add_rm, 1, 1},
> +	{"inotify init stress", inotify_inits, 1, 1},
> +	{"inotify add rm watch stress", inotify_add_rm_watches, 1, 1},
> +	{"inotify watching stress", inotify_watch, 1, 0},
> +	{"read stress", readfiles, 0, 1},
> +	{"write stress", writefiles, 0, 1}
> +};
> +
> +static void run(void)
> +{
> +	int tcnt = ARRAY_SIZE(tcases);
> +	int i = 0;
> +	const struct tst_clone_args args = {
> +		.exit_signal = SIGCHLD,
> +	};
> +
> +	setup();

The setup and cleanup has to be passed via the tst_test structure and
shouldn't be called from the run() function at all.

> +	tst_printf("Starting %d stresses\n", tcnt);

This should be tst_res(TINFO, ...);

> +	tst_flush();

The SAFE_CLONE() does flush the buffers before it calls the clone()
syscall, there is no need to flush anything in the test itself.

> +	while (i < tcnt) {
> +		if (tcases[i].ondir && !SAFE_CLONE(&args)) {
> +			tst_printf("Starting %s on dir\n", tcases[i].desc);
> +			tst_flush();

Here as well.

> +			tcases[i].func_test(TESTDIR, arg_timeout);
> +			tst_printf("Ending %s on dir\n", tcases[i].desc);
> +			tst_flush();
> +			exit(EXIT_SUCCESS);

Some of the func_test() calls exit(EXIT_SUCCESS) at the end, in these
casese these  messages will not be printed at all.

> +		}
> +		if (tcases[i].onfile && !SAFE_CLONE(&args)) {
> +			tst_printf("Starting %s on file\n", tcases[i].desc);
> +			tst_flush();

Here as well.

> +			tcases[i].func_test(TESTFILE, arg_timeout);
> +			tst_printf("Ending %s on file\n", tcases[i].desc);
> +			tst_flush();
> +			exit(EXIT_SUCCESS);

And here as well.

> +		}
> +		i++;
> +	}
> +	tst_reap_children();
> +	cleanup();
> +	tst_res(TPASS, "No panic or hang, test pass!");
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = 1,
> +	.forks_child = 1,
> +	.needs_root = 1,
> +	.needs_tmpdir = 1,
> +	.needs_cmds = 0,
> +	.needs_cgroup_ver = 0,
> +	.needs_cgroup_ctrls = 0,

Please do not set anything to 0 here, that is the default value for any
uninitialized variables. Unfortunatelly gcc is buggy and prints warnings
for nearly all tst_test initializations in the LTP tree, there is an
upstream gcc bug for that that haven't been worked on for quite some
time now. But that is not a reason to work around compiler bugs here.

> +	.test = run,
> +	.options = (struct tst_option[]) {
> +		{"t:", &str_timeout, "Seconds for tiemout (default 60)"},
> +		{}
> +	},
> +	.tags = (const struct tst_tag[]) {
> +		{"linux-git", "4396a731 "},
> +		{},
> +	},
> +};
> -- 
> 2.27.0
> 

-- 
Cyril Hrubis
chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH v3] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-09 10:38             ` Cyril Hrubis
@ 2022-03-10 20:48               ` Petr Vorel
  2022-09-02  7:58                 ` Murphy Zhou
  2022-09-02  8:17                 ` [LTP] [PATCH v4] " Murphy Zhou
  0 siblings, 2 replies; 18+ messages in thread
From: Petr Vorel @ 2022-03-10 20:48 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: ltp

Hi,

...
> > +static struct tst_test test = {
> > +	.tcnt = 1,
> > +	.forks_child = 1,
> > +	.needs_root = 1,
> > +	.needs_tmpdir = 1,
> > +	.needs_cmds = 0,
> > +	.needs_cgroup_ver = 0,
> > +	.needs_cgroup_ctrls = 0,

> Please do not set anything to 0 here, that is the default value for any
> uninitialized variables. Unfortunatelly gcc is buggy and prints warnings
> for nearly all tst_test initializations in the LTP tree, there is an
> upstream gcc bug for that that haven't been worked on for quite some
> time now. But that is not a reason to work around compiler bugs here.

FYI
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84685
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82283

Kind regards,
Petr

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [LTP] [PATCH v3] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-10 20:48               ` Petr Vorel
@ 2022-09-02  7:58                 ` Murphy Zhou
  2022-09-02  8:17                 ` [LTP] [PATCH v4] " Murphy Zhou
  1 sibling, 0 replies; 18+ messages in thread
From: Murphy Zhou @ 2022-09-02  7:58 UTC (permalink / raw)
  To: ltp

This is a stress test that exercises fanotify and inotify interfaces
while IO going on. It intentionally ignores failures or return values
of some syscalls to let the stress go on. If the kernel does not panic
or hang after a certain period of time of testing, test pass.

Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
---
v2 -> v3:
	remove unnecessary comments;
	Fix TINFO printing and no flushing;
	Use max_runtime instead of local timeout;
	Remove unnecessary initialization for struct tst_test

 runtest/fs                                    |   2 +
 testcases/kernel/fs/fsnotify-stress/Makefile  |   9 +
 .../fs/fsnotify-stress/fsnotify-stress.c      | 470 ++++++++++++++++++
 3 files changed, 481 insertions(+)
 create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
 create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c

diff --git a/runtest/fs b/runtest/fs
index 1d753e0dd..beb43aae4 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
 binfmt_misc02 binfmt_misc02.sh
 
 squashfs01 squashfs01
+
+fsnotify-stress fsnotify-stress
diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
new file mode 100644
index 000000000..451f791f1
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/Makefile
@@ -0,0 +1,9 @@
+#
+#    kernel/fs/fs-notify testcases Makefile.
+#
+
+top_srcdir	?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
new file mode 100644
index 000000000..638f6c6b6
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This is an irregular stress test for Linux kernel fanotify/inotify
+ * interfaces. It calls thoese interfaces with possible best coverage
+ * arguments, in a loop. It ignores some return values in the loop to
+ * let the stress going on. At the same time, it initiates IO traffics
+ * by calling IO syscalls.
+ *
+ * If kernel does no panic or hang after the test, test pass.
+ *
+ * It detected a leak in fsnotify code which was fixed by Amir through
+ * this Linux commit:
+ *     4396a731 fsnotify: fix sb_connectors leak
+ *
+ * Author: Murphy Zhou <jencce.kernel@gmail.com>
+ *
+ */
+
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/fanotify.h>
+#include <sys/inotify.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tst_test.h"
+#include "../../syscalls/fanotify/fanotify.h"
+#include "../../syscalls/inotify/inotify.h"
+
+static int fd0;
+
+#define TESTDIR "testdir"
+#define TESTFILE "testdir/file"
+
+static void cleanup(void)
+{
+	if (fd0 > 0) {
+		SAFE_CLOSE(fd0);
+	}
+}
+
+static void setup(void)
+{
+	SAFE_MKDIR(TESTDIR, 0777);
+	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
+}
+
+static void fanotify_flushes(char *fn)
+{
+	int fd;
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+
+	while (tst_remaining_runtime() > 10) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
+						0, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, fn);
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+static void fanotify_inits(char *fn)
+{
+	int fd;
+
+	while (tst_remaining_runtime() > 10) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
+				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, fn);
+		close(fd);
+	}
+	exit(EXIT_SUCCESS);
+}
+
+static void add_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path);
+}
+
+static void remove_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path);
+}
+
+static void fanotify_marks(char *fn)
+{
+	int fd;
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	while (tst_remaining_runtime() > 10) {
+		add_mark(fd, FAN_ACCESS, fn);
+		remove_mark(fd, FAN_ACCESS, fn);
+		add_mark(fd, FAN_MODIFY, fn);
+		remove_mark(fd, FAN_MODIFY, fn);
+		add_mark(fd, FAN_OPEN_PERM, fn);
+		remove_mark(fd, FAN_OPEN_PERM, fn);
+		add_mark(fd, FAN_CLOSE, fn);
+		remove_mark(fd, FAN_CLOSE, fn);
+		add_mark(fd, FAN_OPEN, fn);
+		remove_mark(fd, FAN_OPEN, fn);
+		add_mark(fd, FAN_ACCESS_PERM, fn);
+		remove_mark(fd, FAN_ACCESS_PERM, fn);
+		add_mark(fd, FAN_ONDIR, fn);
+		remove_mark(fd, FAN_ONDIR, fn);
+		add_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		remove_mark(fd, FAN_EVENT_ON_CHILD, fn);
+	}
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+/* Read all available fanotify events from the file descriptor 'fd' */
+static void fa_handle_events(int fd)
+{
+	const struct fanotify_event_metadata *metadata;
+	struct fanotify_event_metadata buf[200];
+	ssize_t len;
+	struct fanotify_response response;
+
+	/* Loop while events can be read from fanotify file descriptor */
+	for (;;) {
+		/* Read some events */
+		len = read(fd, (void *) &buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "fanotify read events failed");
+		/* Check if end of available data reached */
+		if (len <= 0)
+			break;
+		/* Point to the first event in the buffer */
+		metadata = buf;
+		/* Loop over all events in the buffer */
+		while (FAN_EVENT_OK(metadata, len)) {
+			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+				tst_brk(TBROK | TERRNO,
+				"Mismatch of fanotify metadata version.\n");
+			}
+			/* metadata->fd contains either FAN_NOFD, indicating a
+			 * queue overflow, or a file descriptor (a nonnegative
+			 * integer). Here, we simply ignore queue overflow.
+			 */
+			if (metadata->fd >= 0) {
+				/* Handle open permission event */
+				if (metadata->mask & FAN_OPEN_PERM) {
+					/* Allow file to be opened */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle access permission event */
+				if (metadata->mask & FAN_ACCESS_PERM) {
+					/* Allow file to be accessed */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+				/* Ignore read/write access events */
+				/* Close the file descriptor of the event */
+				close(metadata->fd);
+			}
+			/* Advance to next event */
+			metadata = FAN_EVENT_NEXT(metadata, len);
+		}
+	}
+}
+
+/* This is from fanotify(7) man page example */
+static void fanotify_watch(void)
+{
+	int fd, poll_num, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	/* Mark the mount for:
+	 * - permission events before opening files
+	 * - notification events after closing a write-enabled file descriptor
+	 */
+	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+			FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, "/");
+
+	nfds = 1;
+	fds[0].fd = fd;
+	fds[0].events = POLLIN;
+
+	while (tst_remaining_runtime() > 10) {
+		poll_num = poll(fds, nfds, 10);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "fanotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[0].revents & POLLIN) {
+				/* Fanotify events are available */
+				fa_handle_events(fd);
+				ecnt++;
+			}
+		}
+	}
+	tst_printf("Got %d fanotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+static void freadfiles(char *fn)
+{
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		f = fopen(fn, "r+");
+		if (f == NULL)
+			continue;
+		fread(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+	}
+}
+
+static void fwritefiles(char *fn)
+{
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		f = fopen(fn, "w+");
+		if (f == NULL)
+			continue;
+		fwrite(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+	}
+}
+
+static void readfiles(char *fn)
+{
+	int fd;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		fd = open(fn, O_RDONLY);
+		if (fd == -1)
+			continue;
+		read(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+	}
+}
+
+static void writefiles(char *fn)
+{
+	int fd;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		fd = open(fn, O_RDWR);
+		if (fd == -1)
+			continue;
+		write(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+	}
+}
+
+static void inotify_add_rm(char *fn)
+{
+	int notify_fd;
+	int wd;
+
+	notify_fd = SAFE_MYINOTIFY_INIT1(IN_CLOEXEC);
+
+	while (tst_remaining_runtime() > 10) {
+		wd = inotify_add_watch(notify_fd, fn,
+			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
+			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
+			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
+			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+
+		inotify_rm_watch(notify_fd, wd);
+	}
+	close(notify_fd);
+}
+
+static void inotify_inits(void)
+{
+	int notify_fd;
+
+	while (tst_remaining_runtime() > 10) {
+		notify_fd = inotify_init1(IN_CLOEXEC);
+		close(notify_fd);
+	}
+}
+
+static void inotify_add_rm_watches(char *fn)
+{
+	int fd, wd;
+
+	fd = SAFE_MYINOTIFY_INIT();
+
+	while (tst_remaining_runtime() > 10) {
+		wd = inotify_add_watch(fd, fn, IN_MODIFY);
+		inotify_rm_watch(fd, wd);
+	}
+	close(fd);
+}
+
+static void i_handle_events(int fd)
+{
+	char buf[4096]
+		__attribute__((aligned(__alignof__(struct inotify_event))));
+	ssize_t len;
+
+	/* Loop while events can be read from inotify file descriptor. */
+	for (;;) {
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "inotify read event failed");
+		/* If the nonblocking read() found no events to read, then
+		 * it returns -1 with errno set to EAGAIN. In that case,
+		 * we exit the loop.
+		 */
+		if (len <= 0)
+			break;
+	}
+}
+
+static void inotify_watch(char *fn)
+{
+	int fd, poll_num, wd, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+
+	fd = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK);
+
+	/* Mark directories for events
+	 * - file was opened
+	 * - file was closed
+	 */
+	wd = SAFE_MYINOTIFY_ADD_WATCH(fd, fn, IN_OPEN | IN_CLOSE);
+
+	nfds = 2;
+	fds[0].fd = STDIN_FILENO;       /* Console input */
+	fds[0].events = POLLIN;
+	fds[1].fd = fd;                 /* Inotify input */
+	fds[1].events = POLLIN;
+
+	/* Wait for events and/or terminal input. */
+	while (tst_remaining_runtime() > 10) {
+		poll_num = poll(fds, nfds, 10);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "inotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[1].revents & POLLIN) {
+				/* Inotify events are available. */
+				i_handle_events(fd);
+				ecnt++;
+			}
+		}
+	}
+
+	inotify_rm_watch(fd, wd);
+	close(fd);
+	tst_printf("Got %d inotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+struct tcase {
+	char *desc;
+	void (*func_test)(char *fn);
+	int ondir;  /* run stress on directory */
+	int onfile;  /* run stress on file */
+};
+static struct tcase tcases[] = {
+	{"fanotify_flush stress", fanotify_flushes, 1, 1},
+	{"fanotify_init stress", fanotify_inits, 1, 1},
+	{"fanotify_mark stress", fanotify_marks, 1, 1},
+	{"fanotify watching stress", fanotify_watch, 1, 0},
+	{"fread stress", freadfiles, 0, 1},
+	{"fwrite stress", fwritefiles, 0, 1},
+	{"inotify add rm stress", inotify_add_rm, 1, 1},
+	{"inotify init stress", inotify_inits, 1, 1},
+	{"inotify add rm watch stress", inotify_add_rm_watches, 1, 1},
+	{"inotify watching stress", inotify_watch, 1, 0},
+	{"read stress", readfiles, 0, 1},
+	{"write stress", writefiles, 0, 1}
+};
+
+static void run(void)
+{
+	int tcnt = ARRAY_SIZE(tcases);
+	int i = 0;
+	const struct tst_clone_args args = {
+		.exit_signal = SIGCHLD,
+	};
+
+	tst_res(TINFO, "Starting %d stresses\n", tcnt);
+
+	while (i < tcnt) {
+		if (tcases[i].ondir && !SAFE_CLONE(&args)) {
+			tst_res(TINFO, "Starting %s on dir\n", tcases[i].desc);
+			tcases[i].func_test(TESTDIR);
+			tst_res(TINFO, "Ending %s on dir\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		if (tcases[i].onfile && !SAFE_CLONE(&args)) {
+			tst_res(TINFO, "Starting %s on file\n", tcases[i].desc);
+			tcases[i].func_test(TESTFILE);
+			tst_res(TINFO, "Ending %s on file\n", tcases[i].desc);
+			exit(EXIT_SUCCESS);
+		}
+		i++;
+	}
+	tst_reap_children();
+	tst_res(TPASS, "No panic no hang, test PASS");
+}
+
+static struct tst_test test = {
+	.tcnt = 1,
+	.max_runtime = 60,
+	.forks_child = 1,
+	.needs_root = 1,
+	.needs_tmpdir = 1,
+	.test = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "4396a731 "},
+		{},
+	},
+};
-- 
2.31.1


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [LTP] [PATCH v4] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-03-10 20:48               ` Petr Vorel
  2022-09-02  7:58                 ` Murphy Zhou
@ 2022-09-02  8:17                 ` Murphy Zhou
  2022-10-17  9:40                   ` Richard Palethorpe
  1 sibling, 1 reply; 18+ messages in thread
From: Murphy Zhou @ 2022-09-02  8:17 UTC (permalink / raw)
  To: ltp

This is a stress test that exercises fanotify and inotify interfaces
while IO going on. It intentionally ignores failures or return values
of some syscalls to let the stress go on. If the kernel does not panic
or hang after a certain period of time of testing, test pass.

Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
---
v3 -> v4:
	Convert comment to docparse part.

 runtest/fs                                    |   2 +
 testcases/kernel/fs/fsnotify-stress/Makefile  |   9 +
 .../fs/fsnotify-stress/fsnotify-stress.c      | 476 ++++++++++++++++++
 3 files changed, 487 insertions(+)
 create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
 create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c

diff --git a/runtest/fs b/runtest/fs
index 1d753e0dd..beb43aae4 100644
--- a/runtest/fs
+++ b/runtest/fs
@@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
 binfmt_misc02 binfmt_misc02.sh
 
 squashfs01 squashfs01
+
+fsnotify-stress fsnotify-stress
diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
new file mode 100644
index 000000000..451f791f1
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/Makefile
@@ -0,0 +1,9 @@
+#
+#    kernel/fs/fs-notify testcases Makefile.
+#
+
+top_srcdir	?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
new file mode 100644
index 000000000..8130f7f12
--- /dev/null
+++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
@@ -0,0 +1,476 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
+ * Author: Murphy Zhou <jencce.kernel@gmail.com>
+ * Copyright (c) Linux Test Project, 2001-2022
+ */
+
+/*\
+ * [Description]
+ *
+ * This is an irregular stress test for Linux kernel fanotify/inotify
+ * interfaces. It calls thoese interfaces with possible best coverage
+ * arguments, in a loop. It ignores some return values in the loop to
+ * let the stress going on. At the same time, it initiates IO traffics
+ * by calling IO syscalls.
+ *
+ * If kernel does no panic or hang after the test, test pass.
+ *
+ * It detected a leak in fsnotify code which was fixed by Amir through
+ * this Linux commit:
+ *     4396a731 fsnotify: fix sb_connectors leak
+ *
+ */
+
+#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/fanotify.h>
+#include <sys/inotify.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tst_test.h"
+#include "../../syscalls/fanotify/fanotify.h"
+#include "../../syscalls/inotify/inotify.h"
+
+static int fd0;
+
+#define TESTDIR "testdir"
+#define TESTFILE "testdir/file"
+
+static void cleanup(void)
+{
+	if (fd0 > 0) {
+		SAFE_CLOSE(fd0);
+	}
+}
+
+static void setup(void)
+{
+	SAFE_MKDIR(TESTDIR, 0777);
+	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
+}
+
+static void fanotify_flushes(char *fn)
+{
+	int fd;
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+
+	while (tst_remaining_runtime() > 10) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH | FAN_MARK_MOUNT,
+						0, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
+			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
+			  FAN_EVENT_ON_CHILD, -1, fn);
+
+		fanotify_mark(fd, FAN_MARK_FLUSH, 0, -1, fn);
+	}
+
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+static void fanotify_inits(char *fn)
+{
+	int fd;
+
+	while (tst_remaining_runtime() > 10) {
+		/* As a stress test, we ignore the return values here to
+		 * proceed with the stress.
+		 */
+		fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT |
+				FAN_NONBLOCK, O_RDONLY | O_LARGEFILE);
+		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+				FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+				FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+				FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, fn);
+		close(fd);
+	}
+	exit(EXIT_SUCCESS);
+}
+
+static void add_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_ADD, mask, -1, path);
+}
+
+static void remove_mark(int fd, uint64_t mask, char *path)
+{
+	fanotify_mark(fd, FAN_MARK_REMOVE, mask, -1, path);
+}
+
+static void fanotify_marks(char *fn)
+{
+	int fd;
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	while (tst_remaining_runtime() > 10) {
+		add_mark(fd, FAN_ACCESS, fn);
+		remove_mark(fd, FAN_ACCESS, fn);
+		add_mark(fd, FAN_MODIFY, fn);
+		remove_mark(fd, FAN_MODIFY, fn);
+		add_mark(fd, FAN_OPEN_PERM, fn);
+		remove_mark(fd, FAN_OPEN_PERM, fn);
+		add_mark(fd, FAN_CLOSE, fn);
+		remove_mark(fd, FAN_CLOSE, fn);
+		add_mark(fd, FAN_OPEN, fn);
+		remove_mark(fd, FAN_OPEN, fn);
+		add_mark(fd, FAN_ACCESS_PERM, fn);
+		remove_mark(fd, FAN_ACCESS_PERM, fn);
+		add_mark(fd, FAN_ONDIR, fn);
+		remove_mark(fd, FAN_ONDIR, fn);
+		add_mark(fd, FAN_EVENT_ON_CHILD, fn);
+		remove_mark(fd, FAN_EVENT_ON_CHILD, fn);
+	}
+	close(fd);
+	exit(EXIT_SUCCESS);
+}
+
+/* Read all available fanotify events from the file descriptor 'fd' */
+static void fa_handle_events(int fd)
+{
+	const struct fanotify_event_metadata *metadata;
+	struct fanotify_event_metadata buf[200];
+	ssize_t len;
+	struct fanotify_response response;
+
+	/* Loop while events can be read from fanotify file descriptor */
+	for (;;) {
+		/* Read some events */
+		len = read(fd, (void *) &buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "fanotify read events failed");
+		/* Check if end of available data reached */
+		if (len <= 0)
+			break;
+		/* Point to the first event in the buffer */
+		metadata = buf;
+		/* Loop over all events in the buffer */
+		while (FAN_EVENT_OK(metadata, len)) {
+			if (metadata->vers != FANOTIFY_METADATA_VERSION) {
+				tst_brk(TBROK | TERRNO,
+				"Mismatch of fanotify metadata version.\n");
+			}
+			/* metadata->fd contains either FAN_NOFD, indicating a
+			 * queue overflow, or a file descriptor (a nonnegative
+			 * integer). Here, we simply ignore queue overflow.
+			 */
+			if (metadata->fd >= 0) {
+				/* Handle open permission event */
+				if (metadata->mask & FAN_OPEN_PERM) {
+					/* Allow file to be opened */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+
+				/* Handle access permission event */
+				if (metadata->mask & FAN_ACCESS_PERM) {
+					/* Allow file to be accessed */
+					response.fd = metadata->fd;
+					response.response = FAN_ALLOW;
+					write(fd, &response,
+					    sizeof(struct fanotify_response));
+				}
+				/* Ignore read/write access events */
+				/* Close the file descriptor of the event */
+				close(metadata->fd);
+			}
+			/* Advance to next event */
+			metadata = FAN_EVENT_NEXT(metadata, len);
+		}
+	}
+}
+
+/* This is from fanotify(7) man page example */
+static void fanotify_watch(void)
+{
+	int fd, poll_num, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+
+	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
+					   O_RDONLY | O_LARGEFILE);
+	/* Mark the mount for:
+	 * - permission events before opening files
+	 * - notification events after closing a write-enabled file descriptor
+	 */
+	SAFE_FANOTIFY_MARK(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+			FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM |
+			FAN_CLOSE | FAN_OPEN | FAN_ACCESS_PERM |
+			FAN_ONDIR | FAN_EVENT_ON_CHILD, -1, "/");
+
+	nfds = 1;
+	fds[0].fd = fd;
+	fds[0].events = POLLIN;
+
+	while (tst_remaining_runtime() > 10) {
+		poll_num = poll(fds, nfds, 10);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "fanotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[0].revents & POLLIN) {
+				/* Fanotify events are available */
+				fa_handle_events(fd);
+				ecnt++;
+			}
+		}
+	}
+	tst_printf("Got %d fanotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+static void freadfiles(char *fn)
+{
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		f = fopen(fn, "r+");
+		if (f == NULL)
+			continue;
+		fread(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+	}
+}
+
+static void fwritefiles(char *fn)
+{
+	char buf[BUFSIZ];
+	FILE *f;
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		f = fopen(fn, "w+");
+		if (f == NULL)
+			continue;
+		fwrite(buf, sizeof(char), BUFSIZ, f);
+		usleep(1);
+		fclose(f);
+	}
+}
+
+static void readfiles(char *fn)
+{
+	int fd;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		fd = open(fn, O_RDONLY);
+		if (fd == -1)
+			continue;
+		read(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+	}
+}
+
+static void writefiles(char *fn)
+{
+	int fd;
+	char buf[BUFSIZ];
+
+	memset(buf, 1, BUFSIZ);
+	while (tst_remaining_runtime() > 10) {
+		fd = open(fn, O_RDWR);
+		if (fd == -1)
+			continue;
+		write(fd, buf, BUFSIZ);
+		usleep(1);
+		close(fd);
+	}
+}
+
+static void inotify_add_rm(char *fn)
+{
+	int notify_fd;
+	int wd;
+
+	notify_fd = SAFE_MYINOTIFY_INIT1(IN_CLOEXEC);
+
+	while (tst_remaining_runtime() > 10) {
+		wd = inotify_add_watch(notify_fd, fn,
+			IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE |
+			IN_CLOSE_NOWRITE | IN_CREATE | IN_DELETE |
+			IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF |
+			IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN);
+
+		inotify_rm_watch(notify_fd, wd);
+	}
+	close(notify_fd);
+}
+
+static void inotify_inits(void)
+{
+	int notify_fd;
+
+	while (tst_remaining_runtime() > 10) {
+		notify_fd = inotify_init1(IN_CLOEXEC);
+		close(notify_fd);
+	}
+}
+
+static void inotify_add_rm_watches(char *fn)
+{
+	int fd, wd;
+
+	fd = SAFE_MYINOTIFY_INIT();
+
+	while (tst_remaining_runtime() > 10) {
+		wd = inotify_add_watch(fd, fn, IN_MODIFY);
+		inotify_rm_watch(fd, wd);
+	}
+	close(fd);
+}
+
+static void i_handle_events(int fd)
+{
+	char buf[4096]
+		__attribute__((aligned(__alignof__(struct inotify_event))));
+	ssize_t len;
+
+	/* Loop while events can be read from inotify file descriptor. */
+	for (;;) {
+		len = read(fd, buf, sizeof(buf));
+		if (len == -1 && errno != EAGAIN)
+			tst_brk(TBROK | TERRNO, "inotify read event failed");
+		/* If the nonblocking read() found no events to read, then
+		 * it returns -1 with errno set to EAGAIN. In that case,
+		 * we exit the loop.
+		 */
+		if (len <= 0)
+			break;
+	}
+}
+
+static void inotify_watch(char *fn)
+{
+	int fd, poll_num, wd, ecnt = 0;
+	nfds_t nfds;
+	struct pollfd fds[2];
+
+	fd = SAFE_MYINOTIFY_INIT1(IN_NONBLOCK);
+
+	/* Mark directories for events
+	 * - file was opened
+	 * - file was closed
+	 */
+	wd = SAFE_MYINOTIFY_ADD_WATCH(fd, fn, IN_OPEN | IN_CLOSE);
+
+	nfds = 2;
+	fds[0].fd = STDIN_FILENO;       /* Console input */
+	fds[0].events = POLLIN;
+	fds[1].fd = fd;                 /* Inotify input */
+	fds[1].events = POLLIN;
+
+	/* Wait for events and/or terminal input. */
+	while (tst_remaining_runtime() > 10) {
+		poll_num = poll(fds, nfds, 10);
+		if (poll_num == -1)
+			tst_brk(TBROK | TERRNO, "inotify watch poll failed");
+		if (poll_num > 0) {
+			if (fds[1].revents & POLLIN) {
+				/* Inotify events are available. */
+				i_handle_events(fd);
+				ecnt++;
+			}
+		}
+	}
+
+	inotify_rm_watch(fd, wd);
+	close(fd);
+	tst_printf("Got %d inotify events\n", ecnt);
+	tst_flush();
+	exit(EXIT_SUCCESS);
+}
+
+struct tcase {
+	char *desc;
+	void (*func_test)(char *fn);
+	int ondir;  /* run stress on directory */
+	int onfile;  /* run stress on file */
+};
+static struct tcase tcases[] = {
+	{"fanotify_flush stress", fanotify_flushes, 1, 1},
+	{"fanotify_init stress", fanotify_inits, 1, 1},
+	{"fanotify_mark stress", fanotify_marks, 1, 1},
+	{"fanotify watching stress", fanotify_watch, 1, 0},
+	{"fread stress", freadfiles, 0, 1},
+	{"fwrite stress", fwritefiles, 0, 1},
+	{"inotify add rm stress", inotify_add_rm, 1, 1},
+	{"inotify init stress", inotify_inits, 1, 1},
+	{"inotify add rm watch stress", inotify_add_rm_watches, 1, 1},
+	{"inotify watching stress", inotify_watch, 1, 0},
+	{"read stress", readfiles, 0, 1},
+	{"write stress", writefiles, 0, 1}
+};
+
+static void run(void)
+{
+	int tcnt = ARRAY_SIZE(tcases);
+	int i = 0;
+	const struct tst_clone_args args = {
+		.exit_signal = SIGCHLD,
+	};
+
+	tst_res(TINFO, "Starting %d stresses\n", tcnt);
+
+	while (i < tcnt) {
+		if (tcases[i].ondir && !SAFE_CLONE(&args)) {
+			tst_res(TINFO, "Starting %s on dir\n", tcases[i].desc);
+			tcases[i].func_test(TESTDIR);
+			tst_res(TINFO, "Ending %s on dir\n", tcases[i].desc);
+			tst_flush();
+			exit(EXIT_SUCCESS);
+		}
+		if (tcases[i].onfile && !SAFE_CLONE(&args)) {
+			tst_res(TINFO, "Starting %s on file\n", tcases[i].desc);
+			tcases[i].func_test(TESTFILE);
+			tst_res(TINFO, "Ending %s on file\n", tcases[i].desc);
+			exit(EXIT_SUCCESS);
+		}
+		i++;
+	}
+	tst_reap_children();
+	tst_res(TPASS, "No panic no hang, test PASS");
+}
+
+static struct tst_test test = {
+	.tcnt = 1,
+	.max_runtime = 60,
+	.forks_child = 1,
+	.needs_root = 1,
+	.needs_tmpdir = 1,
+	.test = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "4396a731 "},
+		{},
+	},
+};
-- 
2.31.1


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [LTP] [PATCH v4] kernel/fs/fsnotify-stress: fsnotify stress test
  2022-09-02  8:17                 ` [LTP] [PATCH v4] " Murphy Zhou
@ 2022-10-17  9:40                   ` Richard Palethorpe
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Palethorpe @ 2022-10-17  9:40 UTC (permalink / raw)
  To: Murphy Zhou; +Cc: ltp

Hello,

Murphy Zhou <jencce.kernel@gmail.com> writes:

> This is a stress test that exercises fanotify and inotify interfaces
> while IO going on. It intentionally ignores failures or return values
> of some syscalls to let the stress go on. If the kernel does not panic
> or hang after a certain period of time of testing, test pass.
>
> Signed-off-by: Murphy Zhou <jencce.kernel@gmail.com>
> ---
> v3 -> v4:
> 	Convert comment to docparse part.
>
>  runtest/fs                                    |   2 +
>  testcases/kernel/fs/fsnotify-stress/Makefile  |   9 +
>  .../fs/fsnotify-stress/fsnotify-stress.c      | 476 ++++++++++++++++++
>  3 files changed, 487 insertions(+)
>  create mode 100644 testcases/kernel/fs/fsnotify-stress/Makefile
>  create mode 100644 testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
>
> diff --git a/runtest/fs b/runtest/fs
> index 1d753e0dd..beb43aae4 100644
> --- a/runtest/fs
> +++ b/runtest/fs
> @@ -87,3 +87,5 @@ binfmt_misc01 binfmt_misc01.sh
>  binfmt_misc02 binfmt_misc02.sh
>  
>  squashfs01 squashfs01
> +
> +fsnotify-stress fsnotify-stress
> diff --git a/testcases/kernel/fs/fsnotify-stress/Makefile b/testcases/kernel/fs/fsnotify-stress/Makefile
> new file mode 100644
> index 000000000..451f791f1
> --- /dev/null
> +++ b/testcases/kernel/fs/fsnotify-stress/Makefile
> @@ -0,0 +1,9 @@
> +#
> +#    kernel/fs/fs-notify testcases Makefile.
> +#
> +
> +top_srcdir	?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
> new file mode 100644
> index 000000000..8130f7f12
> --- /dev/null
> +++ b/testcases/kernel/fs/fsnotify-stress/fsnotify-stress.c
> @@ -0,0 +1,476 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
> + * Author: Murphy Zhou <jencce.kernel@gmail.com>
> + * Copyright (c) Linux Test Project, 2001-2022
> + */
> +
> +/*\
> + * [Description]
> + *
> + * This is an irregular stress test for Linux kernel fanotify/inotify
> + * interfaces. It calls thoese interfaces with possible best coverage
> + * arguments, in a loop. It ignores some return values in the loop to
> + * let the stress going on. At the same time, it initiates IO traffics
> + * by calling IO syscalls.
> + *
> + * If kernel does no panic or hang after the test, test pass.
> + *
> + * It detected a leak in fsnotify code which was fixed by Amir through
> + * this Linux commit:
> + *     4396a731 fsnotify: fix sb_connectors leak

The problem with stress tests is that they are expensive to run. If they
do fail then it's often difficult to reproduce the errors. Eventually
they just get added to a skip list.

Why not make a reproducer for this bug which executes in the minimum
time necessary?

As well as saving CPU time and avoiding random timeouts this helps
create a better understanding of what really matters in the test.

> + *
> + */
> +
> +#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <poll.h>
> +#include <sys/fanotify.h>
> +#include <sys/inotify.h>
> +#include <sys/time.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +#include "tst_test.h"
> +#include "../../syscalls/fanotify/fanotify.h"
> +#include "../../syscalls/inotify/inotify.h"
> +
> +static int fd0;
> +
> +#define TESTDIR "testdir"
> +#define TESTFILE "testdir/file"
> +
> +static void cleanup(void)
> +{
> +	if (fd0 > 0) {
> +		SAFE_CLOSE(fd0);
> +	}
> +}
> +
> +static void setup(void)
> +{
> +	SAFE_MKDIR(TESTDIR, 0777);
> +	fd0 = SAFE_OPEN(TESTFILE, O_CREAT|O_RDWR, 0666);
> +}
> +
> +static void fanotify_flushes(char *fn)
> +{
> +	int fd;
> +
> +	fd = SAFE_FANOTIFY_INIT(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
> +					   O_RDONLY | O_LARGEFILE);
> +
> +	while (tst_remaining_runtime() > 10) {
> +		/* As a stress test, we ignore the return values here to
> +		 * proceed with the stress.
> +		 */

The LTP style guide forbids inline comments unless they are explaining
something that would be very difficult to understand without them. There
are lots of comments like this in the test.

> +		fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
> +			  FAN_ACCESS | FAN_MODIFY | FAN_OPEN_PERM | FAN_CLOSE |
> +			  FAN_OPEN | FAN_ACCESS_PERM | FAN_ONDIR |
> +			  FAN_EVENT_ON_CHILD, -1, fn);
> +

...

> +static void readfiles(char *fn)
> +{
> +	int fd;
> +	char buf[BUFSIZ];
> +
> +	memset(buf, 1, BUFSIZ);
> +	while (tst_remaining_runtime() > 10) {
> +		fd = open(fn, O_RDONLY);

If this fails then what are we stressing? We could just be testing
spinning in a loop.

> +		if (fd == -1)
> +			continue;
> +		read(fd, buf, BUFSIZ);

Also ignoring the result of read results in compiler warnings.

> +
> +static struct tst_test test = {
> +	.tcnt = 1,
> +	.max_runtime = 60,

Does it need 60 seconds to reproduce the bug?
Why does it need this long to achieve the desired coverage?

Without further evidence I'd assume that full coverage (ignoring setup)
is approached after <1 second on a reasonable system.

Also we should limit the number of iterations.

-- 
Thank you,
Richard.

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2022-10-17 10:40 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-25  2:47 [LTP] [PATCH 1/2] kernel/fs/fs-notify: fsnotify stress tests Murphy Zhou
2022-01-25  7:29 ` Amir Goldstein
2022-01-25  8:30   ` Murphy Zhou
2022-01-25 10:28     ` Petr Vorel
2022-01-25 10:56     ` Cyril Hrubis
2022-01-25 11:04 ` Cyril Hrubis
2022-01-27  4:42   ` Murphy Zhou
2022-02-28  6:29   ` Murphy Zhou
2022-02-28  6:42     ` Petr Vorel
2022-03-03  3:06       ` [LTP] [PATCH v2] kernel/fs/fsnotify-stress: fsnotify stress test Murphy Zhou
2022-03-03 15:22         ` Petr Vorel
2022-03-04  3:28           ` Murphy Zhou
2022-03-08  1:38           ` [LTP] [PATCH v3] " Murphy Zhou
2022-03-09 10:38             ` Cyril Hrubis
2022-03-10 20:48               ` Petr Vorel
2022-09-02  7:58                 ` Murphy Zhou
2022-09-02  8:17                 ` [LTP] [PATCH v4] " Murphy Zhou
2022-10-17  9:40                   ` Richard Palethorpe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.