3d28f34982d523b78b3e6704037b39617e36e7f5
[samba.git] / ctdb / utils / ping_pong / ping_pong.c
1 /*
2    A ping-pong fcntl byte range lock test
3
4    Copyright (C) Andrew Tridgell 2002
5    Copyright (C) Michael Adam 2012
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22   This measures the ping-pong byte range lock latency. It is
23   especially useful on a cluster of nodes sharing a common lock
24   manager as it will give some indication of the lock managers
25   performance under stress.
26
27   tridge@samba.org, February 2002
28
29 */
30
31 #define _XOPEN_SOURCE 500
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/time.h>
36 #include <time.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <sys/mman.h>
42 #include <stdbool.h>
43
44 static struct timeval tp1,tp2;
45
46 static int do_reads, do_writes, use_mmap, do_check, do_brl_test;
47
48 static void start_timer(void)
49 {
50         gettimeofday(&tp1,NULL);
51 }
52
53 static double end_timer(void)
54 {
55         gettimeofday(&tp2,NULL);
56         return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
57                 (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
58 }
59
60 /* lock a byte range in a open file */
61 static int lock_range(int fd, int offset, int len, bool wait)
62 {
63         struct flock lock;
64
65         lock.l_type = F_WRLCK;
66         lock.l_whence = SEEK_SET;
67         lock.l_start = offset;
68         lock.l_len = len;
69         lock.l_pid = 0;
70         
71         return fcntl(fd, wait ? F_SETLKW : F_SETLK, &lock);
72 }
73
74 /* check whether we could place a lock */
75 static int check_lock(int fd, int offset, int len)
76 {
77         struct flock lock;
78         int ret;
79
80         lock.l_type = F_WRLCK;
81         lock.l_whence = SEEK_SET;
82         lock.l_start = offset;
83         lock.l_len = len;
84         lock.l_pid = 0;
85
86         ret = fcntl(fd, F_GETLK, &lock);
87         if (ret != 0) {
88                 printf("error calling fcntl F_GETLCK: %s\n", strerror(errno));
89                 return -1;
90         }
91
92         if (lock.l_type == F_UNLCK) {
93                 /* we would be able to place the lock */
94                 return 0;
95         }
96
97         /* we would not be able to place lock */
98         printf("check_lock failed: lock held: "
99                "pid='%d', type='%d', start='%d', len='%d'\n",
100                (int)lock.l_pid, (int)lock.l_type, (int)lock.l_start, (int)lock.l_len);
101         return 1;
102 }
103
104 /* unlock a byte range in a open file */
105 static int unlock_range(int fd, int offset, int len)
106 {
107         struct flock lock;
108
109         lock.l_type = F_UNLCK;
110         lock.l_whence = SEEK_SET;
111         lock.l_start = offset;
112         lock.l_len = len;
113         lock.l_pid = 0;
114         
115         return fcntl(fd,F_SETLKW,&lock);
116 }
117
118 /* run the ping pong test on fd */
119 static void ping_pong(int fd, int num_locks)
120 {
121         unsigned count = 0;
122         int i=0, loops=0;
123         unsigned char *val;
124         unsigned char incr=0, last_incr=0;
125         unsigned char *p = NULL;
126         int ret;
127
128         ret = ftruncate(fd, num_locks+1);
129         if (ret == -1) {
130                 printf("ftruncate failed: %s\n", strerror(errno));
131                 return;
132         }
133
134         if (use_mmap) {
135                 p = mmap(NULL, num_locks+1, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
136                 if (p == MAP_FAILED) {
137                         printf("mmap failed: %s\n", strerror(errno));
138                         return;
139                 }
140         }
141
142         val = (unsigned char *)calloc(num_locks+1, sizeof(unsigned char));
143         if (val == NULL) {
144                 printf("calloc failed\n");
145                 if (use_mmap) {
146                         munmap(p, num_locks+1);
147                 }
148                 return;
149         }
150
151         start_timer();
152
153         ret = lock_range(fd, 0, 1, true);
154         if (ret != 0) {
155                 printf("initial lock at 0 failed! - %s\n", strerror(errno));
156                 goto done;
157         }
158
159         i = 0;
160
161         while (1) {
162                 if (lock_range(fd, (i+1) % num_locks, 1, true) != 0) {
163                         printf("lock at %d failed! - %s\n",
164                                (i+1) % num_locks, strerror(errno));
165                 }
166                 if (do_check) {
167                         ret = check_lock(fd, i, 1);
168                         if (ret != 0) {
169                                 goto done;
170                         }
171                 }
172                 if (do_reads) {
173                         unsigned char c;
174                         if (use_mmap) {
175                                 c = p[i];
176                         } else if (pread(fd, &c, 1, i) != 1) {
177                                 printf("read failed at %d\n", i);
178                         }
179                         incr = c - val[i];
180                         val[i] = c;
181                 }
182                 if (do_writes) {
183                         char c = val[i] + 1;
184                         if (use_mmap) {
185                                 p[i] = c;
186                         } else if (pwrite(fd, &c, 1, i) != 1) {
187                                 printf("write failed at %d\n", i);
188                         }
189                 }
190                 if (unlock_range(fd, i, 1) != 0) {
191                         printf("unlock at %d failed! - %s\n",
192                                i, strerror(errno));
193                 }
194                 i = (i+1) % num_locks;
195                 count++;
196                 if (loops > num_locks && incr != last_incr) {
197                         last_incr = incr;
198                         printf("data increment = %u\n", incr);
199                         fflush(stdout);
200                 }
201                 if (end_timer() > 1.0) {
202                         printf("%8u locks/sec\r", 
203                                (unsigned)(2*count/end_timer()));
204                         fflush(stdout);
205                         start_timer();
206                         count=0;
207                 }
208                 loops++;
209         }
210
211 done:
212         if (use_mmap) {
213                 munmap(p, num_locks+1);
214         }
215         free(val);
216 }
217
218 static void usage(void)
219 {
220         printf("ping_pong -rwmc <file> <num_locks>\n");
221         printf("ping_pong -l <file>\n\n");
222         printf("Options\n");
223         printf(" -r    do reads\n");
224         printf(" -w    do writes\n");
225         printf(" -m    use mmap\n");
226         printf(" -c    check locks\n");
227         printf(" -l    test for working byte range locks\n");
228 }
229
230 int main(int argc, char *argv[])
231 {
232         char *fname;
233         int fd, num_locks;
234         int c;
235
236         while ((c = getopt(argc, argv, "rwmcl")) != -1) {
237                 switch (c){
238                 case 'w':
239                         do_writes = 1;
240                         break;
241                 case 'r':
242                         do_reads = 1;
243                         break;
244                 case 'm':
245                         use_mmap = 1;
246                         break;
247                 case 'c':
248                         do_check = 1;
249                         break;
250                 case 'l':
251                         do_brl_test = 1;
252                         break;
253                 default:
254                         fprintf(stderr, "Unknown option '%c'\n", c);
255                         exit(1);
256                 }
257         }
258
259         argv += optind;
260         argc -= optind;
261
262         if (argc < 1) {
263                 usage();
264                 exit(1);
265         }
266
267         fname = argv[0];
268
269         fd = open(fname, O_CREAT|O_RDWR, 0600);
270         if (fd == -1) {
271                 exit(1);
272         }
273
274         if (do_brl_test) {
275                 if (lock_range(fd, 0, 0, false) != 0) {
276                         printf("file already locked, calling check_lock to tell us who has it locked:\n");
277                         (void)check_lock(fd, 0, 0);
278                         printf("Working POSIX byte range locks\n");
279                         exit(0);
280                 }
281
282                 printf("Holding lock, press any key to continue...\n");
283                 printf("You should run the same command on another node now.\n");
284                 (void)getchar();
285                 printf("Good bye.\n");
286                 exit(0);
287         }
288
289         if (argc < 2) {
290                 usage();
291                 exit(1);
292         }
293
294         num_locks = atoi(argv[1]);
295         if (num_locks <= 0) {
296                 printf("num_locks should be > 0\n");
297                 exit(1);
298         }
299
300         ping_pong(fd, num_locks);
301
302         return 0;
303 }