diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h index 62207d137ee59829fa5faecbbea8f0047d6634ae..84c0790545aadd33efddf231a06d7a1bb89277e4 100644 --- a/lustre/include/obd_support.h +++ b/lustre/include/obd_support.h @@ -229,6 +229,7 @@ extern unsigned int obd_alloc_fail_rate; #define OBD_FAIL_LDLM_PAUSE_CANCEL 0x312 #define OBD_FAIL_LDLM_CLOSE_THREAD 0x313 #define OBD_FAIL_LDLM_CANCEL_BL_CB_RACE 0x314 +#define OBD_FAIL_LDLM_CP_CB_WAIT 0x315 #define OBD_FAIL_OSC 0x400 #define OBD_FAIL_OSC_BRW_READ_BULK 0x401 diff --git a/lustre/ldlm/ldlm_flock.c b/lustre/ldlm/ldlm_flock.c index f5eb0b94221d69e20fbfc1b578c5535a7bf80ef0..b158d1d448997766f3ac280940793580f75e1d2d 100644 --- a/lustre/ldlm/ldlm_flock.c +++ b/lustre/ldlm/ldlm_flock.c @@ -558,6 +558,7 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data) RETURN(rc); granted: + OBD_FAIL_TIMEOUT(OBD_FAIL_LDLM_CP_CB_WAIT, 10); LDLM_DEBUG(lock, "client-side enqueue granted"); ns = lock->l_resource->lr_namespace; lock_res_and_lock(lock); diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index 93712fa549a09d771016bc19de17fe70261de44c..65b0f4a6ad851e68bc56636d44f7a210b0f15009 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -59,6 +59,9 @@ multiop_LDADD=$(LIBLUSTREAPI) ll_dirstripe_verify_SOURCES= ll_dirstripe_verify.c ll_dirstripe_verify_LDADD= -L$(top_builddir)/lustre/utils -llustreapi +flocks_test_SOURCES=flocks_test.c +flocks_test_LDADD=-lpthread + if MPITESTS #LAM_LD_FLAGS=-L/opt/lam/lib -lmpi -llam -lpthread LAM_LD_FLAGS=-lmpich -lpthread diff --git a/lustre/tests/flocks_test.c b/lustre/tests/flocks_test.c index 633e1b1b9a4c1cbcc42c9f8cf800305f47f0ce6e..97890d85f57976935eba4bad1722c14bca77616c 100644 --- a/lustre/tests/flocks_test.c +++ b/lustre/tests/flocks_test.c @@ -40,38 +40,118 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> - +#include <pthread.h> #include <sys/file.h> +#include <stdarg.h> -void usage(void) +#define MAX_PATH_LENGTH 4096 +/** + * helper functions + */ +int t_fcntl(int fd, int cmd, ...) +{ + va_list ap; + long arg; + struct flock *lock; + int rc = -1; + + va_start(ap, cmd); + switch (cmd) { + case F_GETFL: + va_end(ap); + rc = fcntl(fd, cmd); + if (rc == -1) { + fprintf(stderr, "fcntl GETFL failed: %s\n", + strerror(errno)); + return(1); + } + break; + case F_SETFL: + arg = va_arg(ap, long); + va_end(ap); + rc = fcntl(fd, cmd, arg); + if (rc == -1) { + fprintf(stderr, "fcntl SETFL %ld failed: %s\n", + arg, strerror(errno)); + return(1); + } + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + lock = va_arg(ap, struct flock *); + va_end(ap); + rc = fcntl(fd, cmd, lock); + if (rc == -1) { + fprintf(stderr, "fcntl cmd %d failed: %s\n", + cmd, strerror(errno)); + return(1); + } + break; + case F_DUPFD: + arg = va_arg(ap, long); + va_end(ap); + rc = fcntl(fd, cmd, arg); + if (rc == -1) { + fprintf(stderr, "fcntl F_DUPFD %d failed: %s\n", + (int)arg, strerror(errno)); + return(1); + } + break; + default: + va_end(ap); + fprintf(stderr, "fcntl cmd %d not supported\n", cmd); + return(1); + } + return rc; +} + +int t_unlink(const char *path) { - fprintf(stderr, "usage: ./flocks_test on|off -c|-f|-l /path/to/file\n"); - exit(EXIT_FAILURE); + int rc; + + rc = unlink(path); + if (rc) + fprintf(stderr, "unlink(%s) error: %s\n", path, strerror(errno)); + return rc; } -int main(int argc, char *argv[]) +/** ================================================================= + * test number 1 + * + * normal flock test + */ +void t1_usage(void) +{ + fprintf(stderr, "usage: ./flocks_test 1 on|off -c|-f|-l /path/to/file\n"); +} + +int t1(int argc, char *argv[]) { int fd; int mount_with_flock = 0; int error = 0; - if (argc != 4) - usage(); - - if (!strncmp(argv[1], "on", 3)) { + if (argc != 5) { + t1_usage(); + return EXIT_FAILURE; + } + + if (!strncmp(argv[2], "on", 3)) { mount_with_flock = 1; - } else if (!strncmp(argv[1], "off", 4)) { + } else if (!strncmp(argv[2], "off", 4)) { mount_with_flock = 0; } else { - usage(); + t1_usage(); + return EXIT_FAILURE; } - if ((fd = open(argv[3], O_RDWR)) < 0) { - fprintf(stderr, "Couldn't open file: %s\n", argv[2]); - exit(EXIT_FAILURE); + if ((fd = open(argv[4], O_RDWR)) < 0) { + fprintf(stderr, "Couldn't open file: %s\n", argv[3]); + return EXIT_FAILURE; } - if (!strncmp(argv[2], "-c", 3)) { + if (!strncmp(argv[3], "-c", 3)) { struct flock fl; fl.l_type = F_RDLCK; @@ -80,12 +160,13 @@ int main(int argc, char *argv[]) fl.l_len = 1; error = fcntl(fd, F_SETLK, &fl); - } else if (!strncmp(argv[2], "-l", 3)) { + } else if (!strncmp(argv[3], "-l", 3)) { error = lockf(fd, F_LOCK, 1); - } else if (!strncmp(argv[2], "-f", 3)) { + } else if (!strncmp(argv[3], "-f", 3)) { error = flock(fd, LOCK_EX); } else { - usage(); + t1_usage(); + return EXIT_FAILURE; } if (mount_with_flock) @@ -93,3 +174,128 @@ int main(int argc, char *argv[]) else return((error == 0) ? EXIT_FAILURE : EXIT_SUCCESS); } + +/** =============================================================== + * test number 2 + * + * 2 threads flock ops interweave + */ +typedef struct { + struct flock* lock; + int fd; +} th_data; + +void* t2_thread1(void *arg) +{ + struct flock *lock = ((th_data *)arg)->lock; + int fd = ((th_data *)arg)->fd; + + printf("thread 1: set write lock (blocking)\n"); + lock->l_type = F_WRLCK; + t_fcntl(fd, F_SETLKW, lock); + printf("thread 1: set write lock done\n"); + t_fcntl(fd, F_GETLK, lock); + printf("thread 1: unlock\n"); + lock->l_type = F_UNLCK; + t_fcntl(fd, F_SETLK, lock); + printf("thread 1: unlock done\n"); + return 0; +} + +void* t2_thread2(void *arg) +{ + struct flock *lock = ((th_data *)arg)->lock; + int fd = ((th_data *)arg)->fd; + + sleep(2); + printf("thread 2: unlock\n"); + lock->l_type = F_UNLCK; + t_fcntl(fd, F_SETLK, lock); + printf("thread 2: unlock done\n"); + printf("thread 2: set write lock (non-blocking)\n"); + lock->l_type = F_WRLCK; + t_fcntl(fd, F_SETLK, lock); + printf("thread 2: set write lock done\n"); + t_fcntl(fd, F_GETLK, lock); + return 0; +} + +int t2(int argc, char* argv[]) +{ + struct flock lock = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + }; + char file[MAX_PATH_LENGTH] = ""; + int fd, rc; + pthread_t th1, th2; + th_data ta; + + snprintf(file, MAX_PATH_LENGTH, "%s/test_t2_file", argv[2]); + + fd = open(file, O_RDWR|O_CREAT, (mode_t)0666); + if (fd < 0) { + fprintf(stderr, "error open file: %s\n", file); + return EXIT_FAILURE; + } + + t_fcntl(fd, F_SETFL, O_APPEND); + if (!(rc = t_fcntl(fd, F_GETFL)) & O_APPEND) { + fprintf(stderr, "error get flag: ret %x\n", rc); + return EXIT_FAILURE; + } + + ta.lock = &lock; + ta.fd = fd; + rc = pthread_create(&th1, NULL, t2_thread1, &ta); + if (rc) { + fprintf(stderr, "error create thread 1\n"); + rc = EXIT_FAILURE; + goto out; + } + rc = pthread_create(&th2, NULL, t2_thread2, &ta); + if (rc) { + fprintf(stderr, "error create thread 2\n"); + rc = EXIT_FAILURE; + goto out; + } + (void)pthread_join(th1, NULL); + (void)pthread_join(th2, NULL); +out: + t_unlink(file); + close(fd); + return rc; +} + +/** ============================================================== + * program entry + */ +void usage(void) +{ + fprintf(stderr, "usage: ./flocks_test test# [corresponding arguments]\n"); +} + +int main(int argc, char* argv[]) +{ + int test_no; + int rc = EXIT_SUCCESS; + + if (argc < 1) { + usage(); + exit(EXIT_FAILURE); + } + test_no = atoi(argv[1]); + + switch(test_no) { + case 1: + rc = t1(argc, argv); + break; + case 2: + rc = t2(argc, argv); + break; + default: + fprintf(stderr, "unknow test number %s\n", argv[1]); + break; + } + return rc; +} diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 40d4b6f3e5bc39156e92f48face716f4177bcd5f..a4c96567c4c5bc0ca801613f4a52979ea683a484 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -4093,9 +4093,9 @@ test_105a() { touch $DIR/$tfile if [ -n "`mount | grep \"$DIR.*flock\" | grep -v noflock`" ]; then - flocks_test on -f $DIR/$tfile || error "fail flock on" + flocks_test 1 on -f $DIR/$tfile || error "fail flock on" else - flocks_test off -f $DIR/$tfile || error "fail flock off" + flocks_test 1 off -f $DIR/$tfile || error "fail flock off" fi } run_test 105a "flock when mounted without -o flock test ========" @@ -4104,9 +4104,9 @@ test_105b() { touch $DIR/$tfile if [ -n "`mount | grep \"$DIR.*flock\" | grep -v noflock`" ]; then - flocks_test on -c $DIR/$tfile || error "fail flock on" + flocks_test 1 on -c $DIR/$tfile || error "fail flock on" else - flocks_test off -c $DIR/$tfile || error "fail flock off" + flocks_test 1 off -c $DIR/$tfile || error "fail flock off" fi } run_test 105b "fcntl when mounted without -o flock test ========" @@ -4115,13 +4115,23 @@ test_105c() { touch $DIR/$tfile if [ -n "`mount | grep \"$DIR.*flock\" | grep -v noflock`" ]; then - flocks_test on -l $DIR/$tfile || error "fail flock on" + flocks_test 1 on -l $DIR/$tfile || error "fail flock on" else - flocks_test off -l $DIR/$tfile || error "fail flock off" + flocks_test 1 off -l $DIR/$tfile || error "fail flock off" fi } run_test 105c "lockf when mounted without -o flock test ========" +test_105d() { # bug 15924 + mkdir -p $DIR/$tdir + [ -z "`mount | grep \"$DIR.*flock\" | grep -v noflock`" ] && \ + skip "mount w/o flock enabled" && return + #define OBD_FAIL_LDLM_CP_CB_WAIT 0x315 + $LCTL set_param fail_loc=0x80000315 + flocks_test 2 $DIR/$tdir +} +run_test 105d "flock race (should not freeze) ========" + test_106() { #bug 10921 mkdir -p $DIR/$tdir $DIR/$tdir && error "exec $DIR/$tdir succeeded"