src/locktest: Add lease testing for basic signal reception
[xfstests-dev.git] / src / locktest.c
index e2f9c8e37a067d7f3ccbaed6511e6880b8960dee..be71866860e2480a15260e39c7ec68efdd6392d5 100644 (file)
@@ -1,11 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2003 Silicon Graphics, Inc.
+ * Copyright (c) 2019 Intel Corp.
  * All Rights Reserved.
  */
 
 /*
- * Synchronized byte range lock exerciser
+ * Synchronized byte range lock and lease exerciser
  */
 
 #include <stdio.h>
@@ -26,6 +27,8 @@
 #include <byteswap.h>
 #include <errno.h>
 #include <string.h>
+#include <signal.h>
+
 #define     HEX_2_ASC(x)    ((x) > 9) ? (x)-10+'a' : (x)+'0'
 #define        FILE_SIZE       1024
 #define PLATFORM_INIT()     /*no-op*/
@@ -82,6 +85,7 @@ static int    server = 1;
 static int     port = 0;
 static int     testnumber = -1;
 static int     saved_errno = 0;
+static int      got_sigio = 0;
 
 static SOCKET  s_fd = -1;              /* listen socket    */
 static SOCKET  c_fd = -1;              /* IPC socket       */
@@ -94,6 +98,10 @@ static HANDLE        f_fd = INVALID_HANDLE;  /* shared file      */
 #define                CMD_OPEN        4
 #define                CMD_WRTEST      5
 #define                CMD_RDTEST      6
+#define        CMD_SETLEASE    7
+#define        CMD_GETLEASE    8
+#define                CMD_SIGIO       9
+#define                CMD_WAIT_SIGIO  10
 
 #define                PASS    1
 #define                FAIL    0
@@ -108,6 +116,8 @@ static HANDLE       f_fd = INVALID_HANDLE;  /* shared file      */
 #define                RESULT          4
 #define                WHO             5
 #define                FLAGS           2 /* index 2 is also used for do_open() flag, see below */
+#define                ARG             FLAGS /* Arguments for Lease operations */
+#define                TIME            FLAGS /* Time for waiting on sigio */
 
 static char *get_cmd_str(int cmd)
 {
@@ -119,6 +129,10 @@ static char *get_cmd_str(int cmd)
                case CMD_OPEN:   return "open"; break;
                case CMD_WRTEST: return "Wait for SIGIO"; break;
                case CMD_RDTEST: return "Truncate"; break;
+               case CMD_SETLEASE: return "Set Lease"; break;
+               case CMD_GETLEASE: return "Get Lease"; break;
+               case CMD_SIGIO:    return "Setup SIGIO"; break;
+               case CMD_WAIT_SIGIO: return "Wait for SIGIO"; break;
        }
        return "unknown";
 }
@@ -146,7 +160,7 @@ static char *get_cmd_str(int cmd)
  * (or vice versa)
  */
 
