summaryrefslogtreecommitdiffstats
path: root/ctdb/utils/ping_pong/ping_pong.c
diff options
context:
space:
mode:
Diffstat (limited to 'ctdb/utils/ping_pong/ping_pong.c')
-rw-r--r--ctdb/utils/ping_pong/ping_pong.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/ctdb/utils/ping_pong/ping_pong.c b/ctdb/utils/ping_pong/ping_pong.c
new file mode 100644
index 0000000..3d28f34
--- /dev/null
+++ b/ctdb/utils/ping_pong/ping_pong.c
@@ -0,0 +1,303 @@
+/*
+ A ping-pong fcntl byte range lock test
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Michael Adam 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This measures the ping-pong byte range lock latency. It is
+ especially useful on a cluster of nodes sharing a common lock
+ manager as it will give some indication of the lock managers
+ performance under stress.
+
+ tridge@samba.org, February 2002
+
+*/
+
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <stdbool.h>
+
+static struct timeval tp1,tp2;
+
+static int do_reads, do_writes, use_mmap, do_check, do_brl_test;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
+ (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
+}
+
+/* lock a byte range in a open file */
+static int lock_range(int fd, int offset, int len, bool wait)
+{
+ struct flock lock;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ return fcntl(fd, wait ? F_SETLKW : F_SETLK, &lock);
+}
+
+/* check whether we could place a lock */
+static int check_lock(int fd, int offset, int len)
+{
+ struct flock lock;
+ int ret;
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ ret = fcntl(fd, F_GETLK, &lock);
+ if (ret != 0) {
+ printf("error calling fcntl F_GETLCK: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (lock.l_type == F_UNLCK) {
+ /* we would be able to place the lock */
+ return 0;
+ }
+
+ /* we would not be able to place lock */
+ printf("check_lock failed: lock held: "
+ "pid='%d', type='%d', start='%d', len='%d'\n",
+ (int)lock.l_pid, (int)lock.l_type, (int)lock.l_start, (int)lock.l_len);
+ return 1;
+}
+
+/* unlock a byte range in a open file */
+static int unlock_range(int fd, int offset, int len)
+{
+ struct flock lock;
+
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = offset;
+ lock.l_len = len;
+ lock.l_pid = 0;
+
+ return fcntl(fd,F_SETLKW,&lock);
+}
+
+/* run the ping pong test on fd */
+static void ping_pong(int fd, int num_locks)
+{
+ unsigned count = 0;
+ int i=0, loops=0;
+ unsigned char *val;
+ unsigned char incr=0, last_incr=0;
+ unsigned char *p = NULL;
+ int ret;
+
+ ret = ftruncate(fd, num_locks+1);
+ if (ret == -1) {
+ printf("ftruncate failed: %s\n", strerror(errno));
+ return;
+ }
+
+ if (use_mmap) {
+ p = mmap(NULL, num_locks+1, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (p == MAP_FAILED) {
+ printf("mmap failed: %s\n", strerror(errno));
+ return;
+ }
+ }
+
+ val = (unsigned char *)calloc(num_locks+1, sizeof(unsigned char));
+ if (val == NULL) {
+ printf("calloc failed\n");
+ if (use_mmap) {
+ munmap(p, num_locks+1);
+ }
+ return;
+ }
+
+ start_timer();
+
+ ret = lock_range(fd, 0, 1, true);
+ if (ret != 0) {
+ printf("initial lock at 0 failed! - %s\n", strerror(errno));
+ goto done;
+ }
+
+ i = 0;
+
+ while (1) {
+ if (lock_range(fd, (i+1) % num_locks, 1, true) != 0) {
+ printf("lock at %d failed! - %s\n",
+ (i+1) % num_locks, strerror(errno));
+ }
+ if (do_check) {
+ ret = check_lock(fd, i, 1);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+ if (do_reads) {
+ unsigned char c;
+ if (use_mmap) {
+ c = p[i];
+ } else if (pread(fd, &c, 1, i) != 1) {
+ printf("read failed at %d\n", i);
+ }
+ incr = c - val[i];
+ val[i] = c;
+ }
+ if (do_writes) {
+ char c = val[i] + 1;
+ if (use_mmap) {
+ p[i] = c;
+ } else if (pwrite(fd, &c, 1, i) != 1) {
+ printf("write failed at %d\n", i);
+ }
+ }
+ if (unlock_range(fd, i, 1) != 0) {
+ printf("unlock at %d failed! - %s\n",
+ i, strerror(errno));
+ }
+ i = (i+1) % num_locks;
+ count++;
+ if (loops > num_locks && incr != last_incr) {
+ last_incr = incr;
+ printf("data increment = %u\n", incr);
+ fflush(stdout);
+ }
+ if (end_timer() > 1.0) {
+ printf("%8u locks/sec\r",
+ (unsigned)(2*count/end_timer()));
+ fflush(stdout);
+ start_timer();
+ count=0;
+ }
+ loops++;
+ }
+
+done:
+ if (use_mmap) {
+ munmap(p, num_locks+1);
+ }
+ free(val);
+}
+
+static void usage(void)
+{
+ printf("ping_pong -rwmc <file> <num_locks>\n");
+ printf("ping_pong -l <file>\n\n");
+ printf("Options\n");
+ printf(" -r do reads\n");
+ printf(" -w do writes\n");
+ printf(" -m use mmap\n");
+ printf(" -c check locks\n");
+ printf(" -l test for working byte range locks\n");
+}
+
+int main(int argc, char *argv[])
+{
+ char *fname;
+ int fd, num_locks;
+ int c;
+
+ while ((c = getopt(argc, argv, "rwmcl")) != -1) {
+ switch (c){
+ case 'w':
+ do_writes = 1;
+ break;
+ case 'r':
+ do_reads = 1;
+ break;
+ case 'm':
+ use_mmap = 1;
+ break;
+ case 'c':
+ do_check = 1;
+ break;
+ case 'l':
+ do_brl_test = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown option '%c'\n", c);
+ exit(1);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ fname = argv[0];
+
+ fd = open(fname, O_CREAT|O_RDWR, 0600);
+ if (fd == -1) {
+ exit(1);
+ }
+
+ if (do_brl_test) {
+ if (lock_range(fd, 0, 0, false) != 0) {
+ printf("file already locked, calling check_lock to tell us who has it locked:\n");
+ (void)check_lock(fd, 0, 0);
+ printf("Working POSIX byte range locks\n");
+ exit(0);
+ }
+
+ printf("Holding lock, press any key to continue...\n");
+ printf("You should run the same command on another node now.\n");
+ (void)getchar();
+ printf("Good bye.\n");
+ exit(0);
+ }
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ num_locks = atoi(argv[1]);
+ if (num_locks <= 0) {
+ printf("num_locks should be > 0\n");
+ exit(1);
+ }
+
+ ping_pong(fd, num_locks);
+
+ return 0;
+}