[Devel] [PATCH 9/9] cr_tests: fs: Add fsnotify test for dnotify
Matt Helsley
matthltc at us.ibm.com
Fri Feb 19 18:18:57 PST 2010
Test that all dnotify flags are saved and restored properly.
Signed-off-by: Matt Helsley <matthltc at us.ibm.com>
---
fs/notify/dnotify.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++
fs/notify/module.mk | 12 +++
fs/notify/run.sh | 146 +++++++++++++++++++++++++++++++++
3 files changed, 384 insertions(+), 0 deletions(-)
create mode 100644 fs/notify/dnotify.c
create mode 100644 fs/notify/module.mk
create mode 100755 fs/notify/run.sh
diff --git a/fs/notify/dnotify.c b/fs/notify/dnotify.c
new file mode 100644
index 0000000..9f7652d
--- /dev/null
+++ b/fs/notify/dnotify.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2009 IBM Corp.
+ * Author: Matt Helsley <matthltc at us.ibm.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h> /* also for dnotify definitions like DN_* */
+#include <signal.h>
+#include <dirent.h> /* for dirfd */
+#include <assert.h>
+#include <getopt.h>
+#include <poll.h> /* POLL* definitions */
+#include <string.h>
+#include <errno.h>
+
+#include <sys/stat.h> /* for mkdir */
+#include <sys/types.h>
+
+#include "libcrtest/libcrtest.h"
+#include "libcrtest/labels.h"
+
+#define LOG_FILE "log.dnotify"
+extern FILE *logfp;
+
+static void usage(FILE *pout)
+{
+ fprintf(pout, "\ndnotify [-L] [-N] [-h|--help] [-l LABEL] [-n NUM] [-f DIR]\n"
+"Create and monitor a directory with dnotify.\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\t-f\tUse cgroup specified by DIR to freeze.\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ { "freezer", 1, 0, 'f'},
+ {0, 0, 0, 0},
+};
+
+static char *freezer = "1";
+
+static void parse_args(int argc, char **argv)
+{
+ ckpt_op_num = num_labels - 1;
+ ckpt_label = labels[ckpt_op_num];
+ while (1) {
+ int c;
+ c = getopt_long(argc, argv, "f:LNhl:n:", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'f':
+ freezer = optarg;
+ printf("Will enter freezer cgroup %s\n", freezer);
+ break;
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+#define eassert(cond) do { \
+ typeof(cond) ____cond = (cond); \
+ if (!____cond) \
+ perror(stringify(cond)); \
+ assert(!!____cond); \
+} while(0)
+
+#define DN_ALL (DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB|DN_MODIFY|DN_ACCESS)
+static int watchfd = -1;
+
+static void dnotify_sigio(int signo, siginfo_t *info, void *ucontext)
+{
+ assert(signo == SIGIO);
+ assert(info->si_signo == SIGIO);
+/* assert(info->si_fd == watchfd);
+ assert(info->si_code == POLLMSG);
+ assert(info->si_band == (POLLIN|POLLRDNORM|POLLMSG));*/
+ /* TODO find a way to double-check dn_events ?? siginfo doesn't give enough */ \
+}
+
+#define dn_expect_events(dn_events) do { \
+ struct timespec __timeout = { \
+ .tv_sec = 1, \
+ .tv_nsec = 0 \
+ }; \
+ siginfo_t __info; \
+label(dn_events ## _sigio_wait, ret, sigtimedwait(&sigio, &__info, &__timeout)); \
+ dnotify_sigio(ret, &__info, NULL); \
+} while (0)
+
+int main (int argc, char **argv)
+{
+ const char *writ_contents = "hello world";
+ char contents[16];
+ sigset_t sigio;
+ DIR *dir;
+ int ret = 0, fd = -1;
+ int op_num = 0;
+
+ parse_args(argc, argv);
+
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w+");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ /* redirect stdout and stderr to the log file */
+ if (dup2(fileno(logfp), 1) < 0) {
+ fprintf(logfp, "dup2(logfp, 1)\n");
+ goto out;
+ }
+ if (dup2(fileno(logfp), 2) < 0) {
+ fprintf(logfp, "dup2(logfp, 2)\n");
+ goto out;
+ }
+ if (!move_to_cgroup("freezer", freezer, getpid())) {
+ fprintf(logfp, "move_to_cgroup\n");
+ goto out;
+ }
+ printf("entered cgroup %s\n", freezer);
+ ret = mkdir("./dwatch", S_IRWXU);
+ eassert(ret == 0);
+ dir = opendir("./dwatch");
+ eassert(dir);
+ watchfd = dirfd(dir);
+ eassert(watchfd >= 0);
+
+ /* Block SIGIO -- handle it synchronously with dn_expect_events() */
+ ret = sigemptyset(&sigio);
+ eassert(ret == 0);
+ ret = sigaddset(&sigio, SIGIO);
+ eassert(ret == 0);
+ ret = sigprocmask(SIG_BLOCK, &sigio, NULL);
+ eassert(ret == 0);
+
+label(watch_create, ret, fcntl(watchfd, F_NOTIFY, DN_CREATE));
+label(do_create, ret, open("./dwatch/create", O_CREAT|O_EXCL|O_RDWR));
+ fd = ret;
+ assert(fd != watchfd);
+ assert(fd >= 0);
+ dn_expect_events(DN_CREATE);
+
+label(watch_modify, ret, fcntl(watchfd, F_NOTIFY, DN_MODIFY));
+label(do_modify, ret, write(fd, writ_contents, strlen(writ_contents) + 1));
+ assert(ret == (strlen(writ_contents) + 1));
+ dn_expect_events(DN_MODIFY);
+
+ ret = lseek(fd, 0, SEEK_SET);
+ assert(ret == 0);
+label(watch_access, ret, fcntl(watchfd, F_NOTIFY, DN_ACCESS));
+label(do_access, ret, read(fd, contents, strlen(writ_contents) + 1));
+ assert(ret == (strlen(writ_contents) + 1));
+ assert(strcmp(contents, writ_contents) == 0);
+ dn_expect_events(DN_ACCESS);
+
+label(watch_attrib, ret, fcntl(watchfd, F_NOTIFY, DN_ATTRIB));
+label(do_attrib, ret, fchmod(fd, S_IRUSR));
+ assert(ret == 0);
+ ret = close(fd);
+ assert(ret == 0);
+ fd = 0;
+ dn_expect_events(DN_ATTRIB);
+
+label(watch_rename, ret, fcntl(watchfd, F_NOTIFY, DN_RENAME));
+label(do_rename, ret, rename("./dwatch/create", "./dwatch/rename"));
+ assert(ret == 0);
+ dn_expect_events(DN_RENAME);
+
+label(watch_delete, ret, fcntl(watchfd, F_NOTIFY, DN_DELETE|DN_MULTISHOT));
+label(do_delete, ret, unlink("./dwatch/rename"));
+ assert(ret == 0);
+ dn_expect_events(DN_DELETE);
+label(do_rmdir, ret, rmdir("./dwatch"));
+ assert(ret == 0);
+ ret = EXIT_SUCCESS;
+out:
+ fprintf(logfp, "\nerrno: %s\n", strerror(errno));
+ if (fd >= 0)
+ close(fd);
+ closedir(dir);
+ fclose(logfp);
+ if (ret != EXIT_SUCCESS)
+ ret = EXIT_FAILURE;
+ exit(ret);
+}
+
diff --git a/fs/notify/module.mk b/fs/notify/module.mk
new file mode 100644
index 0000000..6c2eb44
--- /dev/null
+++ b/fs/notify/module.mk
@@ -0,0 +1,12 @@
+local_dir := fs/notify
+local_progs := $(addprefix $(local_dir)/, dnotify)
+
+sources += $(addprefix $(local_dir)/,../libfstest.c)
+progs += $(local_progs)
+test_clean += $(local_dir)/cr_fsnotify*
+
+# fs tests include libcrtest/libcrtest.h
+$(local_progs): CFLAGS += -D_GNU_SOURCE=1
+$(local_progs): CPPFLAGS += -I .
+$(local_progs): LDFLAGS += -Xlinker -dT -Xlinker libcrtest/labels.lds
+$(local_progs): $(local_dir)/../libfstest.o libcrtest/libcrtest.a
diff --git a/fs/notify/run.sh b/fs/notify/run.sh
new file mode 100755
index 0000000..603d7df
--- /dev/null
+++ b/fs/notify/run.sh
@@ -0,0 +1,146 @@
+#!/bin/bash
+
+
+source ../../common.sh
+dir=`mktemp -p . -d -t cr_fsnotify_XXXXXXX` || (echo "mktemp failed"; exit 1)
+echo "Using output dir $dir"
+cd $dir
+
+#
+# Check if the running kernel supports fsnotify
+#
+( if [ -r /proc/config.gz ]; then
+ zcat /proc/config.gz
+elif [ -r /proc/config ]; then
+ cat /proc/config
+# elif TODO look for CONFIG_DNOTIFY=y in /boot/config-$(uname -r)
+else
+# There is no way to test CONFIG_DNOTIFY -- assume it is set =y
+ echo 'CONFIG_DNOTIFY=y'
+fi ) | grep -E '^[[:space:]]*CONFIG_[DI]NOTIFY=y' > /dev/null 2>&1
+[ $? ] || {
+ echo "WARNING: Kernel does not support fsnotify (dnotify, inotify, fanotify). Skipping tests."
+ exit 1
+}
+
+TESTS=( dnotify )
+
+#make ${TESTS[@]}
+
+# mount -t cgroup foo /cg
+# mkdir /cg/1
+# chown -R $(id --name -u).$(id --name -g) /cg/1
+err_msg="BROK"
+function do_err()
+{
+ if [ -n "${TEST_PID}" ]; then
+ local PIDLIST=( $(ps --ppid ${TEST_PID} -o pid=) ${TEST_PID} )
+ kill ${PIDLIST[@]} || kill -9 ${PIDLIST[@]}
+ fi
+ echo "${err_msg}"
+ ((failed++))
+ wait
+}
+
+failed=0
+
+NUMTESTS=${#TESTS[@]}
+for (( CURTEST = 0; CURTEST < NUMTESTS; CURTEST = CURTEST + 1 )); do
+ T=${TESTS[$CURTEST]}
+ ((IMAX = $(../${T} -N)))
+ echo "INFO: Test ${T} does:"
+ ../${T} -L | sed -e 's/^/INFO:/'
+
+ TEST_LABELS=( $(../${T} -L | tail -n '+2' | cut -f 3) )
+
+ # First we run the test taking checkpoints at all the labelled points
+ rm -f "./checkpoint-"{ready,done,skip}
+ echo "Running test: \"${T}\""
+ ../${T} &
+ TEST_PID=$!
+
+ trap 'do_err; break 2' ERR EXIT
+ for ((I = 0; I <= IMAX; I = I + 1)); do
+ TLABEL="${TEST_LABELS[$I]}"
+ while [ '!' -r "./checkpoint-ready" ]; do
+ sleep 0.1 # slow
+ done
+ echo "Taking checkpoint ${I}: ${TLABEL}."
+ freeze
+ trap 'thaw; do_err; break 2' ERR EXIT
+ sync # slow
+ cp log.${T} log.${T}.${I}.${TLABEL}
+ err_msg="FAIL"
+ ${CHECKPOINT} ${TEST_PID} > "checkpoint-${T}.${I}.${TLABEL}"
+ err_msg="BROK"
+
+ # Reset for the next iteration
+ touch "./checkpoint-done"
+ thaw
+ trap 'do_err; break 2' ERR EXIT
+ done
+
+ sleep 0.1
+ while [ '!' -r "./checkpoint-done" ]; do
+ touch "./checkpoint-done"
+ sleep 0.1
+ done
+
+ echo "Done taking checkpoints. Waiting for ${TEST_PID} to finish."
+
+ # FAIL if the status of the test program's original run isn't 0.
+ err_msg="FAIL"
+ wait ${TEST_PID} || /bin/true # FIXME why can't we wait for TEST_PID ?
+ err_msg="BROK"
+
+ echo "Original completed running."
+
+ # Save the original run's log for later comparison.
+ mv log.${T} log.${T}.orig
+
+ # Now that the original, non-restart run is complete let's restart
+ # each checkpoint and make sure they produce the same results.
+ trap 'do_err; break 2' EXIT
+ trap '' ERR
+ echo "Restarting checkpoints"
+ for ((I=0; I <= IMAX; I = I + 1)); do
+ touch "./checkpoint-ready" "./checkpoint-done" "./checkpoint-skip"
+ TLABEL="${TEST_LABELS[$I]}"
+
+ # now try restarting. restore log first
+ cp log.${T}.${I}.${TLABEL} log.${T}
+ echo "Restart ${I} ${TLABEL}"
+ err_msg="FAIL"
+ # --copy-status ensures that we trap on error.
+ trap 'do_err; break 2' ERR
+ ${RESTART} --copy-status -w < checkpoint-${T}.${I}.${TLABEL}
+ echo restart returned $?
+ trap '' ERR
+ err_msg="BROK"
+
+ # Now compare the logs. We can strip the thread id differences
+ # since all of these tests are single-threaded.
+ SEDEXPR='s/^INFO: thread [[:digit:]]\+: //'
+ sed -i -e "${SEDEXPR}" "log.${T}.orig"
+ sed -i -e "${SEDEXPR}" "log.${T}"
+
+ err_msg="FAIL"
+ diff -s -pu "log.${T}.orig" "log.${T}" > "log.${T}.${I}.${TLABEL}.diff"
+ err_msg="BROK"
+ echo "PASS \"${T}\""
+ rm -f "log.${T}.${I}.${TLABEL}"* "log.${T}" "checkpoint-${T}.${I}.${TLABEL}"
+ done
+ rm -f "log.${T}.orig"
+
+ trap '' EXIT
+ wait
+done
+trap '' EXIT
+
+rm -f "./checkpoint-"{ready,done,skip}
+
+
+# rmdir /cg/1
+# umount /cg
+
+exit ${failed}
--
1.6.3.3
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list