-char *descriptions[] = {
+char *lock_descriptions[] = {
     /* 1 */"Add a lock to an empty lock list",
     /* 2 */"Add a lock to the start and end of a list - no overlaps",
     /* 3 */"Add a lock to the middle of a list - no overlap",
@@ -183,7 +197,7 @@ char *descriptions[] = {
     #endif
 };
 
-static int64_t tests[][6] =
+static int64_t lock_tests[][6] =
        /*      test #  Action  [offset|flags]  length          expected        server/client */
        {       
        /* Various simple tests exercising the list */
@@ -544,6 +558,102 @@ static int64_t tests[][6] =
                {32,    CMD_OPEN,       O_RDWR,         0,              PASS,           SERVER, },
                {32,    CMD_OPEN,       O_RDWR,         0,              PASS,           CLIENT, },
 #endif /* macosx */
+
+       /* indicate end of array */
+               {0,0,0,0,0,SERVER},
+               {0,0,0,0,0,CLIENT}
+       };
+
+
+char *lease_descriptions[] = {
+    /*  1 */"Take Read Lease",
+    /*  2 */"Take Write Lease",
+    /*  3 */"Fail Write Lease if file is open somewhere else",
+    /*  4 */"Fail Read Lease if opened with write permissions",
+    /*  5 */"Read lease gets SIGIO on write open",
+    /*  6 */"Write lease gets SIGIO on read open",
+    /*  7 */"Read lease does _not_ get SIGIO on read open",
+    /*  8 */"Read lease gets SIGIO on write open",
+};
+
+static int64_t lease_tests[][6] =
+       /*      test #  Action  [offset|flags|arg]      length          expected        server/client */
+       /*                      [sigio_wait_time]                                               */
+       {
+       /* Various tests to exercise leases */
+
+/* SECTION 1: Simple verification of being able to take leases */
+       /* Take Read Lease */
+               {1,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+               {1,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+               {1,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {1,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+               {1,     CMD_SETLEASE,   F_RDLCK,        0,      PASS,           SERVER  },
+               {1,     CMD_GETLEASE,   F_RDLCK,        0,      PASS,           SERVER  },
+               {1,     CMD_SETLEASE,   F_UNLCK,        0,      PASS,           SERVER  },
+               {1,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {1,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Take Write Lease */
+               {2,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+               {2,     CMD_SETLEASE,   F_WRLCK,        0,      PASS,           SERVER  },
+               {2,     CMD_GETLEASE,   F_WRLCK,        0,      PASS,           SERVER  },
+               {2,     CMD_SETLEASE,   F_UNLCK,        0,      PASS,           SERVER  },
+               {2,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+       /* Fail Write Lease with other users */
+               {3,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+               {3,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+               {3,     CMD_SETLEASE,   F_WRLCK,        0,      FAIL,           SERVER  },
+               {3,     CMD_GETLEASE,   F_WRLCK,        0,      FAIL,           SERVER  },
+               {3,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {3,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+       /* Fail Read Lease if opened for write */
+               {4,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+               {4,     CMD_SETLEASE,   F_RDLCK,        0,      FAIL,           SERVER  },
+               {4,     CMD_GETLEASE,   F_RDLCK,        0,      FAIL,           SERVER  },
+               {4,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+
+/* SECTION 2: Proper SIGIO notifications */
+       /* Get SIGIO when read lease is broken by write */
+               {5,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+               {5,     CMD_SETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {5,     CMD_GETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {5,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+               {5,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+               {5,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+               {5,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {5,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when write lease is broken by read */
+               {6,     CMD_OPEN,       O_RDWR,         0,      PASS,           CLIENT  },
+               {6,     CMD_SETLEASE,   F_WRLCK,        0,      PASS,           CLIENT  },
+               {6,     CMD_GETLEASE,   F_WRLCK,        0,      PASS,           CLIENT  },
+               {6,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+               {6,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+               {6,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+               {6,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {6,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Don't get SIGIO when read lease is taken by read */
+               {7,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+               {7,     CMD_SETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {7,     CMD_GETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {7,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+               {7,     CMD_OPEN,       O_RDONLY,       0,      PASS,           SERVER  },
+               {7,     CMD_WAIT_SIGIO, 5,              0,      FAIL,           CLIENT  },
+               {7,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {7,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
+       /* Get SIGIO when Read lease is broken by Write */
+               {8,     CMD_OPEN,       O_RDONLY,       0,      PASS,           CLIENT  },
+               {8,     CMD_SETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {8,     CMD_GETLEASE,   F_RDLCK,        0,      PASS,           CLIENT  },
+               {8,     CMD_SIGIO,      0,              0,      PASS,           CLIENT  },
+               {8,     CMD_OPEN,       O_RDWR,         0,      PASS,           SERVER  },
+               {8,     CMD_WAIT_SIGIO, 5,              0,      PASS,           CLIENT  },
+               {8,     CMD_CLOSE,      0,              0,      PASS,           SERVER  },
+               {8,     CMD_CLOSE,      0,              0,      PASS,           CLIENT  },
+
        /* indicate end of array */
                {0,0,0,0,0,SERVER},
                {0,0,0,0,0,CLIENT}
@@ -607,6 +717,70 @@ initialize(HANDLE fd)
     }
 }
 
+void release_lease(int fd)
+{
+       int rc;
+
+       rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+       if (rc != 0)
+               fprintf(stderr, "%s Failed to remove lease %d : %d %s\n",
+                       __FILE__, rc, errno, strerror(errno));
+}
+
+void lease_break(int sig, siginfo_t *info, void *p)
+{
+    if (debug)
+       fprintf(stderr, "lease break %d %p fd %d\n",
+               sig, info, info->si_fd);
+    got_sigio = 1;
+    release_lease(f_fd);
+}
+
+struct sigaction lease_break_action = {
+       .sa_sigaction = lease_break,
+       .sa_flags = SA_SIGINFO,
+};
+
+int do_setup_sigio(int fd)
+{
+       int rc;
+
+       got_sigio = 0;
+
+       rc = sigaction(SIGIO, &lease_break_action, NULL);
+       if (rc != 0) {
+               fprintf(stderr, "%s Set '%s' sigaction failed %d\n",
+                       __FILE__, strsignal(SIGIO), rc);
+               return FAIL;
+       }
+
+       if (debug)
+               fprintf(stderr, "Set '%s' sigaction on %d\n",
+                       strsignal(SIGIO), fd);
+
+       rc = fcntl(fd, F_SETSIG, SIGIO);
+       if (rc)
+               fprintf(stderr, "%s Set '%s' sigaction failed %d\n",
+                       __FILE__, strsignal(SIGIO), rc);
+
+       return (rc == 0 ? PASS : FAIL);
+}
+
+int do_wait_sigio(int32_t time)
+{
+    if (time <= 0)
+       return FAIL;
+
+    while (!got_sigio && time--) {
+       sleep(1);
+    }
+
+    if (debug > 1 && !got_sigio)
+       fprintf(stderr, "%s failed to get sigio\n",
+               __FILE__);
+
+    return (got_sigio ? PASS: FAIL);
+}
 
 int do_open(int flag)
 {
@@ -647,12 +821,38 @@ static int do_lock(int cmd, int type, int start, int length)
     ret = fcntl(filedes, cmd, &fl);
     saved_errno = errno;           
 
-    if(debug > 1 && ret)
+    if(ret)
        fprintf(stderr, "do_lock: ret = %d, errno = %d (%s)\n", ret, errno, strerror(errno));
 
     return(ret==0?PASS:FAIL);
 }
 
+static int do_lease(int cmd, int arg, int expected)
+{
+    int ret;
+
+    if(debug > 1)
+       fprintf(stderr, "do_lease: cmd=%d arg=%d exp=%X\n",
+               cmd, arg, expected);
+
+    if (f_fd < 0)
+       return f_fd;
+
+    errno = 0;
+
+    ret = fcntl(f_fd, cmd, arg);
+    saved_errno = errno;
+
+    if (expected && (expected == ret))
+       ret = 0;
+
+    if(ret)
+       fprintf(stderr, "%s do_lease: ret = %d, errno = %d (%s)\n",
+               __FILE__, ret, errno, strerror(errno));
+
+    return(ret==0?PASS:FAIL);
+}
+
 int do_close(void)
 {      
     if(debug > 1) {
@@ -665,12 +865,15 @@ int do_close(void)
 
     saved_errno = errno;           
        
-    if (errno)
+    if (errno) {
+       fprintf(stderr, "%s errno = %d (%s)\n",
+               __FILE__, errno, strerror(errno));
        return FAIL;
+    }
     return PASS;
 }
 
-static void init_ctl(int32_t index)
+static void init_ctl(int64_t tests[][6], int32_t index)
 {
     ctl.test= (int32_t)tests[index][TEST_NUM];
     ctl.command = (int32_t)tests[index][COMMAND];
@@ -686,7 +889,7 @@ send_ctl(void)
 {
     int         nwrite;
 
-    if (debug > 1) {
+    if (debug) {
        fprintf(stderr, "send_ctl: test=%d, command=%d offset=%"LL"d, length=%"LL"d, result=%d, error=%d\n", 
                 ctl.test, ctl.command, (long long)ctl.offset, (long long)ctl.length,ctl.result, ctl.error);
     }
@@ -722,10 +925,13 @@ void recv_ctl(void)
 {
     int         nread;
 
+again:
     if ((nread = SOCKET_READ(c_fd, (char*)&ctl, sizeof(ctl))) != sizeof(ctl)) {
-        if (nread < 0)
+        if (nread < 0) {
+           if (errno == EINTR)
+               goto again;
             perror("recv_ctl: read");
-        else {
+        else {
             fprintf(stderr, "recv_ctl[%d]: read() returns %d, not %zu as expected\n", 
                     ctl.test, nread, sizeof(ctl));
            fprintf(stderr, "socket might has been closed by other locktest\n");
@@ -741,7 +947,7 @@ void recv_ctl(void)
     ctl.index= bswap_uint32(ctl.index);
     ctl.error= bswap_uint32(ctl.error);
 
-    if (debug > 1) {
+    if (debug) {
        fprintf(stderr, "recv_ctl: test=%d, command=%d offset=%"LL"d, length=%"LL"d, result=%d, error=%d\n", 
                 ctl.test, ctl.command, (long long)ctl.offset, (long long)ctl.length, ctl.result, ctl.error);
     }
@@ -762,6 +968,9 @@ cleanup(void)
     PLATFORM_CLEANUP();
 }
 
+int
+run(int64_t tests[][6], char *descriptions[]);
+
 int
 main(int argc, char *argv[])
 {
@@ -775,7 +984,8 @@ main(int argc, char *argv[])
     char       *p;
     extern char        *optarg;
     extern int optind;
-    int fail_count = 0;; 
+    int fail_count = 0;
+    int run_leases = 0;
     
     atexit(cleanup);
     
@@ -788,13 +998,17 @@ main(int argc, char *argv[])
            prog = p+1;
     }
 
-    while ((c = getopt(argc, argv, "dn:h:p:?")) != EOF) {
+    while ((c = getopt(argc, argv, "dLn:h:p:?")) != EOF) {
        switch (c) {
 
        case 'd':       /* debug flag */
            debug++;
            break;
 
+       case 'L':       /* Lease testing */
+           run_leases = 1;
+           break;
+
        case 'h':       /* (server) hostname */
            server = 0;
            host = optarg;
@@ -827,6 +1041,8 @@ main(int argc, char *argv[])
     }
 
     filename=argv[optind];
+    if (debug)
+       fprintf(stderr, "Working on file : %s\n", filename);
     if (do_open(O_RDWR) == FAIL)
        exit(1);
 
@@ -905,10 +1121,10 @@ main(int argc, char *argv[])
         struct hostent  *servInfo;
 
         if ((servInfo = gethostbyname(host)) == NULL) {
-           printf("Couldn't get hostbyname for %s", host);
+           fprintf(stderr, "Couldn't get hostbyname for %s", host);
            if (h_errno == HOST_NOT_FOUND)
-               printf(": host not found");
-           printf("\n");
+               fprintf(stderr, ": host not found");
+           fprintf(stderr, "\n");
             exit(1);
             /*NOTREACHED*/
         }
@@ -957,14 +1173,24 @@ main(int argc, char *argv[])
      *
      * real work is in here ...
      */
-    i = 0;
+    if (run_leases)
+       fail_count = run(lease_tests, lease_descriptions);
+    else
+       fail_count = run(lock_tests, lock_descriptions);
+
+    exit(fail_count);
+    /*NOTREACHED*/
+}
+
+int run(int64_t tests[][6], char *descriptions[])
 {
     int index = 0;
     int end = 0;
     int result = 0;
     int last_test = 0;
-    int test_count = 0;
+    int test_count = -1;
     int fail_flag = 0;
+    int fail_count = 0;
     while(!end) {
        if (server) {
            if(testnumber > 0) {
@@ -975,7 +1201,7 @@ main(int argc, char *argv[])
            }
            /* If we have a server command, deal with it */
            if(tests[index][WHO] == SERVER) {
-               if(debug>1)
+               if(debug)
                    fprintf(stderr, "Got a server command (%d)\n", index);
                if(tests[index][TEST_NUM] == 0) {
                    index++;
@@ -1007,20 +1233,30 @@ main(int argc, char *argv[])
                        case CMD_RDTEST:
                            result = do_lock(F_GETLK, F_RDLCK, tests[index][OFFSET], tests[index][LENGTH]);
                            break;
+                       case CMD_SETLEASE:
+                           result = do_lease(F_SETLEASE, tests[index][ARG], 0);
+                           break;
+                       case CMD_GETLEASE:
+                           result = do_lease(F_GETLEASE, tests[index][ARG], tests[index][ARG]);
+                           break;
+                       case CMD_SIGIO:
+                           result = do_setup_sigio(f_fd);
+                           break;
+                       case CMD_WAIT_SIGIO:
+                           result = do_wait_sigio(tests[index][TIME]);
+                           break;
                    }
                    if( result != tests[index][RESULT]) {
                        fail_flag++;
                        /* We have a failure */
-                       if(debug)
-                           fprintf(stderr, "Server failure in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n", 
-                                       ctl.test,
-                                       get_cmd_str(tests[index][COMMAND]),
-                                               (long long)tests[index][OFFSET],
-                                               (long long)tests[index][LENGTH],
-                                               saved_errno, strerror(saved_errno));
-                       fprintf(stderr, "Server failure in %lld:%s\n",
-                                       (long long)tests[index][TEST_NUM],
-                                       descriptions[tests[index][TEST_NUM] - 1]);
+                       fprintf(stderr, "     ***** Server failure *****\n");
+                       fprintf(stderr, "     in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n",
+                               ctl.test, get_cmd_str(tests[index][COMMAND]),
+                               (long long)tests[index][OFFSET],
+                               (long long)tests[index][LENGTH],
+                               saved_errno, strerror(saved_errno));
+                       fprintf(stderr, "     %d:%s\n",
+                                       ctl.test, descriptions[ctl.test - 1]);
                    }
                }
            /* else send it off to the client */
@@ -1030,8 +1266,8 @@ main(int argc, char *argv[])
                    end=1;
                } 
                /* get the client to do something */
-               init_ctl(index);
-               if(debug > 1)
+               init_ctl(tests, index);
+               if(debug)
                    fprintf(stderr, "Sending command to client (%d) - %s - %lld:%lld\n", 
                                        index,
                                        get_cmd_str(ctl.command),
@@ -1047,31 +1283,28 @@ main(int argc, char *argv[])
                     * not what the command returned */
                    if( ctl.result == FAIL ) {
                        fail_flag++;
-                       if(debug)
-                           fprintf(stderr, "Client failure in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n",
+                       fprintf(stderr, "     ***** Client failure *****\n");
+                       fprintf(stderr, "     in test %d, while %sing using offset %lld, length %lld - err = %d:%s\n",
                                        ctl.test, get_cmd_str(ctl.command),
                                        (long long)ctl.offset, (long long)ctl.length,
                                        ctl.error, strerror(ctl.error));
-                       fprintf(stderr, "Client failure in %lld:%s\n",
-                                       (long long)tests[index][TEST_NUM],
-                                       descriptions[tests[index][TEST_NUM] - 1]);
+                       fprintf(stderr, "     %d:%s\n",
+                                       ctl.test, descriptions[ctl.test - 1]);
                    }
                }
            }
-           if(tests[index][TEST_NUM] != 0) {
-               if(last_test != tests[index][TEST_NUM]) {
-                   test_count++;
-                   if(fail_flag)
-                       fail_count++;
-                   fail_flag = 0;
 
-               }
-               last_test = tests[index][TEST_NUM];
+           if(last_test != tests[index][TEST_NUM]) {
+               test_count++;
+               if(fail_flag)
+                   fail_count++;
+               fail_flag = 0;
+               last_test = tests[index][TEST_NUM];
            }
                
            index++;
        } else { /* CLIENT */
-           if(debug > 2)
+           if(debug)
                fprintf(stderr,"client: waiting...\n");
            /* wait for the server to do something */
            recv_ctl();
@@ -1110,11 +1343,25 @@ main(int argc, char *argv[])
                case CMD_RDTEST:
                    result = do_lock(F_GETLK, F_RDLCK, ctl.offset, ctl.length);
                    break;
+               /* NOTE offset carries the argument values */
+               case CMD_SETLEASE:
+                   result = do_lease(F_SETLEASE, ctl.offset, 0);
+                   break;
+               case CMD_GETLEASE:
+                   result = do_lease(F_GETLEASE, ctl.offset, ctl.offset);
+                   break;
+               case CMD_SIGIO:
+                   result = do_setup_sigio(f_fd);
+                   break;
+               case CMD_WAIT_SIGIO:
+                   result = do_wait_sigio(ctl.offset);
+                   break;
            }
            if( result != ctl.result ) {
-               if(debug)
-                   fprintf(stderr,"Got %d, wanted %d\n",
-                               result, ctl.result);
+               fprintf(stderr,"Failure in %d:%s\n",
+                       ctl.test, descriptions[ctl.test - 1]);
+               fprintf(stderr,"     Got %d, wanted %d\n",
+                       result, ctl.result);
                ctl.result = FAIL;
                ctl.error = saved_errno;
                fail_count++;
@@ -1122,23 +1369,17 @@ main(int argc, char *argv[])
                ctl.result = PASS;
                ctl.error = 0;
            }
-           if(debug > 2)
+           if(debug)
                fprintf(stderr,"client: sending result to server (%d)\n", ctl.index);
            /* Send result to the server */
            send_ctl();
-           if(tests[index][TEST_NUM] != 0) {
-               if(last_test != tests[index][TEST_NUM])
-                   test_count++;
+           if(last_test != tests[index][TEST_NUM]) {
+               test_count++;
                last_test = tests[index][TEST_NUM];
            }
        }
     }
-    if(server)
-       printf("%d tests run, %d failed\n", test_count, fail_count);
-}   
-    
-    exit(fail_count);
-    /*NOTREACHED*/
-}
-
+    fprintf(stderr, "%d tests run, %d failed\n", test_count, fail_count);
 
+    return fail_count;
+}