lease02: Do a lock-unlock sequence instead of lock-lock
[jlayton/lockperf.git] / posix01.c
1 /*
2  * POSIX lock performance test 1
3  *
4  * Fork off a given number of children who attempt to repeatedly acquire a
5  * POSIX write lock over the whole file and then release it for a given number
6  * of times. Time this to get a rough indication of locking performance for
7  * a single, contended whole-file lock.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif /* HAVE_CONFIG_H */
13
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include <sys/mman.h>
25 #include <stdbool.h>
26 #include <sched.h>
27
28 #include "timespec.h"
29
30 #define NRPROC (128)
31 #define NRLOCK (10240)
32
33 static struct timespec *diff;
34
35 static int
36 lockunlock(const char *name, int nrlock, struct timespec *slot,
37                 bool verbose, bool yield)
38 {
39         int fd, i, ret = 0;
40         struct flock lck = { .l_whence = SEEK_SET };
41         struct timespec start, end;
42         pid_t pid = getpid();
43
44         fd = open(name, O_CREAT|O_RDWR, 0644);
45         if (fd < 0) {
46                 perror("open");
47                 return fd;
48         }
49
50         ret = clock_gettime(CLOCK_MONOTONIC_RAW, &start);
51         if (ret) {
52                 perror("clock_gettime");
53                 return ret;
54         }
55
56         for (i = 0; i < nrlock; ++i) {
57                 lck.l_type = F_WRLCK;
58                 ret = fcntl(fd, F_SETLKW, &lck);
59                 if (ret < 0) {
60                         perror("fcntl");
61                         break;
62                 }
63
64                 if (verbose)
65                         printf("pid:%u\n", pid);
66
67                 lck.l_type = F_UNLCK;
68                 ret = fcntl(fd, F_SETLKW, &lck);
69                 if (ret < 0) {
70                         perror("fcntl");
71                         break;
72                 }
73
74                 /* yield CPU to give another worker a chance */
75                 if (yield)
76                         sched_yield();
77         }
78
79         clock_gettime(CLOCK_MONOTONIC_RAW, &end);
80         if (ret) {
81                 perror("clock_gettime");
82                 return ret;
83         }
84
85         close(fd);
86         *slot = timespec_sub(end, start);
87         return ret;
88 }
89
90 void
91 usage(char *prog)
92 {
93         printf("usage: %s [-n nr_procs] [-l nr_locks] [-v] [-y] filename\n", prog);
94 }
95
96 int
97 main(int argc, char **argv)
98 {
99         bool verbose = false, yield = false;
100         int i, opt, valid = 0;
101         int nproc = NRPROC;
102         int nlock = NRLOCK;
103         pid_t *pids;
104         struct timespec total = { .tv_sec = 0,
105                                   .tv_nsec = 0 };
106
107         while ((opt = getopt(argc, argv, "l:n:vy")) != -1) {
108                 switch (opt) {
109                 case 'l':
110                         nlock = atoi(optarg);
111                         break;
112                 case 'n':
113                         nproc = atoi(optarg);
114                         break;
115                 case 'v':
116                         verbose = true;
117                         break;
118                 case 'y':
119                         yield = true;
120                         break;
121                 default:
122                         usage(argv[0]);
123                         return 1;
124                 }
125         }
126
127         if (!argv[optind]) {
128                 usage(argv[0]);
129                 return 1;
130         }
131
132         pids = calloc(nproc, sizeof(pid_t));
133         if (!pids) {
134                 fprintf(stderr, "Unable to allocate pids array!");
135                 return 1;
136         }
137
138         diff = mmap(0, nproc * sizeof(*diff), PROT_READ | PROT_WRITE,
139                         MAP_ANONYMOUS | MAP_SHARED, -1, 0);
140         if (diff == (struct timespec *)-1) {
141                 fprintf(stderr, "Unable to allocate timespec array!");
142                 return 1;
143         }
144
145         for (i = 0; i < nproc; ++i) {
146                 pids[i] = fork();
147                 if (!pids[i])
148                         return lockunlock(argv[optind], nlock,
149                                                 &diff[i], verbose, yield);
150         }
151
152         for (i = 0; i < nproc; ++i) {
153                 int status;
154
155                 if (pids[i] < 0) {
156                         fprintf(stderr, "process %d failed to fork\n", i);
157                         continue;
158                 }
159                 if (waitpid(pids[i], &status, 0) < 0) {
160                         fprintf(stderr, "unable to reap pid %d\n", pids[i]);
161                         continue;
162                 }
163                 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
164                         fprintf(stderr, "pid %d exited abnormally(0x%x)\n", pids[i],status);
165                         continue;
166                 }
167                 total = timespec_add(total, diff[i]);
168                 ++valid;
169         }
170
171         if (valid != nproc) {
172                 fprintf(stderr, "Some children didn't run properly -- "
173                                 "requested %d but only got %d\n", nproc, valid);
174                 return 1;
175         }
176
177         printf("%ld.%09ld\n", total.tv_sec, total.tv_nsec);
178         return 0;
179 }