From: Sukadev Bhattiprolu <[email protected]>
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 <[email protected]>
---
 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
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers

_______________________________________________
Devel mailing list
[email protected]
https://openvz.org/mailman/listinfo/devel

Reply via email to