From: Sukadev Bhattiprolu <sukadev@xxxxxxxxxxxxxxxxxx> Date: Thu, 21 Jan 2010 23:07:51 -0800 Subject: [PATCH 07/11] fsetown1: Test async I/O notification after restart Checkpoint a process that is waiting for async notification of data being available on a pipe. When the process is restarted, make data available on the pipe and ensure that the process is notified. Signed-off-by: Sukadev Bhattiprolu <sukadev@xxxxxxxxxxxxxxxxxx> --- fileio/Makefile | 4 +- fileio/fsetown1.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++ fileio/run-fsetown1.sh | 3 + fileio/runtests.sh | 5 + libcrtest/common.c | 2 +- 5 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 fileio/fsetown1.c create mode 100755 fileio/run-fsetown1.sh diff --git a/fileio/Makefile b/fileio/Makefile index acc2df9..bd28561 100644 --- a/fileio/Makefile +++ b/fileio/Makefile @@ -1,4 +1,4 @@ -targets = fileio1 filelock1 filelease1 +targets = fileio1 filelock1 filelease1 fsetown1 INCLUDE = ../libcrtest LIBCRTEST = ../libcrtest/common.o @@ -9,4 +9,4 @@ all: $(LIBCRTEST) $(targets) clean: rm -f $(targets) - rm -rf cr_fileio* cr_filelock1* cr_filelease1* + rm -rf cr_fileio* cr_filelock1* cr_filelease1* cr_fsetown1* diff --git a/fileio/fsetown1.c b/fileio/fsetown1.c new file mode 100644 index 0000000..c6c5734 --- /dev/null +++ b/fileio/fsetown1.c @@ -0,0 +1,268 @@ +#include <stdio.h> +#include <unistd.h> +#define __USE_GNU +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <wait.h> +#include "libcrtest.h" + +#define LOG_FILE "logs.d/log.fsetown1" + +int pipe_fds[2]; +int event_fd1; +int got_sigio; + +/* + * Description: + * Checkpoint a process that is waiting for async notification of data + * being available on a pipe. When the process is restarted, make data + * available on the pipe and ensure that the process is notified. + * + * Implementation: + */ +void iohandler(int sig) +{ + int rc; + char buf[16]; + + fprintf(logfp, "%d: Got signal %d\n", getpid(), sig); + fflush(logfp); + got_sigio = 1; +} + +static void wait_for_child() +{ + int rc; + int status; + + rc = waitpid(-1, &status, 0); + if (rc < 0) { + fprintf(logfp, "%d: waitpid(): rc %d, error %s\n", + getpid(), rc, strerror(errno)); + do_exit(1); + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + fprintf(logfp, "%d: Test case PASSED\n", getpid()); + rc = 0; + } else { + fprintf(logfp, "%d: Test case FAILED\n", getpid()); + print_exit_status(rc, status); + rc = 1; + } + do_exit(rc); +} + +void set_owner(int fd) +{ + int rc; + long flags; + + fprintf(logfp, "%d: Setting owner to myself\n", getpid()); + + signal(SIGIO, iohandler); + + flags = O_ASYNC; + rc = fcntl(fd, F_SETFL, flags); + if (rc < 0) { + fprintf(logfp, "%d: set_owner(): F_SETFL ERROR %s\n", getpid(), + strerror(errno)); + goto error; + } + + rc = fcntl(fd, F_SETOWN, getpid()); + if (rc < 0) { + fprintf(logfp, "%d: set_owner():, ERROR %s\n", getpid(), + strerror(errno)); + if (errno == EINVAL) + fprintf(logfp, "%d: Maybe the fs does not support " + "F_SETLEASE (eg: NFS)\n", getpid()); + goto error; + } + + fprintf(logfp, "%d: Set owner() done\n", getpid()); + return; + +error: + /* + * Parent will be waiting for notification. Signal that we failed + * and are exiting + */ + kill(getppid(), SIGUSR1); + do_exit(1); +} + +/* + * Called by parent to see if child is still the owner + */ +void test_owner(int fd, int exp_owner) +{ + int rc; + + rc = fcntl(fd, F_GETOWN, 0); + if (rc < 0) { + fprintf(logfp, "%d: ERROR: fcntl(F_GETOWN) error %s\n", + getpid(), strerror(errno)); + do_exit(1); + } + + if (rc != exp_owner) { + fprintf(logfp, "%d: FAILED: Expected owner %d, actual %d\n", + getpid(), exp_owner, rc); + /* + * Terminate the child since it will not be notified of I/O. + */ + kill(exp_owner, SIGKILL); + wait_for_child(); + do_exit(1); + } + + fprintf(logfp, "%d: PASS: Owner is %d\n", getpid(), exp_owner); + return; +} + +int do_child() +{ + int rc; + char buf[16]; + int fd = pipe_fds[0]; + + set_owner(fd); + + /* + * Tell parent we are ready for checkpoint... + */ + notify_one_event(event_fd1); + + /* + * Read data from the pipe. If this synchronous read finds data + * without a SIGIO signal, then we were not notified and the + * test fails. + */ + fprintf(logfp, "%d: Waiting for data to be available\n", getpid()); + fflush(logfp); + + rc = read(fd, buf, 4); + if (rc <= 0) { + fprintf(logfp, "%d: ERROR: read(): rc %d, error %s\n", + getpid(), rc, strerror(errno)); + do_exit(1); + } else if (!got_sigio) { + fprintf(logfp, "%d: FAILED: read() found data but did not" + "get SIGIO, rc %d buf %.4s\n", getpid(), + rc, buf); + do_exit(1); + } else { + fprintf(logfp, "%d: PASS: Got SIGIO, read data, rc %d, " + "buf '%.4s'\n", getpid(), rc, buf); + do_exit(0); + } +} + +/* + * Create a pipe that the child will try to read from and parent will + * write to. + */ +void setup_test_data() +{ + int rc; + + rc = pipe(pipe_fds); + if (rc < 0) { + fprintf(logfp, "%d: pipe() failed, rc %d, error %s\n", + getpid(), rc, strerror(errno)); + do_exit(1); + } + + return; +} + +void usr1_handler(int sig) +{ + /* + * Test failed or a child encountered an error. + * Reap the child, report error and exit. + */ + fprintf(logfp, "%d: Signal %d, Test case FAILED\n", getpid(), sig); + fflush(logfp); + + wait_for_child(); +} + +main(int argc, char *argv[]) +{ + int i, status, rc; + int pid; + + if (test_done()) { + printf("Remove %s before running test\n", TEST_DONE); + do_exit(1); + } + + logfp = fopen(LOG_FILE, "w"); + if (!logfp) { + perror("open() logfile"); + do_exit(1); + } + + printf("%s: Closing stdio fds and writing messages to %s\n", + argv[0], LOG_FILE); + + for (i=0; i<100; i++) { + if (fileno(logfp) != i) + close(i); + } + + setup_test_data(); + event_fd1 = setup_notification(); + + /* + * Before waiting for events below, ensure we will be notified + * if a child encounters an error. + */ + signal(SIGUSR1, usr1_handler); + + /* + * Create the child process and wait for it to be ready for checkpoint. + */ + pid = fork(); + if (pid == 0) + do_child(i); + + if (pid < 0) { + fprintf(logfp, "%d: fork() failed, rc %d, error %s\n", getpid(), + rc, strerror(errno)); + do_exit(1); + } + + wait_for_events(event_fd1, 1); + + /* + * Tell any wrapper scripts, we are ready for checkpoint + */ + set_checkpoint_ready(); + + fprintf(logfp, "%d: ***** Ready for checkpoint\n", getpid()); + fflush(logfp); + + /* Wait for wrappers to complete checkpoint/restart */ + while(!test_done()) + sleep(1); + + /* Ensure that child is still owner for the read side of pipe */ + test_owner(pipe_fds[0], pid); + + /* Make data available on the pipe for the child */ + rc = write(pipe_fds[1], "done", 4); + if (rc < 0) { + fprintf(logfp, "%d: write() failed, rc %d, error %s\n", + getpid(), rc, strerror(errno)); + kill(pid, SIGKILL); + do_exit(1); + } + + fflush(logfp); + wait_for_child(); +} diff --git a/fileio/run-fsetown1.sh b/fileio/run-fsetown1.sh new file mode 100755 index 0000000..535c544 --- /dev/null +++ b/fileio/run-fsetown1.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./run-fcntltests.sh fsetown1 diff --git a/fileio/runtests.sh b/fileio/runtests.sh index e83f9cc..b808927 100755 --- a/fileio/runtests.sh +++ b/fileio/runtests.sh @@ -14,3 +14,8 @@ echo echo "****** $0: Running test: filelease1" echo ./run-fcntltests.sh filelease1 + +echo +echo "****** $0: Running test: fsetown1" +echo +./run-fcntltests.sh fsetown1 diff --git a/libcrtest/common.c b/libcrtest/common.c index b29042a..c20da5e 100644 --- a/libcrtest/common.c +++ b/libcrtest/common.c @@ -58,7 +58,7 @@ void set_checkpoint_ready() close(fd); } -static void print_exit_status(int pid, int status) +void print_exit_status(int pid, int status) { fprintf(logfp, "Pid %d unexpected exit - ", pid); if (WIFEXITED(status)) { -- 1.6.0.4 _______________________________________________ Containers mailing list Containers@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/containers