From: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
To: serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org
Cc: Containers
<containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>
Subject: [PATCH 10/11] filelock2: Test restart of process in F_GETLK
Date: Fri, 29 Jan 2010 12:44:03 -0800 [thread overview]
Message-ID: <20100129204403.GI26721@us.ibm.com> (raw)
In-Reply-To: <20100129202842.GA25490-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
From: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
Date: Wed, 27 Jan 2010 19:31:22 -0800
Subject: [PATCH 10/11] filelock2: Test restart of process in F_GETLK
Checkpoint a process that is waiting on a record lock in fcntl(F_GETLK).
Upon restart, ensure that it remains in fcntl(F_GETLK) and when the record
is unlocked, ensure that the process correctly gets the record lock.
Signed-off-by: Sukadev Bhattiprolu <sukadev-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
---
fileio/filelock2.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++
fileio/run-filelock2.sh | 5 +
2 files changed, 426 insertions(+), 0 deletions(-)
create mode 100644 fileio/filelock2.c
create mode 100755 fileio/run-filelock2.sh
diff --git a/fileio/filelock2.c b/fileio/filelock2.c
new file mode 100644
index 0000000..e24ce54
--- /dev/null
+++ b/fileio/filelock2.c
@@ -0,0 +1,421 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include "libcrtest.h"
+
+#define TEST_FILE "data.d/data.filelock2"
+#define LOG_FILE "logs.d/log.filelock2"
+
+extern FILE *logfp;
+int test_fd;
+int event_fd1;
+int event_fd2;
+int mandatory_locks = 1;
+
+/*
+ * Description:
+ * Ensure that a process waiting for a range lock on a file the time
+ * of checkpoint is properly notified after restart from the checkpoint.
+ *
+ * Implementation:
+ * Process P1 acquires a F_WRLCK on a range and waits for checkpoint.
+ * Process P2 waits in F_SETLKW. After the checkpoint, P1 confirms
+ * that it still has the lock and then unlocks the range. P2 must
+ * return succesfully from the fcntl() and must acquire the lock.
+ */
+struct test_record {
+ int start;
+ int len;
+};
+
+struct test_record test_record = { 0, 17 };
+
+void set_lock(int fd, int lock_type, struct test_record *rec)
+{
+ int rc;
+ struct flock lock;
+
+ lock.l_type = lock_type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = (off_t)rec->start;
+ lock.l_len = (off_t)rec->len;
+
+ rc = fcntl(fd, F_SETLKW, &lock);
+ if (rc < 0 && errno != EAGAIN) {
+ fprintf(logfp, "%d: set_lock(): ERROR [%d, %llu, %llu]: %s\n",
+ getpid(), lock_type, (u64)rec->start,
+ (u64)rec->len, strerror(errno));
+ if (mandatory_locks)
+ fprintf(logfp, "\n\t***** Is the FS mounted with "
+ "'-o mand' option ?\n\n");
+ fflush(logfp);
+ kill(getppid(), SIGUSR1);
+ do_exit(1);
+ }
+
+ fprintf(logfp, "%d: set_lock(): [%d, %llu, %llu] %s\n", getpid(),
+ lock_type, (u64)rec->start, (u64)rec->len,
+ rc < 0 ? strerror(errno) : "done");
+}
+/*
+ * If @set is TRUE, ensure that the given lock is set.
+ * If @set is FALSE, ensure that the given lock is NOT set.
+ */
+void test_lock(int fd, int lock_type, struct test_record *rec)
+{
+ int rc;
+ int conflict;
+ struct flock lock;
+ char lock_info[512];
+
+ lock.l_type = lock_type;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = (off_t)rec->start;
+ lock.l_len = (off_t)rec->len;
+ lock.l_pid = 0;
+
+ sprintf(lock_info, "lock [%d, %llu, %llu] ", lock_type,
+ (u64)rec->start, (u64)rec->len);
+
+ conflict = 0;
+ rc = fcntl(fd, F_SETLK, &lock);
+ if (rc < 0 && (errno == EAGAIN || errno == EACCES)) {
+ rc = fcntl(fd, F_GETLK, &lock);
+ if (rc < 0) {
+ fprintf(logfp, "ERROR: fcntl(F_GETLK): %s, error %s\n",
+ lock_info, strerror(errno));
+ goto error;
+ }
+
+ if (lock.l_type == F_UNLCK || lock.l_pid == 0) {
+ fprintf(logfp, "%d: ERROR: %s F_SETLK / F_GETLK "
+ "mismatch !!!\n", getpid(), lock_info);
+ goto error;
+ }
+ conflict = 1;
+ } else if (rc < 0) {
+ fprintf(logfp, "ERROR: fcntl(F_SETLK): %s, error %s\n",
+ lock_info, strerror(errno));
+ if (mandatory_locks)
+ fprintf(logfp, "\n\t***** Is the FS mounted with "
+ "'-o mand' option ?\n\n");
+ goto error;
+ }
+
+ fprintf(logfp, "%d: %s, conflict %d\n", getpid(), lock_info, conflict);
+
+ if (conflict) {
+ fprintf(logfp, "%d: FAIL: %s is NOT set by me !!!\n", getpid(),
+ lock_info);
+ goto error;
+ } else {
+ fprintf(logfp, "%d: PASS: %s is set by me\n", getpid(),
+ lock_info);
+ return;
+ }
+
+error:
+ fflush(logfp);
+ kill(getppid(), SIGUSR1);
+ do_exit(1);
+}
+
+void handler(int sig)
+{
+ /*
+ * We completed the test and sibling completed its test. Safe to
+ * exit.
+ */
+ fprintf(logfp, "%d: Ok to exit...\n", getpid());
+ fflush(logfp);
+ do_exit(0);
+}
+
+/*
+ * Notify parent that we are done testing and wait for a SIGINT to
+ * exit cleanly. Parent will wait for sibling to also exit and then
+ * signal us. This orderly exit will help parent distinguish this
+ * exit from an unexpected exit by the children.
+ */
+exit_cleanly()
+{
+ notify_one_event(event_fd2);
+ pause();
+ do_exit(0);
+}
+
+int do_child1(int idx)
+{
+ int rc;
+ int i;
+ int num_locks;
+ int failed;
+
+ fprintf(logfp, "%d: Child %d starting up\n", getpid(), idx);
+ fflush(logfp);
+
+ signal(SIGINT, handler);
+
+ set_lock(test_fd, F_WRLCK, &test_record);
+
+ /*
+ * Tell parent we are ready for checkpoint...
+ */
+ notify_one_event(event_fd1);
+
+ /*
+ * Wait for checkpoint/restart
+ */
+ fprintf(logfp, "%d: waiting for test-done\n", getpid());
+ fflush(logfp);
+ while(!test_done()) {
+ sleep(1);
+ }
+ fprintf(logfp, "%d: Found test-done\n", getpid());
+ fflush(logfp);
+
+ test_lock(test_fd, F_WRLCK, &test_record);
+
+ /*
+ * Drop our lock and exit. Sibling can then acquire the lock.
+ */
+ set_lock(test_fd, F_UNLCK, &test_record);
+
+ exit_cleanly();
+}
+
+int do_child2(int idx)
+{
+ int rc;
+ int i;
+ int num_locks;
+ int failed;
+
+ fprintf(logfp, "%d: Child %d starting up\n", getpid(), idx);
+ fflush(logfp);
+
+ signal(SIGINT, handler);
+
+ /*
+ * Tell parent we are about to get the lock.
+ * NOTE: There is still a window between now and when we actually
+ * block in the F_SETLK. But the parent has to guess and sleep()
+ * before enabling checkpoint.
+ */
+ notify_one_event(event_fd1);
+
+ /* This should block, until checkpoint/restart is done */
+ set_lock(test_fd, F_WRLCK, &test_record);
+
+ /*
+ * We must got here after checkpoint/restart. If not, something is
+ * wrong ?
+ */
+ if (!test_checkpoint_done()) {
+ fprintf(logfp, "Child2: ERROR: expected C/R to be done "
+ "by now, but is not ?\n");
+ do_exit(1);
+ }
+
+ /*
+ * Since we get here after checkpoint/restart, ensure we have the lock.
+ */
+ test_lock(test_fd, F_WRLCK, &test_record);
+
+ exit_cleanly();
+}
+
+/*
+ * Populate the test file so the children can lock some portions of
+ * the file
+ */
+void setup_test_file()
+{
+ char buf[256];
+ int mode;
+ int rc;
+
+ mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; /* 0666 */
+
+ test_fd = open(TEST_FILE, O_RDWR|O_CREAT|O_TRUNC, mode);
+ if (test_fd < 0) {
+ fprintf(logfp, "ERROR: open(%s): %s\n", TEST_FILE,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ write(test_fd, buf, sizeof(buf));
+
+ if (!mandatory_locks)
+ return;
+
+ /* Enable mandatory file locks (setgid, clear group execute) */
+ mode |= S_ISGID;
+ mode &= ~S_IXGRP;
+
+ rc = fchmod(test_fd, mode);
+ if (rc < 0) {
+ fprintf(logfp, "ERROR: fchmod(%s): rc %d, error %s\n",
+ TEST_FILE, rc, strerror(errno));
+ fprintf(logfp, "Maybe '-o mand' mount option is not set ?\n");
+ do_exit(1);
+ }
+ fprintf(logfp, "Mandatory locking set on %s, mode 0%o\n", TEST_FILE,
+ mode);
+}
+
+int pid1, pid2;
+
+kill_children(int sig)
+{
+ signal(SIGCHLD, SIG_DFL);
+ if (pid1)
+ kill(pid1, sig);
+
+ if (pid2)
+ kill(pid2, sig);
+ do_wait(2);
+}
+
+/*
+ * We get a SIGCHLD if a child exited prematurely. SIGUSR1 if child
+ * exited due to an error.
+ */
+void child_handler(int sig)
+{
+ fprintf(logfp, "%d: Got signal %d. Test case FAILED\n", getpid(), sig);
+ fflush(logfp);
+
+ /*
+ * Kill (remaining) children and exit.
+ */
+ kill_children(SIGKILL);
+
+ do_exit(-1);
+}
+
+usage(char *argv[])
+{
+ fprintf(logfp, "Usage: %s [-m]\n", argv[0]);
+ fprintf(logfp, "\tTest POSIX (advisory) file locks (without -m)\n");
+ fprintf(logfp, "\t-m: Test mandatory file locks\n");
+ fprintf(logfp, "Test FAILED\n");
+ do_exit(1);
+}
+
+int create_child(int idx, int (*child_func)(int))
+{
+ int rc;
+
+ rc = fork();
+ if (rc == 0)
+ (*child_func)(idx);
+
+ if (rc < 0) {
+ fprintf(logfp, "%d: fork() failed, error %s\n", getpid(),
+ strerror(errno));
+ kill_children(SIGKILL);
+ }
+
+ wait_for_events(event_fd1, 1);
+
+ return rc;
+}
+
+main(int argc, char *argv[])
+{
+ int i, c, status, rc;
+
+ logfp = fopen(LOG_FILE, "w");
+ if (!logfp) {
+ perror("open() logfile");
+ do_exit(1);
+ }
+
+ fprintf(logfp, "%d: Parent starting up\n", getpid());
+ fflush(logfp);
+
+ mandatory_locks = 0;
+ while((c = getopt(argc, argv, "m")) != EOF) {
+ switch (c) {
+ case 'm': mandatory_locks = 1; break;
+ default: usage(argv);
+ }
+ }
+
+ if (test_done()) {
+ printf("Remove %s before running test\n", TEST_DONE);
+ 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_file();
+ event_fd1 = setup_notification();
+ event_fd2 = setup_notification();
+
+ /*
+ * Before waiting for events below, ensure we will be notified
+ * if a child encounters an error and/or exits prematurely.
+ */
+ signal(SIGCHLD, child_handler);
+ signal(SIGUSR1, child_handler);
+
+ /*
+ * Create the first child and wait for it take its record lock.
+ */
+ pid1 = create_child(0, do_child1);
+
+ /*
+ * Create the second child and wait for it to block on the
+ * record lock.
+ *
+ */
+ pid2 = create_child(1, do_child2);
+
+ /*
+ * NOTE: We have some guessing to do here. The notification from
+ * the second child (in create_child()) just tells us that
+ * the child is _about_ to attempt the lock. Give it extra
+ * time to actually block before enabling checkpoint.
+ */
+ sleep(10);
+
+ /*
+ * Now that the test processes are ready, tell any wrapper scripts,
+ * we are ready for checkpoint.
+ */
+ set_checkpoint_ready();
+
+ fprintf(logfp, "***** %d: Ready for checkpoint\n", getpid());
+ fflush(logfp);
+
+ /*
+ * Wait for tests to finish their testing.
+ */
+ wait_for_events(event_fd2, 2);
+
+ /*
+ * Now tell them that it is safe to exit.
+ *
+ * NOTE: This orderly exit is needed to:
+ *
+ * - ensure we don't get stuck waiting for events, and,
+ * - enable us to distinguish normal and unexpected exits so
+ * we can properly report test status to wrapper scripts.
+ */
+ kill_children(SIGINT);
+
+ do_exit(0);
+}
diff --git a/fileio/run-filelock2.sh b/fileio/run-filelock2.sh
new file mode 100755
index 0000000..33934cb
--- /dev/null
+++ b/fileio/run-filelock2.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+./run-fcntltests.sh filelock2
+
+./run-fcntltests.sh filelock2 -m
--
1.6.0.4
next prev parent reply other threads:[~2010-01-29 20:44 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-01-29 20:28 [PATCH 01/11] runtests.sh: Make test bit more generic Sukadev Bhattiprolu
[not found] ` <20100129202842.GA25490-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-01-29 20:37 ` [PATCH 02/11] Make runtests.sh a wrapper for fileio tests Sukadev Bhattiprolu
2010-01-29 20:38 ` [PATCH 03/11] Check for failure while waiting for checkpoint-ready Sukadev Bhattiprolu
2010-01-29 20:42 ` [PATCH 04/11] Rename run-filelock1 to run-fcntltests.sh Sukadev Bhattiprolu
2010-01-29 20:42 ` [PATCH 05/11] Move event-notifications to libcrtest/common.c Sukadev Bhattiprolu
[not found] ` <20100129204228.GD26721-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2010-01-30 2:44 ` Serge E. Hallyn
2010-01-29 20:42 ` [PATCH 06/11] filelease1: Test restore of file leases Sukadev Bhattiprolu
2010-01-29 20:43 ` [PATCH 07/11] fsetown1: Test async I/O notification after restart Sukadev Bhattiprolu
2010-01-29 20:43 ` [PATCH 08/11] filelock1: Extend for mandatory locks Sukadev Bhattiprolu
2010-01-29 20:43 ` [PATCH 09/11] pthread1: Don't close stderr() before opening log Sukadev Bhattiprolu
2010-01-29 20:44 ` Sukadev Bhattiprolu [this message]
2010-01-29 20:44 ` [PATCH 11/11] filelease2: Test C/R during lease-break-interval Sukadev Bhattiprolu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20100129204403.GI26721@us.ibm.com \
--to=sukadev-23vcf4htsmix0ybbhkvfkdbpr1lh4cv8@public.gmane.org \
--cc=containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org \
--cc=serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox