All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 0/2] Add lease testing
@ 2019-09-05 17:30 ira.weiny
  2019-09-05 17:30 ` [PATCH V2 1/2] src/leasetest: Add lease test executable ira.weiny
  2019-09-05 17:30 ` [PATCH V2 2/2] generic: Add Lease testing ira.weiny
  0 siblings, 2 replies; 7+ messages in thread
From: ira.weiny @ 2019-09-05 17:30 UTC (permalink / raw)
  To: fstests
  Cc: john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

To get to a point of developing and testing the new Lease semantics for long
term file pins[1], add a simple lease test.

Currently the following tests are performed based on the fcntl() man page.

    /*  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 */"No SIGIO is sent with read lock on read open",
    /*  8 */"Read lease gets SIGIO on write open",

[1] https://lkml.org/lkml/2019/8/14/435

Ira Weiny (2):
  src/leasetest: Add lease test executable
  generic: Add Lease testing

 src/Makefile          |   2 +-
 src/leasetest.c       | 859 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/567     | 100 +++++
 tests/generic/567.out |   2 +
 tests/generic/group   |   1 +
 5 files changed, 963 insertions(+), 1 deletion(-)
 create mode 100644 src/leasetest.c
 create mode 100755 tests/generic/567
 create mode 100644 tests/generic/567.out

-- 
2.20.1

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH V2 1/2] src/leasetest: Add lease test executable
  2019-09-05 17:30 [PATCH V2 0/2] Add lease testing ira.weiny
@ 2019-09-05 17:30 ` ira.weiny
  2019-09-08 14:17   ` Eryu Guan
  2019-09-05 17:30 ` [PATCH V2 2/2] generic: Add Lease testing ira.weiny
  1 sibling, 1 reply; 7+ messages in thread
From: ira.weiny @ 2019-09-05 17:30 UTC (permalink / raw)
  To: fstests
  Cc: john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

locktest uses a pattern which is suitable for lease testing so this
patch copies locktest as a framework and modifies it for lease testing.

Initial tests are built based on the existing fcntl() man page declared
functionality.

Acked-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>

---
Changes:
	Adds tests which were covered here:
	https://marc.info/?l=fstests&m=156690934513424&w=2

 src/Makefile    |   2 +-
 src/leasetest.c | 859 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 860 insertions(+), 1 deletion(-)
 create mode 100644 src/leasetest.c

diff --git a/src/Makefile b/src/Makefile
index c4fcf370431f..c34b4add6695 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -28,7 +28,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
 	attr-list-by-handle-cursor-test listxattr dio-interleaved t_dir_type \
 	dio-invalidate-cache stat_test t_encrypted_d_revalidate \
 	attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
-	fscrypt-crypt-util bulkstat_null_ocount
+	fscrypt-crypt-util bulkstat_null_ocount leasetest
 
 SUBDIRS = log-writes perf
 
diff --git a/src/leasetest.c b/src/leasetest.c
new file mode 100644
index 000000000000..729aca851493
--- /dev/null
+++ b/src/leasetest.c
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc.
+ * Copyright (c) 2019 Intel Corp.
+ * All Rights Reserved.
+ */
+
+/*
+ * Synchronized lease exerciser
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <endian.h>
+#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*/
+#define PLATFORM_CLEANUP()  /*no-op*/
+#define LL                  "ll"
+
+extern int h_errno;
+
+#define inet_aton(STRING, INADDRP) \
+    (((INADDRP)->s_addr = inet_addr(STRING)) == -1 ? 0 : 1)
+
+/* this assumes 32 bit pointers */
+#define PTR_TO_U64(P) ((unsigned __int64)(unsigned int)(P))
+#define U64_TO_PTR(T,U) ((T)(void *)(unsigned int)(U))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define bswap_uint16(x)         (uint16_t)bswap_16(x)
+#define bswap_uint32(x)         (uint32_t)bswap_32(x)
+#define bswap_uint64(x)         (uint64_t)bswap_64(x)
+#else
+#define bswap_uint16(x)         x
+#define bswap_uint32(x)         x
+#define bswap_uint64(x)         x
+#endif
+
+#define SOCKET              int
+#define SOCKET_READ         read
+#define SOCKET_WRITE        write
+#define SOCKET_CLOSE(S)     (close(S))
+#define INVALID_SOCKET      -1
+
+#define O_BINARY            0
+       
+#define HANDLE              int
+#define INVALID_HANDLE      -1
+#define SEEK(H, O)          (lseek(H, O, SEEK_SET))
+#define READ(H, B, L)       (read(H, B, L))
+#define WRITE(H, B, L)      (write(H, B, L))
+#define CLOSE(H)            (close(H))
+
+#define RAND()              (rand())
+#define SRAND(s)            (srand(s))
+#define SLEEP(s)            (sleep(s))
+
+#define MIN(A,B)            (((A)<(B))?(A):(B))
+#define MAX(A,B)            (((A)>(B))?(A):(B))
+
+#define ALLOC_ALIGNED(S)    (memalign(65536, S)) 
+#define FREE_ALIGNED(P)     (free(P)) 
+
+static char	*prog;
+static char	*filename = 0;
+static int	debug = 0;
+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       */
+static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
+
+#define 	CMD_SETLEASE	0
+#define 	CMD_GETLEASE	1
+#define		CMD_CLOSE	2
+#define		CMD_OPEN	3
+#define		CMD_SIGIO	4
+#define		CMD_WAIT_SIGIO	5
+
+#define		PASS 	1
+#define		FAIL	0
+
+#define		SERVER	0
+#define		CLIENT	1
+
+#define		TEST_NUM	0
+#define		COMMAND		1
+#define		ARG		2
+#define		FLAGS		ARG /* Arg and flags are used the same */
+#define		RESULT		3
+#define		WHO		4
+
+static char *get_cmd_str(int cmd)
+{
+	switch (cmd) {
+		case CMD_SETLEASE: return "Set Lease"; break;
+		case CMD_GETLEASE: return "Get Lease"; break;
+		case CMD_CLOSE:    return "Close"; break;
+		case CMD_OPEN:     return "Open"; break;
+	}
+	return "unknown";
+}
+
+/*
+ * When adding tests be sure to add to both the descriptions AND tests array. 
+ * Also, be sure to undo whatever is set for each test (eg unlock any locks)
+ * There is no need to have a matching client command for each server command
+ * (or vice versa)
+ */
+
+char *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 */"No SIGIO is sent with read lock on read open",
+    /*  8 */"Read lease gets SIGIO on write open",
+};
+
+static int64_t tests[][5] =
+	/*	test #	Cmd		Arg|Flags	Result		Server|Client */
+	{ 	
+	/* Various simple tests exercising the list */
+
+/* SECTION 1: Simple verification of being able to take leases */
+	/* Take Read Lease */
+		{1,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+		{1,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
+		{1,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{1,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
+		{1,	CMD_SETLEASE,	F_RDLCK,	PASS,		SERVER	},
+		{1,	CMD_GETLEASE,	F_RDLCK,	PASS,		SERVER	},
+		{1,	CMD_SETLEASE,	F_UNLCK,	PASS,		SERVER	},
+		{1,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{1,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+	/* Take Write Lease */
+		{2,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
+		{2,	CMD_SETLEASE,	F_WRLCK,	PASS,		SERVER	},
+		{2,	CMD_GETLEASE,	F_WRLCK,	PASS,		SERVER	},
+		{2,	CMD_SETLEASE,	F_UNLCK,	PASS,		SERVER	},
+		{2,	CMD_CLOSE,	0,		PASS,		SERVER	},
+	/* Fail Write Lease with other users */
+		{3,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT  },
+		{3,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
+		{3,	CMD_SETLEASE,	F_WRLCK,	FAIL,		SERVER	},
+		{3,	CMD_GETLEASE,	F_WRLCK,	FAIL,		SERVER	},
+		{3,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{3,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+	/* Fail Read Lease if opened for write */
+		{4,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
+		{4,	CMD_SETLEASE,	F_RDLCK,	FAIL,		SERVER	},
+		{4,	CMD_GETLEASE,	F_RDLCK,	FAIL,		SERVER	},
+		{4,	CMD_CLOSE,	0,		PASS,		SERVER	},
+
+/* SECTION 2: Proper SIGIO notifications */
+	/* Get SIGIO when read lease is broken by write */
+		{5,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
+		{5,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{5,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{5,	CMD_SIGIO,	0,		PASS,		CLIENT	},
+		{5,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
+		{5,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
+		{5,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{5,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+
+	/* Get SIGIO when write lease is broken by read */
+		{6,	CMD_OPEN,	O_RDWR,		PASS,		CLIENT	},
+		{6,	CMD_SETLEASE,	F_WRLCK,	PASS,		CLIENT	},
+		{6,	CMD_GETLEASE,	F_WRLCK,	PASS,		CLIENT	},
+		{6,	CMD_SIGIO,	0,		PASS,		CLIENT	},
+		{6,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
+		{6,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
+		{6,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{6,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+
+	/* Don't get SIGIO when read lease is taken by read */
+		{7,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
+		{7,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{7,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{7,	CMD_SIGIO,	0,		PASS,		CLIENT	},
+		{7,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
+		{7,	CMD_WAIT_SIGIO,	5,		FAIL,		CLIENT	},
+		{7,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{7,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+
+	/* Get SIGIO when Read lease is broken by Write */
+		{8,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
+		{8,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{8,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
+		{8,	CMD_SIGIO,	0,		PASS,		CLIENT	},
+		{8,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
+		{8,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
+		{8,	CMD_CLOSE,	0,		PASS,		SERVER	},
+		{8,	CMD_CLOSE,	0,		PASS,		CLIENT	},
+
+	/* indicate end of array */
+	/* Must have an end for the SERVER and one for the CLIENT */
+		{0,0,0,0,SERVER},
+		{0,0,0,0,CLIENT}
+	};
+
+static struct {
+    int32_t		index;
+    int32_t		test;
+    int32_t		command;
+    int32_t		arg;
+    int32_t		result;
+    int32_t		error;
+} ctl;
+
+
+void
+usage(void)
+{
+    fprintf(stderr, "Usage: %s [options] sharedfile\n\
+\n\
+options:\n\
+  -p port	TCP/IP port number for client-server communication\n\
+  -d		enable debug tracing\n\
+  -n #		test number to run\n\
+  -h host	run as client and connect to server on remote host\n\
+  		[default run as server]\n", prog);
+    exit(1);
+}
+
+#define INIT_BUFSZ 512 
+
+void
+initialize(HANDLE fd)
+{
+    char*	ibuf;
+    int		j=0;
+    int		nwrite;
+    int 	offset = 0;
+    int 	togo = FILE_SIZE;
+
+    ibuf = (char*)malloc(INIT_BUFSZ);
+    memset(ibuf, ':', INIT_BUFSZ);
+
+    SEEK(fd, 0L);
+    while (togo) {
+	offset+=j;
+	j = togo > INIT_BUFSZ ? INIT_BUFSZ : togo;
+
+	if ((nwrite = WRITE(fd, ibuf, j)) != j) {
+	    if (nwrite < 0)
+		perror("initialize write:");
+	    else
+		fprintf(stderr, "initialize: write() returns %d, not %d as expected\n", 
+                        nwrite, j);
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+	togo -= j;
+    }
+}
+
+void release_lease(int fd)
+{
+	int rc;
+
+	rc = fcntl(fd, F_SETLEASE, F_UNLCK);
+	if (debug && rc != 0) {
+		fprintf(stderr, "Failed to remove lease %d : %d %s\n",
+			rc, errno, strerror(errno));
+	}
+}
+
+void lease_break(int sig, siginfo_t *info, void *p)
+{
+    if (debug > 1) {
+	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)
+		return FAIL;
+
+	if (debug) {
+		fprintf(stderr, "Set '%s' sigaction on %d\n",
+			strsignal(SIGIO), fd);
+	}
+
+	rc = fcntl(fd, F_SETSIG, SIGIO);
+
+	return (rc == 0 ? PASS : FAIL);
+}
+
+int do_wait_sigio(int32_t time)
+{
+    if (time <= 0)
+	return FAIL;
+
+    while (!got_sigio && time--) {
+	sleep(1);
+    }
+
+    return (got_sigio ? PASS: FAIL);
+}
+
+int do_open(int flag)
+{
+    int flags = flag|O_CREAT|O_BINARY;
+
+    if(debug > 1) {
+	fprintf(stderr, "do_open %s 0x%x\n", filename, flags);
+    }
+
+    if ((f_fd = open(filename, flags, 0666)) == INVALID_HANDLE) {
+	perror("shared file create");
+	return FAIL;
+	/*NOTREACHED*/
+    }
+    return PASS;
+}
+
+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(debug > 1 && ret)
+	fprintf(stderr, "do_lease: ret = %d, errno = %d (%s)\n",
+		ret, errno, strerror(errno));
+
+    return(ret==0?PASS:FAIL);
+}
+
+int do_close(void)
+{	
+    if(debug > 1) {
+	fprintf(stderr, "do_close\n");
+    }
+
+    errno =0;
+    CLOSE(f_fd);
+    f_fd = INVALID_HANDLE;
+
+    saved_errno = errno;	    
+	
+    if (errno)
+	return FAIL;
+    return PASS;
+}
+
+static void init_ctl(int32_t index)
+{
+    ctl.test= (int32_t)tests[index][TEST_NUM];
+    ctl.command = (int32_t)tests[index][COMMAND];
+    ctl.arg = (int32_t)tests[index][ARG];
+    ctl.index = index;
+    ctl.result = (int32_t)tests[index][RESULT];
+    ctl.error = 0;
+}
+
+void
+send_ctl(void)
+{
+    int         nwrite;
+
+    if (debug > 1) {
+	fprintf(stderr, "send_ctl: test=%d, cmd=%d, arg=%d, index=%d, result=%d, error=%d\n", 
+                ctl.test, ctl.command, ctl.arg, ctl.index, ctl.result, ctl.error);
+    }
+
+    ctl.test= bswap_uint32(ctl.test);
+    ctl.command = bswap_uint32(ctl.command);
+    ctl.arg = bswap_uint32(ctl.arg);
+    ctl.index = bswap_uint32(ctl.index);
+    ctl.result = bswap_uint32(ctl.result);
+    ctl.error = bswap_uint32(ctl.error);
+    nwrite = SOCKET_WRITE(c_fd, (char*)&ctl, sizeof(ctl));
+
+    ctl.test= bswap_uint32(ctl.test);
+    ctl.command = bswap_uint32(ctl.command);
+    ctl.arg = bswap_uint32(ctl.arg);
+    ctl.index= bswap_uint32(ctl.index);
+    ctl.result = bswap_uint32(ctl.result);
+    ctl.error= bswap_uint32(ctl.error);
+    if (nwrite != sizeof(ctl)) {
+        if (nwrite < 0)
+            perror("send_ctl: write");
+        else
+            fprintf(stderr, "send_ctl[%d]: write() returns %d, not %zu as expected\n", 
+                    ctl.test, nwrite, sizeof(ctl));
+        exit(1);
+        /*NOTREACHED*/
+    }
+}
+
+void recv_ctl(void)
+{
+    int         nread;
+
+again:
+    if ((nread = SOCKET_READ(c_fd, (char*)&ctl, sizeof(ctl))) != sizeof(ctl)) {
+        if (nread < 0) {
+	    if (errno == EINTR)
+		goto again;
+            perror("recv_ctl: read");
+        } 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 leasetest\n");
+	} 
+        exit(1);
+        /*NOTREACHED*/
+    }
+    ctl.test= bswap_uint32(ctl.test);
+    ctl.command = bswap_uint32(ctl.command);
+    ctl.arg = bswap_uint32(ctl.arg);
+    ctl.index= bswap_uint32(ctl.index);
+    ctl.result = bswap_uint32(ctl.result);
+    ctl.error= bswap_uint32(ctl.error);
+
+    if (debug > 1) {
+	fprintf(stderr, "recv_ctl: test=%d, cmd=%d arg=%d, index=%d, result=%d, error=%d\n", 
+                ctl.test, ctl.command, ctl.arg, ctl.index, ctl.result, ctl.error);
+    }
+}
+
+void
+cleanup(void)
+{
+    if (f_fd>=0)
+        CLOSE(f_fd);
+    
+    if (c_fd>=0)
+        SOCKET_CLOSE(c_fd);
+    
+    if (s_fd>=0)
+        SOCKET_CLOSE(s_fd);
+    
+    PLATFORM_CLEANUP();
+}
+
+int
+main(int argc, char *argv[])
+{
+    int		i, sts;
+    int		c;
+    struct sockaddr_in	myAddr;
+    struct linger	noLinger = {1, 0};
+    char	*host = NULL;
+    char	*endnum;
+    int		errflag = 0;
+    char	*p;
+    extern char	*optarg;
+    extern int	optind;
+    int fail_count = 0;; 
+    
+    atexit(cleanup);
+    
+    PLATFORM_INIT();
+
+    /* trim command name of leading directory components */
+    prog = argv[0];
+    for (p = prog; *p; p++) {
+	if (*p == '/')
+	    prog = p+1;
+    }
+
+    while ((c = getopt(argc, argv, "dn:h:p:?")) != EOF) {
+	switch (c) {
+
+	case 'd':	/* debug flag */
+	    debug++;
+	    break;
+
+	case 'h':	/* (server) hostname */
+	    server = 0;
+	    host = optarg;
+	    break;
+
+	case 'n':
+	    testnumber = atoi(optarg);
+	    break;
+
+	case 'p':	/* TCP/IP port */
+	    port = (int)strtol(optarg, &endnum, 10);
+	    if (*endnum != '\0') {
+		fprintf(stderr, "%s: -p argument must be a numeric\n", 
+                        prog);
+		exit(1);
+		/*NOTREACHED*/
+	    }
+	    break;
+
+	case '?':
+	default:
+	    errflag++;
+	    break;
+	}
+    }
+
+    if (errflag || optind != argc-1) {
+	usage();
+	/*NOTREACHED*/
+    }
+
+    filename=argv[optind];
+    if (debug)
+	fprintf(stderr, "Working on file : %s\n", filename);
+    if (do_open(O_RDWR) == FAIL)
+	exit(1);
+
+    setbuf(stderr, NULL);
+
+    if (server) {
+        int one = 1;
+        
+	s_fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (s_fd == INVALID_SOCKET) {
+	    perror("socket");
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+	if (setsockopt(s_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) < 0) {
+	    perror("setsockopt(nodelay)");
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+        if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one))<0) {
+            perror("setsockopt(reuseaddr)");
+            exit(1);
+            /*NOTREACHED*/
+        }
+#ifdef SO_REUSEPORT
+        if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one))<0) {
+            perror("setsockopt(reuseport)");
+            exit(1);
+            /*NOTREACHED*/
+        }
+#endif
+
+	memset(&myAddr, 0, sizeof(myAddr));
+	myAddr.sin_family = AF_INET;
+	myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+	myAddr.sin_port = htons((short)port);
+	sts = bind(s_fd, (struct sockaddr*)&myAddr, sizeof(myAddr));
+	if (sts < 0) {
+	    perror("bind");
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+
+	sts = listen(s_fd, 5);	/* Max. of 5 pending connection requests */
+	if (sts == -1) {
+	    perror("listen");
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+
+	if (port == 0) {
+	        socklen_t addr_len = sizeof(myAddr);
+
+		if (getsockname(s_fd, &myAddr, &addr_len)) {
+		    perror("getsockname");
+		    exit(1);
+		}
+
+		port = ntohs(myAddr.sin_port);
+	}
+
+	printf("server port: %d\n", port);
+	fflush(stdout);
+
+	c_fd = accept(s_fd, NULL, NULL);
+	if (c_fd == INVALID_SOCKET) {
+	    perror("accept");
+	    exit(1);
+	    /*NOTREACHED*/
+	}
+
+	if (debug) fprintf(stderr, "Client accepted\n");
+	SRAND(12345L);
+    }
+    else { /* Client initialization */
+        struct hostent  *servInfo;
+
+        if ((servInfo = gethostbyname(host)) == NULL) {
+	    printf("Couldn't get hostbyname for %s", host);
+	    if (h_errno == HOST_NOT_FOUND)
+		printf(": host not found");
+	    printf("\n");
+            exit(1);
+            /*NOTREACHED*/
+        }
+
+        c_fd = socket(AF_INET, SOCK_STREAM, 0);
+        if (c_fd == INVALID_SOCKET) {
+            perror("socket");
+            exit(1);
+            /*NOTREACHED*/
+        }
+        /* avoid 200 ms delay */
+        if (setsockopt(c_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)) < 0) {
+            perror("setsockopt(nodelay)");
+            exit(1);
+            /*NOTREACHED*/
+        }
+        /* Don't linger on close */
+        if (setsockopt(c_fd, SOL_SOCKET, SO_LINGER, (char *)&noLinger, sizeof(noLinger)) < 0) {
+            perror("setsockopt(nolinger)");
+            exit(1);
+            /*NOTREACHED*/
+        }
+
+	memset(&myAddr, 0, sizeof(myAddr));	/* Arrgh! &myAddr, not myAddr */
+	myAddr.sin_family = AF_INET;
+	memcpy(&myAddr.sin_addr, servInfo->h_addr, servInfo->h_length);
+	myAddr.sin_port = htons((short)port);
+
+	if (connect(c_fd, (struct sockaddr*)&myAddr, sizeof(myAddr)) < 0) {
+            perror("unable to connect");
+            fprintf(stderr, "Server might still initializing the shared file\n ");
+            exit(1);
+            /*NOTREACHED*/
+	}
+
+	if (debug) fprintf(stderr, "Connected to server\n");
+	SRAND(6789L);
+    }
+
+    if (server)
+	/* only server need do shared file */
+	initialize(f_fd);
+
+    /*
+     * TCP/IP connection to be established, safe to proceed.
+     *
+     * real work is in here ...
+     */
+    i = 0;
+{
+    int index = 0;
+    int end = 0;
+    int result = 0;
+    int last_test = 0;
+    int test_count = 0;
+    int fail_flag = 0;
+    while(!end) {
+	if (server) {
+	    if(testnumber > 0) {
+		last_test = testnumber - 1;
+		while(tests[index][TEST_NUM] != testnumber && tests[index][TEST_NUM] != 0) {
+		    index++;
+		}
+	    }
+	    /* If we have a server command, deal with it */
+	    if(tests[index][WHO] == SERVER) {
+		if(debug>1)
+		    fprintf(stderr, "Got a server command (%d)\n", index);
+		if(tests[index][TEST_NUM] == 0) {
+		    index++;
+		    continue;
+		} 
+		memset(&ctl, 0, sizeof(ctl));
+		ctl.test = tests[index][TEST_NUM];
+
+		if(tests[index][TEST_NUM] != 0) {
+		    switch(tests[index][COMMAND]) {
+			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_CLOSE:
+			    result = do_close();
+			    break;
+			case CMD_OPEN:
+			    result = do_open(tests[index][FLAGS]);
+			    break;
+			default:
+			    result = !tests[index][RESULT];
+			    break;
+		    }
+		    if( result != tests[index][RESULT]) {
+			fail_flag++;
+			/* We have a failure */
+			if(debug)
+			    fprintf(stderr, "Server failure in test %d, while %sing - err = %d:%s\n", 
+					ctl.test,
+					get_cmd_str(tests[index][COMMAND]),
+					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]);
+		    }
+		}
+	    /* else send it off to the client */
+	    } else if (tests[index][WHO] == CLIENT) {
+		if(tests[index][TEST_NUM] == 0)
+		    end=1;
+
+		/* get the client to do something */
+		init_ctl(index);
+		if(debug > 1)
+		    fprintf(stderr, "Sending command to client (%d) - %s\n", 
+					index,
+					get_cmd_str(ctl.command));
+		send_ctl();
+
+		if(!end) {
+		    /* Get the clients response */
+		    recv_ctl();
+		    /* this is the whether the test passed or failed,
+		     * not what the command returned */
+		    if( ctl.result == FAIL ) {
+			fail_flag++;
+			if(debug)
+			    fprintf(stderr, "Client failure in test %d, while %sing - err = %d:%s\n",
+					ctl.test, get_cmd_str(ctl.command),
+					ctl.error, strerror(ctl.error));
+			fprintf(stderr, "Client failure in %d:%s\n",
+					ctl.test, descriptions[ctl.test - 1]);
+		    }
+		}
+	    }
+	    if (debug > 1) {
+		fprintf(stderr, "server sleeping ...\n");
+		SLEEP(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];
+	    }
+		
+	    index++;
+	} else { /* CLIENT */
+	    if(debug > 2)
+		fprintf(stderr,"client: waiting...\n");
+	    /* wait for the server to do something */
+	    recv_ctl();
+
+	    /* check for a client command */
+	    index = ctl.index;
+	    if (tests[index][WHO] != CLIENT) { 
+		fprintf(stderr, "not a client command index (%d)\n", index);
+		exit(1);
+	    }
+		
+	    if(ctl.test == 0) {
+		end = 1;
+		break;
+	    }
+
+	    switch(ctl.command) {
+		case CMD_SETLEASE:
+		    result = do_lease(F_SETLEASE, ctl.arg, 0);
+		    break;
+		case CMD_GETLEASE:
+		    result = do_lease(F_GETLEASE, ctl.arg, ctl.arg);
+		    break;
+		case CMD_CLOSE:
+		    result = do_close();
+		    break;
+		case CMD_OPEN:
+		    result = do_open(tests[index][FLAGS]);
+		    break;
+		case CMD_SIGIO:
+		    result = do_setup_sigio(f_fd);
+		    break;
+		case CMD_WAIT_SIGIO:
+		    result = do_wait_sigio(ctl.arg);
+		    break;
+	    }
+	    if( result != ctl.result ) {
+		if(debug)
+		    fprintf(stderr,"Got %d, wanted %d\n",
+				result, ctl.result);
+		ctl.result = FAIL;
+		ctl.error = saved_errno;
+		fail_count++;
+	    } else {
+		ctl.result = PASS;
+		ctl.error = 0;
+	    }
+	    if(debug > 2)
+		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++;
+		last_test = tests[index][TEST_NUM];
+	    }
+	}
+    }
+    if(server)
+	printf("%d tests run, %d failed\n", test_count, fail_count);
+}
+    
+    exit(fail_count);
+    /*NOTREACHED*/
+}
+
+
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH V2 2/2] generic: Add Lease testing
  2019-09-05 17:30 [PATCH V2 0/2] Add lease testing ira.weiny
  2019-09-05 17:30 ` [PATCH V2 1/2] src/leasetest: Add lease test executable ira.weiny
@ 2019-09-05 17:30 ` ira.weiny
  2019-09-08 14:33   ` Eryu Guan
  1 sibling, 1 reply; 7+ messages in thread
From: ira.weiny @ 2019-09-05 17:30 UTC (permalink / raw)
  To: fstests
  Cc: john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton, Ira Weiny

From: Ira Weiny <ira.weiny@intel.com>

Now that we have a leasetest executable add in a lease test script.

Signed-off-by: Ira Weiny <ira.weiny@intel.com>
---
 tests/generic/567     | 100 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/567.out |   2 +
 tests/generic/group   |   1 +
 3 files changed, 103 insertions(+)
 create mode 100755 tests/generic/567
 create mode 100644 tests/generic/567.out

diff --git a/tests/generic/567 b/tests/generic/567
new file mode 100755
index 000000000000..3ef99c66b207
--- /dev/null
+++ b/tests/generic/567
@@ -0,0 +1,100 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019 Intel Corp.  All Rights Reserved.
+#
+# FS QA Test 567
+#
+# Test Leases
+#
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+#debug="-d -d -d"
+debug=""
+
+TESTFILE=$TEST_DIR/lease_file
+
+_cleanup()
+{
+	kill $leasetest_pid2 > /dev/null 2>&1
+	kill $leasetest_pid1 > /dev/null 2>&1
+	rm -f $TESTFILE
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+_require_test
+_require_test_fcntl_advisory_locks
+
+# set up log files
+SERVER_LOG=$TEST_DIR/server.out
+CLIENT_LOG=$TEST_DIR/client.out
+rm -f $SERVER_LOG
+touch $SERVER_LOG
+rm -f $CLIENT_LOG
+touch $CLIENT_LOG
+
+if [ "$debug" != "" ]; then
+	echo "Test directory : $TEST_DIR"
+	echo "Result directory : $RESULT_DIR"
+	echo "Client log : $CLIENT_LOG"
+	echo "Server log : $SERVER_LOG"
+fi
+
+touch $TESTFILE
+
+# Start the server
+src/leasetest $debug $TESTFILE 2>&1 > $SERVER_LOG &
+leasetest_pid1=$!
+
+timeout=30
+while [ $timeout -gt 0 ]; do
+	sleep 1
+
+	PORT=$(cat $SERVER_LOG | grep "^server port: " | awk '{print $3}')
+	if [ -n "$PORT" ]; then
+		break
+	fi
+
+	# check the existence of server process
+	if ! kill -s 0 $leasetest_pid1 >/dev/null 2>&1; then
+		echo "Server died abnormally"
+		exit 1
+	fi
+
+	let timeout=timeout-1
+done
+
+if [ -z "$PORT" ]; then
+	echo "Could not get server port"
+	exit 1
+fi
+
+# Start the client
+src/leasetest $debug -p $PORT -h localhost $TESTFILE 2>&1 > $CLIENT_LOG
+leasetest_pid2=$!
+result=$?
+if [ $result -eq 0 ]; then
+	echo success!
+	status=0
+else
+	echo "Client reported failure ($result)"
+	cat $TEST_DIR/*.out
+fi
+
+if [ "$debug" != "" ]; then
+	echo "End file details"
+	ls -la $TESTFILE
+fi
+
+exit
diff --git a/tests/generic/567.out b/tests/generic/567.out
new file mode 100644
index 000000000000..dada957258fc
--- /dev/null
+++ b/tests/generic/567.out
@@ -0,0 +1,2 @@
+QA output created by 567
+success!
diff --git a/tests/generic/group b/tests/generic/group
index d26c080fde30..1a48c93ff3bb 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -569,3 +569,4 @@
 564 auto quick copy_range
 565 auto quick copy_range
 566 auto quick quota metadata
+567 auto quick locks
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH V2 1/2] src/leasetest: Add lease test executable
  2019-09-05 17:30 ` [PATCH V2 1/2] src/leasetest: Add lease test executable ira.weiny
@ 2019-09-08 14:17   ` Eryu Guan
  0 siblings, 0 replies; 7+ messages in thread
From: Eryu Guan @ 2019-09-08 14:17 UTC (permalink / raw)
  To: ira.weiny
  Cc: fstests, john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton

On Thu, Sep 05, 2019 at 10:30:25AM -0700, ira.weiny@intel.com wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> locktest uses a pattern which is suitable for lease testing so this
> patch copies locktest as a framework and modifies it for lease testing.

Looks like most of the framework is duplicated and only the test cases
are updated to test file lease. I'd like to extend locktest.c to test
file lease as well, e.g. using the expected 'tests' array to test lock
or lease based on the given option.

Thanks,
Eryu

> 
> Initial tests are built based on the existing fcntl() man page declared
> functionality.
> 
> Acked-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> ---
> Changes:
> 	Adds tests which were covered here:
> 	https://marc.info/?l=fstests&m=156690934513424&w=2
> 
>  src/Makefile    |   2 +-
>  src/leasetest.c | 859 ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 860 insertions(+), 1 deletion(-)
>  create mode 100644 src/leasetest.c
> 
> diff --git a/src/Makefile b/src/Makefile
> index c4fcf370431f..c34b4add6695 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -28,7 +28,7 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
>  	attr-list-by-handle-cursor-test listxattr dio-interleaved t_dir_type \
>  	dio-invalidate-cache stat_test t_encrypted_d_revalidate \
>  	attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
> -	fscrypt-crypt-util bulkstat_null_ocount
> +	fscrypt-crypt-util bulkstat_null_ocount leasetest
>  
>  SUBDIRS = log-writes perf
>  
> diff --git a/src/leasetest.c b/src/leasetest.c
> new file mode 100644
> index 000000000000..729aca851493
> --- /dev/null
> +++ b/src/leasetest.c
> @@ -0,0 +1,859 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2000-2003 Silicon Graphics, Inc.
> + * Copyright (c) 2019 Intel Corp.
> + * All Rights Reserved.
> + */
> +
> +/*
> + * Synchronized lease exerciser
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <malloc.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <netinet/tcp.h>
> +
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <netdb.h>
> +#include <endian.h>
> +#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*/
> +#define PLATFORM_CLEANUP()  /*no-op*/
> +#define LL                  "ll"
> +
> +extern int h_errno;
> +
> +#define inet_aton(STRING, INADDRP) \
> +    (((INADDRP)->s_addr = inet_addr(STRING)) == -1 ? 0 : 1)
> +
> +/* this assumes 32 bit pointers */
> +#define PTR_TO_U64(P) ((unsigned __int64)(unsigned int)(P))
> +#define U64_TO_PTR(T,U) ((T)(void *)(unsigned int)(U))
> +
> +#if __BYTE_ORDER == __LITTLE_ENDIAN
> +#define bswap_uint16(x)         (uint16_t)bswap_16(x)
> +#define bswap_uint32(x)         (uint32_t)bswap_32(x)
> +#define bswap_uint64(x)         (uint64_t)bswap_64(x)
> +#else
> +#define bswap_uint16(x)         x
> +#define bswap_uint32(x)         x
> +#define bswap_uint64(x)         x
> +#endif
> +
> +#define SOCKET              int
> +#define SOCKET_READ         read
> +#define SOCKET_WRITE        write
> +#define SOCKET_CLOSE(S)     (close(S))
> +#define INVALID_SOCKET      -1
> +
> +#define O_BINARY            0
> +       
> +#define HANDLE              int
> +#define INVALID_HANDLE      -1
> +#define SEEK(H, O)          (lseek(H, O, SEEK_SET))
> +#define READ(H, B, L)       (read(H, B, L))
> +#define WRITE(H, B, L)      (write(H, B, L))
> +#define CLOSE(H)            (close(H))
> +
> +#define RAND()              (rand())
> +#define SRAND(s)            (srand(s))
> +#define SLEEP(s)            (sleep(s))
> +
> +#define MIN(A,B)            (((A)<(B))?(A):(B))
> +#define MAX(A,B)            (((A)>(B))?(A):(B))
> +
> +#define ALLOC_ALIGNED(S)    (memalign(65536, S)) 
> +#define FREE_ALIGNED(P)     (free(P)) 
> +
> +static char	*prog;
> +static char	*filename = 0;
> +static int	debug = 0;
> +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       */
> +static HANDLE	f_fd = INVALID_HANDLE;	/* shared file      */
> +
> +#define 	CMD_SETLEASE	0
> +#define 	CMD_GETLEASE	1
> +#define		CMD_CLOSE	2
> +#define		CMD_OPEN	3
> +#define		CMD_SIGIO	4
> +#define		CMD_WAIT_SIGIO	5
> +
> +#define		PASS 	1
> +#define		FAIL	0
> +
> +#define		SERVER	0
> +#define		CLIENT	1
> +
> +#define		TEST_NUM	0
> +#define		COMMAND		1
> +#define		ARG		2
> +#define		FLAGS		ARG /* Arg and flags are used the same */
> +#define		RESULT		3
> +#define		WHO		4
> +
> +static char *get_cmd_str(int cmd)
> +{
> +	switch (cmd) {
> +		case CMD_SETLEASE: return "Set Lease"; break;
> +		case CMD_GETLEASE: return "Get Lease"; break;
> +		case CMD_CLOSE:    return "Close"; break;
> +		case CMD_OPEN:     return "Open"; break;
> +	}
> +	return "unknown";
> +}
> +
> +/*
> + * When adding tests be sure to add to both the descriptions AND tests array. 
> + * Also, be sure to undo whatever is set for each test (eg unlock any locks)
> + * There is no need to have a matching client command for each server command
> + * (or vice versa)
> + */
> +
> +char *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 */"No SIGIO is sent with read lock on read open",
> +    /*  8 */"Read lease gets SIGIO on write open",
> +};
> +
> +static int64_t tests[][5] =
> +	/*	test #	Cmd		Arg|Flags	Result		Server|Client */
> +	{ 	
> +	/* Various simple tests exercising the list */
> +
> +/* SECTION 1: Simple verification of being able to take leases */
> +	/* Take Read Lease */
> +		{1,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +		{1,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
> +		{1,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{1,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
> +		{1,	CMD_SETLEASE,	F_RDLCK,	PASS,		SERVER	},
> +		{1,	CMD_GETLEASE,	F_RDLCK,	PASS,		SERVER	},
> +		{1,	CMD_SETLEASE,	F_UNLCK,	PASS,		SERVER	},
> +		{1,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{1,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +	/* Take Write Lease */
> +		{2,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
> +		{2,	CMD_SETLEASE,	F_WRLCK,	PASS,		SERVER	},
> +		{2,	CMD_GETLEASE,	F_WRLCK,	PASS,		SERVER	},
> +		{2,	CMD_SETLEASE,	F_UNLCK,	PASS,		SERVER	},
> +		{2,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +	/* Fail Write Lease with other users */
> +		{3,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT  },
> +		{3,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
> +		{3,	CMD_SETLEASE,	F_WRLCK,	FAIL,		SERVER	},
> +		{3,	CMD_GETLEASE,	F_WRLCK,	FAIL,		SERVER	},
> +		{3,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{3,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +	/* Fail Read Lease if opened for write */
> +		{4,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
> +		{4,	CMD_SETLEASE,	F_RDLCK,	FAIL,		SERVER	},
> +		{4,	CMD_GETLEASE,	F_RDLCK,	FAIL,		SERVER	},
> +		{4,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +
> +/* SECTION 2: Proper SIGIO notifications */
> +	/* Get SIGIO when read lease is broken by write */
> +		{5,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
> +		{5,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{5,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{5,	CMD_SIGIO,	0,		PASS,		CLIENT	},
> +		{5,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
> +		{5,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
> +		{5,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{5,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +
> +	/* Get SIGIO when write lease is broken by read */
> +		{6,	CMD_OPEN,	O_RDWR,		PASS,		CLIENT	},
> +		{6,	CMD_SETLEASE,	F_WRLCK,	PASS,		CLIENT	},
> +		{6,	CMD_GETLEASE,	F_WRLCK,	PASS,		CLIENT	},
> +		{6,	CMD_SIGIO,	0,		PASS,		CLIENT	},
> +		{6,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
> +		{6,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
> +		{6,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{6,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +
> +	/* Don't get SIGIO when read lease is taken by read */
> +		{7,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
> +		{7,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{7,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{7,	CMD_SIGIO,	0,		PASS,		CLIENT	},
> +		{7,	CMD_OPEN,	O_RDONLY,	PASS,		SERVER	},
> +		{7,	CMD_WAIT_SIGIO,	5,		FAIL,		CLIENT	},
> +		{7,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{7,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +
> +	/* Get SIGIO when Read lease is broken by Write */
> +		{8,	CMD_OPEN,	O_RDONLY,	PASS,		CLIENT	},
> +		{8,	CMD_SETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{8,	CMD_GETLEASE,	F_RDLCK,	PASS,		CLIENT	},
> +		{8,	CMD_SIGIO,	0,		PASS,		CLIENT	},
> +		{8,	CMD_OPEN,	O_RDWR,		PASS,		SERVER	},
> +		{8,	CMD_WAIT_SIGIO,	5,		PASS,		CLIENT	},
> +		{8,	CMD_CLOSE,	0,		PASS,		SERVER	},
> +		{8,	CMD_CLOSE,	0,		PASS,		CLIENT	},
> +
> +	/* indicate end of array */
> +	/* Must have an end for the SERVER and one for the CLIENT */
> +		{0,0,0,0,SERVER},
> +		{0,0,0,0,CLIENT}
> +	};
> +
> +static struct {
> +    int32_t		index;
> +    int32_t		test;
> +    int32_t		command;
> +    int32_t		arg;
> +    int32_t		result;
> +    int32_t		error;
> +} ctl;
> +
> +
> +void
> +usage(void)
> +{
> +    fprintf(stderr, "Usage: %s [options] sharedfile\n\
> +\n\
> +options:\n\
> +  -p port	TCP/IP port number for client-server communication\n\
> +  -d		enable debug tracing\n\
> +  -n #		test number to run\n\
> +  -h host	run as client and connect to server on remote host\n\
> +  		[default run as server]\n", prog);
> +    exit(1);
> +}
> +
> +#define INIT_BUFSZ 512 
> +
> +void
> +initialize(HANDLE fd)
> +{
> +    char*	ibuf;
> +    int		j=0;
> +    int		nwrite;
> +    int 	offset = 0;
> +    int 	togo = FILE_SIZE;
> +
> +    ibuf = (char*)malloc(INIT_BUFSZ);
> +    memset(ibuf, ':', INIT_BUFSZ);
> +
> +    SEEK(fd, 0L);
> +    while (togo) {
> +	offset+=j;
> +	j = togo > INIT_BUFSZ ? INIT_BUFSZ : togo;
> +
> +	if ((nwrite = WRITE(fd, ibuf, j)) != j) {
> +	    if (nwrite < 0)
> +		perror("initialize write:");
> +	    else
> +		fprintf(stderr, "initialize: write() returns %d, not %d as expected\n", 
> +                        nwrite, j);
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +	togo -= j;
> +    }
> +}
> +
> +void release_lease(int fd)
> +{
> +	int rc;
> +
> +	rc = fcntl(fd, F_SETLEASE, F_UNLCK);
> +	if (debug && rc != 0) {
> +		fprintf(stderr, "Failed to remove lease %d : %d %s\n",
> +			rc, errno, strerror(errno));
> +	}
> +}
> +
> +void lease_break(int sig, siginfo_t *info, void *p)
> +{
> +    if (debug > 1) {
> +	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)
> +		return FAIL;
> +
> +	if (debug) {
> +		fprintf(stderr, "Set '%s' sigaction on %d\n",
> +			strsignal(SIGIO), fd);
> +	}
> +
> +	rc = fcntl(fd, F_SETSIG, SIGIO);
> +
> +	return (rc == 0 ? PASS : FAIL);
> +}
> +
> +int do_wait_sigio(int32_t time)
> +{
> +    if (time <= 0)
> +	return FAIL;
> +
> +    while (!got_sigio && time--) {
> +	sleep(1);
> +    }
> +
> +    return (got_sigio ? PASS: FAIL);
> +}
> +
> +int do_open(int flag)
> +{
> +    int flags = flag|O_CREAT|O_BINARY;
> +
> +    if(debug > 1) {
> +	fprintf(stderr, "do_open %s 0x%x\n", filename, flags);
> +    }
> +
> +    if ((f_fd = open(filename, flags, 0666)) == INVALID_HANDLE) {
> +	perror("shared file create");
> +	return FAIL;
> +	/*NOTREACHED*/
> +    }
> +    return PASS;
> +}
> +
> +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(debug > 1 && ret)
> +	fprintf(stderr, "do_lease: ret = %d, errno = %d (%s)\n",
> +		ret, errno, strerror(errno));
> +
> +    return(ret==0?PASS:FAIL);
> +}
> +
> +int do_close(void)
> +{	
> +    if(debug > 1) {
> +	fprintf(stderr, "do_close\n");
> +    }
> +
> +    errno =0;
> +    CLOSE(f_fd);
> +    f_fd = INVALID_HANDLE;
> +
> +    saved_errno = errno;	    
> +	
> +    if (errno)
> +	return FAIL;
> +    return PASS;
> +}
> +
> +static void init_ctl(int32_t index)
> +{
> +    ctl.test= (int32_t)tests[index][TEST_NUM];
> +    ctl.command = (int32_t)tests[index][COMMAND];
> +    ctl.arg = (int32_t)tests[index][ARG];
> +    ctl.index = index;
> +    ctl.result = (int32_t)tests[index][RESULT];
> +    ctl.error = 0;
> +}
> +
> +void
> +send_ctl(void)
> +{
> +    int         nwrite;
> +
> +    if (debug > 1) {
> +	fprintf(stderr, "send_ctl: test=%d, cmd=%d, arg=%d, index=%d, result=%d, error=%d\n", 
> +                ctl.test, ctl.command, ctl.arg, ctl.index, ctl.result, ctl.error);
> +    }
> +
> +    ctl.test= bswap_uint32(ctl.test);
> +    ctl.command = bswap_uint32(ctl.command);
> +    ctl.arg = bswap_uint32(ctl.arg);
> +    ctl.index = bswap_uint32(ctl.index);
> +    ctl.result = bswap_uint32(ctl.result);
> +    ctl.error = bswap_uint32(ctl.error);
> +    nwrite = SOCKET_WRITE(c_fd, (char*)&ctl, sizeof(ctl));
> +
> +    ctl.test= bswap_uint32(ctl.test);
> +    ctl.command = bswap_uint32(ctl.command);
> +    ctl.arg = bswap_uint32(ctl.arg);
> +    ctl.index= bswap_uint32(ctl.index);
> +    ctl.result = bswap_uint32(ctl.result);
> +    ctl.error= bswap_uint32(ctl.error);
> +    if (nwrite != sizeof(ctl)) {
> +        if (nwrite < 0)
> +            perror("send_ctl: write");
> +        else
> +            fprintf(stderr, "send_ctl[%d]: write() returns %d, not %zu as expected\n", 
> +                    ctl.test, nwrite, sizeof(ctl));
> +        exit(1);
> +        /*NOTREACHED*/
> +    }
> +}
> +
> +void recv_ctl(void)
> +{
> +    int         nread;
> +
> +again:
> +    if ((nread = SOCKET_READ(c_fd, (char*)&ctl, sizeof(ctl))) != sizeof(ctl)) {
> +        if (nread < 0) {
> +	    if (errno == EINTR)
> +		goto again;
> +            perror("recv_ctl: read");
> +        } 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 leasetest\n");
> +	} 
> +        exit(1);
> +        /*NOTREACHED*/
> +    }
> +    ctl.test= bswap_uint32(ctl.test);
> +    ctl.command = bswap_uint32(ctl.command);
> +    ctl.arg = bswap_uint32(ctl.arg);
> +    ctl.index= bswap_uint32(ctl.index);
> +    ctl.result = bswap_uint32(ctl.result);
> +    ctl.error= bswap_uint32(ctl.error);
> +
> +    if (debug > 1) {
> +	fprintf(stderr, "recv_ctl: test=%d, cmd=%d arg=%d, index=%d, result=%d, error=%d\n", 
> +                ctl.test, ctl.command, ctl.arg, ctl.index, ctl.result, ctl.error);
> +    }
> +}
> +
> +void
> +cleanup(void)
> +{
> +    if (f_fd>=0)
> +        CLOSE(f_fd);
> +    
> +    if (c_fd>=0)
> +        SOCKET_CLOSE(c_fd);
> +    
> +    if (s_fd>=0)
> +        SOCKET_CLOSE(s_fd);
> +    
> +    PLATFORM_CLEANUP();
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> +    int		i, sts;
> +    int		c;
> +    struct sockaddr_in	myAddr;
> +    struct linger	noLinger = {1, 0};
> +    char	*host = NULL;
> +    char	*endnum;
> +    int		errflag = 0;
> +    char	*p;
> +    extern char	*optarg;
> +    extern int	optind;
> +    int fail_count = 0;; 
> +    
> +    atexit(cleanup);
> +    
> +    PLATFORM_INIT();
> +
> +    /* trim command name of leading directory components */
> +    prog = argv[0];
> +    for (p = prog; *p; p++) {
> +	if (*p == '/')
> +	    prog = p+1;
> +    }
> +
> +    while ((c = getopt(argc, argv, "dn:h:p:?")) != EOF) {
> +	switch (c) {
> +
> +	case 'd':	/* debug flag */
> +	    debug++;
> +	    break;
> +
> +	case 'h':	/* (server) hostname */
> +	    server = 0;
> +	    host = optarg;
> +	    break;
> +
> +	case 'n':
> +	    testnumber = atoi(optarg);
> +	    break;
> +
> +	case 'p':	/* TCP/IP port */
> +	    port = (int)strtol(optarg, &endnum, 10);
> +	    if (*endnum != '\0') {
> +		fprintf(stderr, "%s: -p argument must be a numeric\n", 
> +                        prog);
> +		exit(1);
> +		/*NOTREACHED*/
> +	    }
> +	    break;
> +
> +	case '?':
> +	default:
> +	    errflag++;
> +	    break;
> +	}
> +    }
> +
> +    if (errflag || optind != argc-1) {
> +	usage();
> +	/*NOTREACHED*/
> +    }
> +
> +    filename=argv[optind];
> +    if (debug)
> +	fprintf(stderr, "Working on file : %s\n", filename);
> +    if (do_open(O_RDWR) == FAIL)
> +	exit(1);
> +
> +    setbuf(stderr, NULL);
> +
> +    if (server) {
> +        int one = 1;
> +        
> +	s_fd = socket(AF_INET, SOCK_STREAM, 0);
> +	if (s_fd == INVALID_SOCKET) {
> +	    perror("socket");
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +	if (setsockopt(s_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one)) < 0) {
> +	    perror("setsockopt(nodelay)");
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +        if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one))<0) {
> +            perror("setsockopt(reuseaddr)");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +#ifdef SO_REUSEPORT
> +        if (setsockopt(s_fd, SOL_SOCKET, SO_REUSEPORT, (char*)&one, sizeof(one))<0) {
> +            perror("setsockopt(reuseport)");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +#endif
> +
> +	memset(&myAddr, 0, sizeof(myAddr));
> +	myAddr.sin_family = AF_INET;
> +	myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
> +	myAddr.sin_port = htons((short)port);
> +	sts = bind(s_fd, (struct sockaddr*)&myAddr, sizeof(myAddr));
> +	if (sts < 0) {
> +	    perror("bind");
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +
> +	sts = listen(s_fd, 5);	/* Max. of 5 pending connection requests */
> +	if (sts == -1) {
> +	    perror("listen");
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +
> +	if (port == 0) {
> +	        socklen_t addr_len = sizeof(myAddr);
> +
> +		if (getsockname(s_fd, &myAddr, &addr_len)) {
> +		    perror("getsockname");
> +		    exit(1);
> +		}
> +
> +		port = ntohs(myAddr.sin_port);
> +	}
> +
> +	printf("server port: %d\n", port);
> +	fflush(stdout);
> +
> +	c_fd = accept(s_fd, NULL, NULL);
> +	if (c_fd == INVALID_SOCKET) {
> +	    perror("accept");
> +	    exit(1);
> +	    /*NOTREACHED*/
> +	}
> +
> +	if (debug) fprintf(stderr, "Client accepted\n");
> +	SRAND(12345L);
> +    }
> +    else { /* Client initialization */
> +        struct hostent  *servInfo;
> +
> +        if ((servInfo = gethostbyname(host)) == NULL) {
> +	    printf("Couldn't get hostbyname for %s", host);
> +	    if (h_errno == HOST_NOT_FOUND)
> +		printf(": host not found");
> +	    printf("\n");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +
> +        c_fd = socket(AF_INET, SOCK_STREAM, 0);
> +        if (c_fd == INVALID_SOCKET) {
> +            perror("socket");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +        /* avoid 200 ms delay */
> +        if (setsockopt(c_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)) < 0) {
> +            perror("setsockopt(nodelay)");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +        /* Don't linger on close */
> +        if (setsockopt(c_fd, SOL_SOCKET, SO_LINGER, (char *)&noLinger, sizeof(noLinger)) < 0) {
> +            perror("setsockopt(nolinger)");
> +            exit(1);
> +            /*NOTREACHED*/
> +        }
> +
> +	memset(&myAddr, 0, sizeof(myAddr));	/* Arrgh! &myAddr, not myAddr */
> +	myAddr.sin_family = AF_INET;
> +	memcpy(&myAddr.sin_addr, servInfo->h_addr, servInfo->h_length);
> +	myAddr.sin_port = htons((short)port);
> +
> +	if (connect(c_fd, (struct sockaddr*)&myAddr, sizeof(myAddr)) < 0) {
> +            perror("unable to connect");
> +            fprintf(stderr, "Server might still initializing the shared file\n ");
> +            exit(1);
> +            /*NOTREACHED*/
> +	}
> +
> +	if (debug) fprintf(stderr, "Connected to server\n");
> +	SRAND(6789L);
> +    }
> +
> +    if (server)
> +	/* only server need do shared file */
> +	initialize(f_fd);
> +
> +    /*
> +     * TCP/IP connection to be established, safe to proceed.
> +     *
> +     * real work is in here ...
> +     */
> +    i = 0;
> +{
> +    int index = 0;
> +    int end = 0;
> +    int result = 0;
> +    int last_test = 0;
> +    int test_count = 0;
> +    int fail_flag = 0;
> +    while(!end) {
> +	if (server) {
> +	    if(testnumber > 0) {
> +		last_test = testnumber - 1;
> +		while(tests[index][TEST_NUM] != testnumber && tests[index][TEST_NUM] != 0) {
> +		    index++;
> +		}
> +	    }
> +	    /* If we have a server command, deal with it */
> +	    if(tests[index][WHO] == SERVER) {
> +		if(debug>1)
> +		    fprintf(stderr, "Got a server command (%d)\n", index);
> +		if(tests[index][TEST_NUM] == 0) {
> +		    index++;
> +		    continue;
> +		} 
> +		memset(&ctl, 0, sizeof(ctl));
> +		ctl.test = tests[index][TEST_NUM];
> +
> +		if(tests[index][TEST_NUM] != 0) {
> +		    switch(tests[index][COMMAND]) {
> +			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_CLOSE:
> +			    result = do_close();
> +			    break;
> +			case CMD_OPEN:
> +			    result = do_open(tests[index][FLAGS]);
> +			    break;
> +			default:
> +			    result = !tests[index][RESULT];
> +			    break;
> +		    }
> +		    if( result != tests[index][RESULT]) {
> +			fail_flag++;
> +			/* We have a failure */
> +			if(debug)
> +			    fprintf(stderr, "Server failure in test %d, while %sing - err = %d:%s\n", 
> +					ctl.test,
> +					get_cmd_str(tests[index][COMMAND]),
> +					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]);
> +		    }
> +		}
> +	    /* else send it off to the client */
> +	    } else if (tests[index][WHO] == CLIENT) {
> +		if(tests[index][TEST_NUM] == 0)
> +		    end=1;
> +
> +		/* get the client to do something */
> +		init_ctl(index);
> +		if(debug > 1)
> +		    fprintf(stderr, "Sending command to client (%d) - %s\n", 
> +					index,
> +					get_cmd_str(ctl.command));
> +		send_ctl();
> +
> +		if(!end) {
> +		    /* Get the clients response */
> +		    recv_ctl();
> +		    /* this is the whether the test passed or failed,
> +		     * not what the command returned */
> +		    if( ctl.result == FAIL ) {
> +			fail_flag++;
> +			if(debug)
> +			    fprintf(stderr, "Client failure in test %d, while %sing - err = %d:%s\n",
> +					ctl.test, get_cmd_str(ctl.command),
> +					ctl.error, strerror(ctl.error));
> +			fprintf(stderr, "Client failure in %d:%s\n",
> +					ctl.test, descriptions[ctl.test - 1]);
> +		    }
> +		}
> +	    }
> +	    if (debug > 1) {
> +		fprintf(stderr, "server sleeping ...\n");
> +		SLEEP(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];
> +	    }
> +		
> +	    index++;
> +	} else { /* CLIENT */
> +	    if(debug > 2)
> +		fprintf(stderr,"client: waiting...\n");
> +	    /* wait for the server to do something */
> +	    recv_ctl();
> +
> +	    /* check for a client command */
> +	    index = ctl.index;
> +	    if (tests[index][WHO] != CLIENT) { 
> +		fprintf(stderr, "not a client command index (%d)\n", index);
> +		exit(1);
> +	    }
> +		
> +	    if(ctl.test == 0) {
> +		end = 1;
> +		break;
> +	    }
> +
> +	    switch(ctl.command) {
> +		case CMD_SETLEASE:
> +		    result = do_lease(F_SETLEASE, ctl.arg, 0);
> +		    break;
> +		case CMD_GETLEASE:
> +		    result = do_lease(F_GETLEASE, ctl.arg, ctl.arg);
> +		    break;
> +		case CMD_CLOSE:
> +		    result = do_close();
> +		    break;
> +		case CMD_OPEN:
> +		    result = do_open(tests[index][FLAGS]);
> +		    break;
> +		case CMD_SIGIO:
> +		    result = do_setup_sigio(f_fd);
> +		    break;
> +		case CMD_WAIT_SIGIO:
> +		    result = do_wait_sigio(ctl.arg);
> +		    break;
> +	    }
> +	    if( result != ctl.result ) {
> +		if(debug)
> +		    fprintf(stderr,"Got %d, wanted %d\n",
> +				result, ctl.result);
> +		ctl.result = FAIL;
> +		ctl.error = saved_errno;
> +		fail_count++;
> +	    } else {
> +		ctl.result = PASS;
> +		ctl.error = 0;
> +	    }
> +	    if(debug > 2)
> +		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++;
> +		last_test = tests[index][TEST_NUM];
> +	    }
> +	}
> +    }
> +    if(server)
> +	printf("%d tests run, %d failed\n", test_count, fail_count);
> +}
> +    
> +    exit(fail_count);
> +    /*NOTREACHED*/
> +}
> +
> +
> -- 
> 2.20.1
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2 2/2] generic: Add Lease testing
  2019-09-05 17:30 ` [PATCH V2 2/2] generic: Add Lease testing ira.weiny
@ 2019-09-08 14:33   ` Eryu Guan
  2019-09-09 15:33     ` Ira Weiny
  0 siblings, 1 reply; 7+ messages in thread
From: Eryu Guan @ 2019-09-08 14:33 UTC (permalink / raw)
  To: ira.weiny
  Cc: fstests, john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton

On Thu, Sep 05, 2019 at 10:30:26AM -0700, ira.weiny@intel.com wrote:
> From: Ira Weiny <ira.weiny@intel.com>
> 
> Now that we have a leasetest executable add in a lease test script.
> 
> Signed-off-by: Ira Weiny <ira.weiny@intel.com>

The new test could be merged with patch 1.

> ---
>  tests/generic/567     | 100 ++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/567.out |   2 +
>  tests/generic/group   |   1 +
>  3 files changed, 103 insertions(+)
>  create mode 100755 tests/generic/567
>  create mode 100644 tests/generic/567.out
> 
> diff --git a/tests/generic/567 b/tests/generic/567
> new file mode 100755
> index 000000000000..3ef99c66b207
> --- /dev/null
> +++ b/tests/generic/567
> @@ -0,0 +1,100 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2019 Intel Corp.  All Rights Reserved.
> +#
> +# FS QA Test 567
> +#
> +# Test Leases
> +#
> +seq=`basename $0`
> +seqres=$RESULT_DIR/$seq
> +echo "QA output created by $seq"
> +
> +here=`pwd`
> +tmp=/tmp/$$
> +status=1	# failure is the default!
> +trap "_cleanup; exit \$status" 0 1 2 3 15
> +#debug="-d -d -d"
> +debug=""

No need to define 'debug', if you need debug output, make 'debug' the
default and dump all debug output to $seqres.full file.

> +
> +TESTFILE=$TEST_DIR/lease_file
> +
> +_cleanup()
> +{
> +	kill $leasetest_pid2 > /dev/null 2>&1
> +	kill $leasetest_pid1 > /dev/null 2>&1
> +	rm -f $TESTFILE
> +}
> +
> +# get standard environment, filters and checks
> +. ./common/rc
> +. ./common/filter
> +
> +# real QA test starts here
> +_supported_fs generic
> +_supported_os Linux
> +_require_test
> +_require_test_fcntl_advisory_locks
> +
> +# set up log files
> +SERVER_LOG=$TEST_DIR/server.out
> +CLIENT_LOG=$TEST_DIR/client.out
> +rm -f $SERVER_LOG
> +touch $SERVER_LOG
> +rm -f $CLIENT_LOG
> +touch $CLIENT_LOG
> +
> +if [ "$debug" != "" ]; then
> +	echo "Test directory : $TEST_DIR"
> +	echo "Result directory : $RESULT_DIR"
> +	echo "Client log : $CLIENT_LOG"
> +	echo "Server log : $SERVER_LOG"
> +fi
> +
> +touch $TESTFILE
> +
> +# Start the server
> +src/leasetest $debug $TESTFILE 2>&1 > $SERVER_LOG &
> +leasetest_pid1=$!
> +
> +timeout=30
> +while [ $timeout -gt 0 ]; do
> +	sleep 1
> +
> +	PORT=$(cat $SERVER_LOG | grep "^server port: " | awk '{print $3}')
> +	if [ -n "$PORT" ]; then
> +		break
> +	fi
> +
> +	# check the existence of server process
> +	if ! kill -s 0 $leasetest_pid1 >/dev/null 2>&1; then
> +		echo "Server died abnormally"
> +		exit 1
> +	fi
> +
> +	let timeout=timeout-1
> +done
> +
> +if [ -z "$PORT" ]; then
> +	echo "Could not get server port"
> +	exit 1
> +fi

The whole "discovering server port" part in generic/131 has been patched
several times in the past to fix corner cases here and there, we'd
better to refactor this hunk into a common helper, instead of
duplicating the code.

> +
> +# Start the client
> +src/leasetest $debug -p $PORT -h localhost $TESTFILE 2>&1 > $CLIENT_LOG
> +leasetest_pid2=$!
> +result=$?
> +if [ $result -eq 0 ]; then
> +	echo success!
> +	status=0
> +else
> +	echo "Client reported failure ($result)"
> +	cat $TEST_DIR/*.out
> +fi
> +
> +if [ "$debug" != "" ]; then
> +	echo "End file details"
> +	ls -la $TESTFILE
> +fi
> +
> +exit

echo "Silence is golden"
src/leasetest ...
leasetest_pid2=$!
result=$?
if [ $result -ne 0 ]; then
	echo "Server log" >> $seqres.full
	cat $SERVER_LOG >> $seqres.full
	echo "Client log" >> $seqres.full
	cat $CLIENT_LOG >> $seqres.full
	_fail "Client reported failure ($result)"
fi
status=0
exit

_fail will output the failure message, which breaks the golden output,
and prompt user to check $seqres.full file for details.

Thanks,
Eryu

> diff --git a/tests/generic/567.out b/tests/generic/567.out
> new file mode 100644
> index 000000000000..dada957258fc
> --- /dev/null
> +++ b/tests/generic/567.out
> @@ -0,0 +1,2 @@
> +QA output created by 567
> +success!
> diff --git a/tests/generic/group b/tests/generic/group
> index d26c080fde30..1a48c93ff3bb 100644
> --- a/tests/generic/group
> +++ b/tests/generic/group
> @@ -569,3 +569,4 @@
>  564 auto quick copy_range
>  565 auto quick copy_range
>  566 auto quick quota metadata
> +567 auto quick locks
> -- 
> 2.20.1
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2 2/2] generic: Add Lease testing
  2019-09-08 14:33   ` Eryu Guan
@ 2019-09-09 15:33     ` Ira Weiny
  2019-09-10  2:29       ` Eryu Guan
  0 siblings, 1 reply; 7+ messages in thread
From: Ira Weiny @ 2019-09-09 15:33 UTC (permalink / raw)
  To: Eryu Guan
  Cc: fstests, john.hubbard, Dave Chinner, Jason Gunthorpe, Jan Kara,
	Dan Williams, Jeff Layton

On Sun, Sep 08, 2019 at 10:33:12PM +0800, Eryu Guan wrote:
> On Thu, Sep 05, 2019 at 10:30:26AM -0700, ira.weiny@intel.com wrote:
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > Now that we have a leasetest executable add in a lease test script.
> > 
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> The new test could be merged with patch 1.

Ok, I was not sure how best to submit this.  Now I know.
> 
> > ---
> >  tests/generic/567     | 100 ++++++++++++++++++++++++++++++++++++++++++
> >  tests/generic/567.out |   2 +
> >  tests/generic/group   |   1 +
> >  3 files changed, 103 insertions(+)
> >  create mode 100755 tests/generic/567
> >  create mode 100644 tests/generic/567.out
> > 
> > diff --git a/tests/generic/567 b/tests/generic/567
> > new file mode 100755
> > index 000000000000..3ef99c66b207
> > --- /dev/null
> > +++ b/tests/generic/567
> > @@ -0,0 +1,100 @@
> > +#! /bin/bash
> > +# SPDX-License-Identifier: GPL-2.0
> > +# Copyright (c) 2019 Intel Corp.  All Rights Reserved.
> > +#
> > +# FS QA Test 567
> > +#
> > +# Test Leases
> > +#
> > +seq=`basename $0`
> > +seqres=$RESULT_DIR/$seq
> > +echo "QA output created by $seq"
> > +
> > +here=`pwd`
> > +tmp=/tmp/$$
> > +status=1	# failure is the default!
> > +trap "_cleanup; exit \$status" 0 1 2 3 15
> > +#debug="-d -d -d"
> > +debug=""
> 
> No need to define 'debug', if you need debug output, make 'debug' the
> default and dump all debug output to $seqres.full file.

I'll check this out.  I'm new to the harness so I was not sure what got output
where.

FWIW I think that means we can simplify leasetest even more then.  Remove all
the "debug" flags and leave stderr for error condition details.

Anyway, your comment WRT leasetest is that you would like to see locktest
"modernized" to include lease testing?  Is that correct?  If so, would you like
me to make these script modifications to 131?  Or would you like to see 2
"modes" of locktest?

I'm not convinced that overloading the parameters of the test array in locktest
is a good idea.  There are already a number of confusing overloads there.  And
leasetest is going to get some of its own which will further complicate things.
(I have added truncate tests to lease testing as well.)

> 
> > +
> > +TESTFILE=$TEST_DIR/lease_file
> > +
> > +_cleanup()
> > +{
> > +	kill $leasetest_pid2 > /dev/null 2>&1
> > +	kill $leasetest_pid1 > /dev/null 2>&1
> > +	rm -f $TESTFILE
> > +}
> > +
> > +# get standard environment, filters and checks
> > +. ./common/rc
> > +. ./common/filter
> > +
> > +# real QA test starts here
> > +_supported_fs generic
> > +_supported_os Linux
> > +_require_test
> > +_require_test_fcntl_advisory_locks
> > +
> > +# set up log files
> > +SERVER_LOG=$TEST_DIR/server.out
> > +CLIENT_LOG=$TEST_DIR/client.out
> > +rm -f $SERVER_LOG
> > +touch $SERVER_LOG
> > +rm -f $CLIENT_LOG
> > +touch $CLIENT_LOG
> > +
> > +if [ "$debug" != "" ]; then
> > +	echo "Test directory : $TEST_DIR"
> > +	echo "Result directory : $RESULT_DIR"
> > +	echo "Client log : $CLIENT_LOG"
> > +	echo "Server log : $SERVER_LOG"
> > +fi
> > +
> > +touch $TESTFILE
> > +
> > +# Start the server
> > +src/leasetest $debug $TESTFILE 2>&1 > $SERVER_LOG &
> > +leasetest_pid1=$!
> > +
> > +timeout=30
> > +while [ $timeout -gt 0 ]; do
> > +	sleep 1
> > +
> > +	PORT=$(cat $SERVER_LOG | grep "^server port: " | awk '{print $3}')
> > +	if [ -n "$PORT" ]; then
> > +		break
> > +	fi
> > +
> > +	# check the existence of server process
> > +	if ! kill -s 0 $leasetest_pid1 >/dev/null 2>&1; then
> > +		echo "Server died abnormally"
> > +		exit 1
> > +	fi
> > +
> > +	let timeout=timeout-1
> > +done
> > +
> > +if [ -z "$PORT" ]; then
> > +	echo "Could not get server port"
> > +	exit 1
> > +fi
> 
> The whole "discovering server port" part in generic/131 has been patched
> several times in the past to fix corner cases here and there, we'd
> better to refactor this hunk into a common helper, instead of
> duplicating the code.

You mean refactor this to be common between this and the generic/131
(locktest)?

But see my comment above.

> 
> > +
> > +# Start the client
> > +src/leasetest $debug -p $PORT -h localhost $TESTFILE 2>&1 > $CLIENT_LOG
> > +leasetest_pid2=$!
> > +result=$?
> > +if [ $result -eq 0 ]; then
> > +	echo success!
> > +	status=0
> > +else
> > +	echo "Client reported failure ($result)"
> > +	cat $TEST_DIR/*.out
> > +fi
> > +
> > +if [ "$debug" != "" ]; then
> > +	echo "End file details"
> > +	ls -la $TESTFILE
> > +fi
> > +
> > +exit
> 
> echo "Silence is golden"
> src/leasetest ...
> leasetest_pid2=$!
> result=$?
> if [ $result -ne 0 ]; then
> 	echo "Server log" >> $seqres.full
> 	cat $SERVER_LOG >> $seqres.full
> 	echo "Client log" >> $seqres.full
> 	cat $CLIENT_LOG >> $seqres.full
> 	_fail "Client reported failure ($result)"
> fi
> status=0
> exit
> 
> _fail will output the failure message, which breaks the golden output,
> and prompt user to check $seqres.full file for details.

Ah ok I see.  Sorry I was not aware of $seqres.full  My bad...

I'll see if I can clean up what is really "debugging" vs not as well within
leasetest.

Thanks,
Ira

> 
> Thanks,
> Eryu
> 
> > diff --git a/tests/generic/567.out b/tests/generic/567.out
> > new file mode 100644
> > index 000000000000..dada957258fc
> > --- /dev/null
> > +++ b/tests/generic/567.out
> > @@ -0,0 +1,2 @@
> > +QA output created by 567
> > +success!
> > diff --git a/tests/generic/group b/tests/generic/group
> > index d26c080fde30..1a48c93ff3bb 100644
> > --- a/tests/generic/group
> > +++ b/tests/generic/group
> > @@ -569,3 +569,4 @@
> >  564 auto quick copy_range
> >  565 auto quick copy_range
> >  566 auto quick quota metadata
> > +567 auto quick locks
> > -- 
> > 2.20.1
> > 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2 2/2] generic: Add Lease testing
  2019-09-09 15:33     ` Ira Weiny
@ 2019-09-10  2:29       ` Eryu Guan
  0 siblings, 0 replies; 7+ messages in thread
From: Eryu Guan @ 2019-09-10  2:29 UTC (permalink / raw)
  To: Ira Weiny
  Cc: Eryu Guan, fstests, john.hubbard, Dave Chinner, Jason Gunthorpe,
	Jan Kara, Dan Williams, Jeff Layton

On Mon, Sep 09, 2019 at 08:33:29AM -0700, Ira Weiny wrote:
> On Sun, Sep 08, 2019 at 10:33:12PM +0800, Eryu Guan wrote:
> > On Thu, Sep 05, 2019 at 10:30:26AM -0700, ira.weiny@intel.com wrote:
> > > From: Ira Weiny <ira.weiny@intel.com>
> > > 
> > > Now that we have a leasetest executable add in a lease test script.
> > > 
> > > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > 
> > The new test could be merged with patch 1.
> 
> Ok, I was not sure how best to submit this.  Now I know.
> > 
> > > ---
> > >  tests/generic/567     | 100 ++++++++++++++++++++++++++++++++++++++++++
> > >  tests/generic/567.out |   2 +
> > >  tests/generic/group   |   1 +
> > >  3 files changed, 103 insertions(+)
> > >  create mode 100755 tests/generic/567
> > >  create mode 100644 tests/generic/567.out
> > > 
> > > diff --git a/tests/generic/567 b/tests/generic/567
> > > new file mode 100755
> > > index 000000000000..3ef99c66b207
> > > --- /dev/null
> > > +++ b/tests/generic/567
> > > @@ -0,0 +1,100 @@
> > > +#! /bin/bash
> > > +# SPDX-License-Identifier: GPL-2.0
> > > +# Copyright (c) 2019 Intel Corp.  All Rights Reserved.
> > > +#
> > > +# FS QA Test 567
> > > +#
> > > +# Test Leases
> > > +#
> > > +seq=`basename $0`
> > > +seqres=$RESULT_DIR/$seq
> > > +echo "QA output created by $seq"
> > > +
> > > +here=`pwd`
> > > +tmp=/tmp/$$
> > > +status=1	# failure is the default!
> > > +trap "_cleanup; exit \$status" 0 1 2 3 15
> > > +#debug="-d -d -d"
> > > +debug=""
> > 
> > No need to define 'debug', if you need debug output, make 'debug' the
> > default and dump all debug output to $seqres.full file.
> 
> I'll check this out.  I'm new to the harness so I was not sure what got output
> where.

Basically, all stdout and stderr outputs from the test script are
recorded by the harness and compared with the golden output (the .out
file) after test, and fail the test if they are different. And
$seqres.full is a place for holding verbose test outputs for debug
purpose, which are used to help determine what's really going wrong.

> 
> FWIW I think that means we can simplify leasetest even more then.  Remove all
> the "debug" flags and leave stderr for error condition details.

Only if we dump all the outputs to $seqres.full and test only depends on
the return value of leasetest.

> 
> Anyway, your comment WRT leasetest is that you would like to see locktest
> "modernized" to include lease testing?  Is that correct?  If so, would you like
> me to make these script modifications to 131?  Or would you like to see 2
> "modes" of locktest?
> 
> I'm not convinced that overloading the parameters of the test array in locktest
> is a good idea.  There are already a number of confusing overloads there.  And
> leasetest is going to get some of its own which will further complicate things.
> (I have added truncate tests to lease testing as well.)

I was thinking of two modes, not overloading the parameters of single
test array, i.e. define two test arrays, one for locktest and another
for lease test, and using which array is determined by command switch.
But either way is fine if we could reuse the framework from locktest.

> 
> > 
> > > +
> > > +TESTFILE=$TEST_DIR/lease_file
> > > +
> > > +_cleanup()
> > > +{
> > > +	kill $leasetest_pid2 > /dev/null 2>&1
> > > +	kill $leasetest_pid1 > /dev/null 2>&1
> > > +	rm -f $TESTFILE
> > > +}
> > > +
> > > +# get standard environment, filters and checks
> > > +. ./common/rc
> > > +. ./common/filter
> > > +
> > > +# real QA test starts here
> > > +_supported_fs generic
> > > +_supported_os Linux
> > > +_require_test
> > > +_require_test_fcntl_advisory_locks
> > > +
> > > +# set up log files
> > > +SERVER_LOG=$TEST_DIR/server.out
> > > +CLIENT_LOG=$TEST_DIR/client.out
> > > +rm -f $SERVER_LOG
> > > +touch $SERVER_LOG
> > > +rm -f $CLIENT_LOG
> > > +touch $CLIENT_LOG
> > > +
> > > +if [ "$debug" != "" ]; then
> > > +	echo "Test directory : $TEST_DIR"
> > > +	echo "Result directory : $RESULT_DIR"
> > > +	echo "Client log : $CLIENT_LOG"
> > > +	echo "Server log : $SERVER_LOG"
> > > +fi
> > > +
> > > +touch $TESTFILE
> > > +
> > > +# Start the server
> > > +src/leasetest $debug $TESTFILE 2>&1 > $SERVER_LOG &
> > > +leasetest_pid1=$!
> > > +
> > > +timeout=30
> > > +while [ $timeout -gt 0 ]; do
> > > +	sleep 1
> > > +
> > > +	PORT=$(cat $SERVER_LOG | grep "^server port: " | awk '{print $3}')
> > > +	if [ -n "$PORT" ]; then
> > > +		break
> > > +	fi
> > > +
> > > +	# check the existence of server process
> > > +	if ! kill -s 0 $leasetest_pid1 >/dev/null 2>&1; then
> > > +		echo "Server died abnormally"
> > > +		exit 1
> > > +	fi
> > > +
> > > +	let timeout=timeout-1
> > > +done
> > > +
> > > +if [ -z "$PORT" ]; then
> > > +	echo "Could not get server port"
> > > +	exit 1
> > > +fi
> > 
> > The whole "discovering server port" part in generic/131 has been patched
> > several times in the past to fix corner cases here and there, we'd
> > better to refactor this hunk into a common helper, instead of
> > duplicating the code.
> 
> You mean refactor this to be common between this and the generic/131
> (locktest)?

Correct.

> 
> But see my comment above.
> 
> > 
> > > +
> > > +# Start the client
> > > +src/leasetest $debug -p $PORT -h localhost $TESTFILE 2>&1 > $CLIENT_LOG
> > > +leasetest_pid2=$!
> > > +result=$?
> > > +if [ $result -eq 0 ]; then
> > > +	echo success!
> > > +	status=0
> > > +else
> > > +	echo "Client reported failure ($result)"
> > > +	cat $TEST_DIR/*.out
> > > +fi
> > > +
> > > +if [ "$debug" != "" ]; then
> > > +	echo "End file details"
> > > +	ls -la $TESTFILE
> > > +fi
> > > +
> > > +exit
> > 
> > echo "Silence is golden"
> > src/leasetest ...
> > leasetest_pid2=$!
> > result=$?
> > if [ $result -ne 0 ]; then
> > 	echo "Server log" >> $seqres.full
> > 	cat $SERVER_LOG >> $seqres.full
> > 	echo "Client log" >> $seqres.full
> > 	cat $CLIENT_LOG >> $seqres.full
> > 	_fail "Client reported failure ($result)"
> > fi
> > status=0
> > exit
> > 
> > _fail will output the failure message, which breaks the golden output,
> > and prompt user to check $seqres.full file for details.
> 
> Ah ok I see.  Sorry I was not aware of $seqres.full  My bad...

Not a problem :)

> 
> I'll see if I can clean up what is really "debugging" vs not as well within
> leasetest.

Thanks for the test!

Eryu

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-09-10  2:30 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-05 17:30 [PATCH V2 0/2] Add lease testing ira.weiny
2019-09-05 17:30 ` [PATCH V2 1/2] src/leasetest: Add lease test executable ira.weiny
2019-09-08 14:17   ` Eryu Guan
2019-09-05 17:30 ` [PATCH V2 2/2] generic: Add Lease testing ira.weiny
2019-09-08 14:33   ` Eryu Guan
2019-09-09 15:33     ` Ira Weiny
2019-09-10  2:29       ` Eryu Guan

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.