13 #include <sys/types.h>
19 * In distributions that do not have these macros ready in glibc-headers,
20 * compilation fails. Adding them here to avoid build errors, relevant tests
21 * would fail at the helper which requires OFD locks support and notrun if the
22 * kernel does not support OFD locks. If the kernel does support OFD locks, we
26 #define F_OFD_GETLK 36
30 #define F_OFD_SETLK 37
34 #define F_OFD_SETLKW 38
38 * Usually we run getlk routine after running setlk routine
39 * in background. However, getlk could be executed before setlk
40 * sometimes, which is invalid for our tests. So we use semaphore
41 * to synchronize between getlk and setlk.
43 * setlk routine: * getlk routine:
47 * open file * open file
51 * wait init sem done * wait init sem done
55 * |------------clone()--------| * |
57 * |(parent) (child)| * |
61 * | set sem0=0 * wait sem0==0
65 * wait sem1==0 | * set sem1=0
76 /* This is required by semctl to set semaphore value */
78 int val; /* Value for SETVAL */
79 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
80 unsigned short *array; /* Array for GETALL, SETALL */
81 struct seminfo *__buf; /* Buffer for IPC_INFO
85 static void err_exit(char *op, int errn)
87 fprintf(stderr, "%s: %s\n", op, strerror(errn));
90 if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1)
96 * Flags that used to specify operation details.
97 * They can be specified via command line options.
100 * posix : 1 <--> test posix lock
101 * 0 <--> test OFD lock (default)
104 * lock_cmd : 1 <--> setlk (default)
108 * lock_rw : 1 <--> set/get wrlck (default)
109 * 0 <--> set/get rdlck
112 * lock_start : l_start to getlk
115 * clone_fs : clone with CLONE_FILES
118 * use_dup : dup and close to setup condition in setlk
121 * open_rw : 1 <--> open file RDWR (default)
122 * 0 <--> open file RDONLY
124 * This option is for _require_ofd_locks helper, just do
125 * fcntl setlk then return errno.
127 * testrun : 1 <--> this is a testrun, return after setlk
128 * 0 <--> this is not a testrun, run as usual
131 static void usage(char *arg0)
133 printf("Usage: %s [-sgrwo:l:RWPtFd] filename\n", arg0);
134 printf("\t-s/-g : to setlk or to getlk\n");
135 printf("\t-P : POSIX locks\n");
136 printf("\t-F : clone with CLONE_FILES in setlk to setup test condition\n");
137 printf("\t-d : dup and close in setlk\n");
138 printf("\twithout both -F/d, use clone without CLONE_FILES\n");
139 printf("\t-r/-w : set/get rdlck/wrlck\n");
140 printf("\t-o num : offset start to lock, default 0\n");
141 printf("\t-l num : lock length, default 10\n");
142 printf("\t-R/-W : open file RDONLY/RDWR\n\n");
143 printf("\tUsually we run a setlk routine in background and then\n");
144 printf("\trun a getlk routine to check. They must be paired, or\n");
145 printf("\ttest will hang.\n\n");
149 #define STACK_SIZE (1024 * 1024)
150 static char child_stack[STACK_SIZE] __attribute__((aligned));
152 static int child_fn(void* p)
157 /* close relative fd */
158 if (cfd > 0 && close(cfd) == -1)
159 perror("close in child");
161 /* set sem0 = 0 (setlk and close fd done) */
163 if (semctl(semid, 0, SETVAL, semu) == -1)
164 err_exit("set sem0 0", errno);
169 int main(int argc, char **argv)
180 int setlk_macro = F_OFD_SETLKW;
181 int getlk_macro = F_OFD_GETLK;
184 unsigned short vals[2];
186 struct semid_ds sem_ds;
190 while((opt = getopt(argc, argv, "sgrwo:l:PRWtFd")) != -1) {
205 lock_start = atoi(optarg);
208 lock_l = atoi(optarg);
234 if (optind >= argc) {
240 .l_whence = SEEK_SET,
241 .l_start = lock_start,
247 /* OFD lock requires l_pid to be zero */
249 setlk_macro = F_OFD_SETLKW;
250 getlk_macro = F_OFD_GETLK;
252 setlk_macro = F_SETLKW;
253 getlk_macro = F_GETLK;
257 flk.l_type = F_WRLCK;
259 flk.l_type = F_RDLCK;
262 fd = open(argv[optind], O_RDONLY);
264 fd = open(argv[optind], O_RDWR);
266 err_exit("open", errno);
269 * In a testun, we do a fcntl getlk call and exit
270 * immediately no matter it succeeds or not.
273 fcntl(fd, F_OFD_GETLK, &flk);
274 err_exit("test_ofd_getlk", errno);
277 if((semkey = ftok(argv[optind], 255)) == -1)
278 err_exit("ftok", errno);
280 /* setlk, and always init the semaphore at setlk time */
283 * Init the semaphore, with a key related to the testfile.
284 * getlk routine will wait untill this sem has been created and
287 * We must make sure the semaphore set is newly created, rather
288 * then the one left from last run. In which case getlk will
289 * exit immediately and left setlk routine waiting forever.
290 * Also because newly created semaphore has zero sem_otime,
291 * which is used here to sync with getlk routine.
295 semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL);
296 if (semid < 0 && errno == EEXIST) {
297 /* remove sem set after one round of test */
298 if (semctl(semid, 2, IPC_RMID, semu) == -1)
299 err_exit("rmid 0", errno);
301 } else if (semid < 0)
302 err_exit("semget", errno);
306 /* We can't create a new semaphore set in 5 tries */
308 err_exit("semget", errno);
310 /* Init both new sem to 1 */
314 if (semctl(semid, 2, SETALL, semu) == -1)
315 err_exit("init sem", errno);
316 /* Inc both new sem to 2 */
322 if (semtimedop(semid, &sop, 1, &ts) == -1)
323 err_exit("inc sem0 2", errno);
329 if (semtimedop(semid, &sop, 1, &ts) == -1)
330 err_exit("inc sem1 2", errno);
333 * Wait initialization complete. semctl(2) only update
334 * sem_ctime, semop(2) will update sem_otime.
338 memset(&sem_ds, 0, sizeof(sem_ds));
340 ret = semctl(semid, 0, IPC_STAT, semu);
341 } while (!(ret == 0 && sem_ds.sem_otime != 0));
344 if (fcntl(fd, setlk_macro, &flk) < 0)
345 err_exit("setlkw", errno);
348 /* dup fd and close the newfd */
351 err_exit("dup", errno);
353 /* set sem0 = 0 (setlk and close fd done) */
355 if (semctl(semid, 0, SETVAL, semu) == -1)
356 err_exit("set sem0 0", errno);
359 * clone a child to close the fd then tell getlk to go;
360 * in parent we keep holding the lock till getlk done.
364 child_pid = clone(child_fn, child_stack+STACK_SIZE,
365 CLONE_FILES|CLONE_SYSVSEM|SIGCHLD, &fd);
367 child_pid = clone(child_fn, child_stack+STACK_SIZE,
368 CLONE_SYSVSEM|SIGCHLD, &fd);
370 err_exit("clone", errno);
371 /* wait child done */
372 waitpid(child_pid, NULL, 0);
375 /* "hold" lock and wait sem1 == 0 (getlk done) */
381 if (semtimedop(semid, &sop, 1, &ts) == -1)
382 err_exit("wait sem1 0", errno);
384 /* remove sem set after one round of test */
385 if (semctl(semid, 2, IPC_RMID, semu) == -1)
386 err_exit("rmid", errno);
393 /* wait sem created and initialized */
396 semid = semget(semkey, 2, 0);
399 if (errno == ENOENT && retry) {
404 err_exit("getlk_semget", errno);
408 memset(&sem_ds, 0, sizeof(sem_ds));
410 ret = semctl(semid, 0, IPC_STAT, semu);
411 } while (!(ret == 0 && sem_ds.sem_otime != 0));
413 /* wait sem0 == 0 (setlk and close fd done) */
419 if (semtimedop(semid, &sop, 1, &ts) == -1)
420 err_exit("wait sem0 0", errno);
422 if (fcntl(fd, getlk_macro, &flk) < 0)
423 err_exit("getlk", errno);
425 /* set sem1 = 0 (getlk done) */
427 if (semctl(semid, 1, SETVAL, semu) == -1)
428 err_exit("set sem1 0", errno);
431 switch (flk.l_type) {
433 printf("lock could be placed\n");
436 printf("get rdlck\n");
439 printf("get wrlck\n");
442 printf("unknown lock type\n");