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 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.