1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2011 New Dream Network
9 * This is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License version 2.1, as published by the Free Software
12 * Foundation. See file COPYING.
17 #include "gtest/gtest.h"
18 #ifndef GTEST_IS_THREADSAFE
19 #error "!GTEST_IS_THREADSAFE"
22 #include "include/compat.h"
23 #include "include/cephfs/libcephfs.h"
24 #include "include/fs_types.h"
26 #include <sys/fcntl.h>
29 #include <sys/types.h>
34 #include <semaphore.h>
43 #include <sys/xattr.h>
45 #include <sys/types.h>
49 #include "include/ceph_assert.h"
50 #include "ceph_pthread_self.h"
52 // Startup common: create and mount ceph fs
53 #define STARTUP_CEPH() do { \
54 ASSERT_EQ(0, ceph_create(&cmount, NULL)); \
55 ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); \
56 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL)); \
57 ASSERT_EQ(0, ceph_mount(cmount, NULL)); \
60 // Cleanup common: unmount and release ceph fs
61 #define CLEANUP_CEPH() do { \
62 ASSERT_EQ(0, ceph_unmount(cmount)); \
63 ASSERT_EQ(0, ceph_release(cmount)); \
66 static const mode_t fileMode
= S_IRWXU
| S_IRWXG
| S_IRWXO
;
68 // Default wait time for normal and "slow" operations
69 // (5" should be enough in case of network congestion)
70 static const long waitMs
= 10;
71 static const long waitSlowMs
= 5000;
73 // Get the absolute struct timespec reference from now + 'ms' milliseconds
74 static const struct timespec
* abstime(struct timespec
&ts
, long ms
) {
75 if (clock_gettime(CLOCK_REALTIME
, &ts
) == -1) {
78 ts
.tv_nsec
+= ms
* 1000000;
79 ts
.tv_sec
+= ts
.tv_nsec
/ 1000000000;
80 ts
.tv_nsec
%= 1000000000;
86 TEST(LibCephFS
, BasicRecordLocking
) {
87 struct ceph_mount_info
*cmount
= NULL
;
91 sprintf(c_file
, "recordlock_test_%d", getpid());
93 Inode
*root
= NULL
, *inode
= NULL
;
94 struct ceph_statx stx
;
96 struct flock lock1
, lock2
;
97 UserPerm
*perms
= ceph_mount_perms(cmount
);
100 rc
= ceph_ll_lookup_root(cmount
, &root
);
103 // Get the inode and Fh corresponding to c_file
104 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
105 &inode
, &fh
, &stx
, 0, 0, perms
);
109 lock1
.l_type
= F_WRLCK
;
110 lock1
.l_whence
= SEEK_SET
;
113 lock1
.l_pid
= getpid();
114 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
116 lock2
.l_type
= F_WRLCK
;
117 lock2
.l_whence
= SEEK_SET
;
120 lock2
.l_pid
= getpid();
121 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock2
, 43, false));
123 // Now try a conflicting read lock
124 lock2
.l_type
= F_RDLCK
;
125 lock2
.l_whence
= SEEK_SET
;
128 lock2
.l_pid
= getpid();
129 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock2
, 43, false));
132 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
133 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
134 ASSERT_EQ(lock2
.l_start
, 0);
135 ASSERT_EQ(lock2
.l_len
, 1024);
136 ASSERT_EQ(lock2
.l_pid
, getpid());
138 // Extend the range of the write lock
139 lock1
.l_type
= F_WRLCK
;
140 lock1
.l_whence
= SEEK_SET
;
141 lock1
.l_start
= 1024;
143 lock1
.l_pid
= getpid();
144 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
147 lock2
.l_type
= F_RDLCK
;
148 lock2
.l_whence
= SEEK_SET
;
151 lock2
.l_pid
= getpid();
152 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
153 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
154 ASSERT_EQ(lock2
.l_start
, 0);
155 ASSERT_EQ(lock2
.l_len
, 2048);
156 ASSERT_EQ(lock2
.l_pid
, getpid());
158 // Now release part of the range
159 lock1
.l_type
= F_UNLCK
;
160 lock1
.l_whence
= SEEK_SET
;
163 lock1
.l_pid
= getpid();
164 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
166 // Now do a getlk to check 1st part
167 lock2
.l_type
= F_RDLCK
;
168 lock2
.l_whence
= SEEK_SET
;
171 lock2
.l_pid
= getpid();
172 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
173 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
174 ASSERT_EQ(lock2
.l_start
, 0);
175 ASSERT_EQ(lock2
.l_len
, 512);
176 ASSERT_EQ(lock2
.l_pid
, getpid());
178 // Now do a getlk to check 2nd part
179 lock2
.l_type
= F_RDLCK
;
180 lock2
.l_whence
= SEEK_SET
;
181 lock2
.l_start
= 2000;
183 lock2
.l_pid
= getpid();
184 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
185 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
186 ASSERT_EQ(lock2
.l_start
, 1536);
187 ASSERT_EQ(lock2
.l_len
, 512);
188 ASSERT_EQ(lock2
.l_pid
, getpid());
190 // Now do a getlk to check released part
191 lock2
.l_type
= F_RDLCK
;
192 lock2
.l_whence
= SEEK_SET
;
195 lock2
.l_pid
= getpid();
196 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
197 ASSERT_EQ(lock2
.l_type
, F_UNLCK
);
198 ASSERT_EQ(lock2
.l_start
, 512);
199 ASSERT_EQ(lock2
.l_len
, 1024);
200 ASSERT_EQ(lock2
.l_pid
, getpid());
202 // Now downgrade the 1st part of the lock
203 lock1
.l_type
= F_RDLCK
;
204 lock1
.l_whence
= SEEK_SET
;
207 lock1
.l_pid
= getpid();
208 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
210 // Now do a getlk to check 1st part
211 lock2
.l_type
= F_WRLCK
;
212 lock2
.l_whence
= SEEK_SET
;
215 lock2
.l_pid
= getpid();
216 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
217 ASSERT_EQ(lock2
.l_type
, F_RDLCK
);
218 ASSERT_EQ(lock2
.l_start
, 0);
219 ASSERT_EQ(lock2
.l_len
, 512);
220 ASSERT_EQ(lock2
.l_pid
, getpid());
222 // Now upgrade the 1st part of the lock
223 lock1
.l_type
= F_WRLCK
;
224 lock1
.l_whence
= SEEK_SET
;
227 lock1
.l_pid
= getpid();
228 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
230 // Now do a getlk to check 1st part
231 lock2
.l_type
= F_WRLCK
;
232 lock2
.l_whence
= SEEK_SET
;
235 lock2
.l_pid
= getpid();
236 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
237 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
238 ASSERT_EQ(lock2
.l_start
, 0);
239 ASSERT_EQ(lock2
.l_len
, 512);
240 ASSERT_EQ(lock2
.l_pid
, getpid());
242 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
243 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
247 /* Locking in different threads */
249 // Used by ConcurrentLocking test
250 struct str_ConcurrentRecordLocking
{
252 struct ceph_mount_info
*cmount
; // !NULL if shared
255 void sem_init(int pshared
) {
256 ASSERT_EQ(0, ::sem_init(&sem
[0], pshared
, 0));
257 ASSERT_EQ(0, ::sem_init(&sem
[1], pshared
, 0));
258 ASSERT_EQ(0, ::sem_init(&semReply
[0], pshared
, 0));
259 ASSERT_EQ(0, ::sem_init(&semReply
[1], pshared
, 0));
262 ASSERT_EQ(0, ::sem_destroy(&sem
[0]));
263 ASSERT_EQ(0, ::sem_destroy(&sem
[1]));
264 ASSERT_EQ(0, ::sem_destroy(&semReply
[0]));
265 ASSERT_EQ(0, ::sem_destroy(&semReply
[1]));
269 // Wakeup main (for (N) steps)
270 #define PING_MAIN(n) ASSERT_EQ(0, sem_post(&s.sem[n%2]))
271 // Wait for main to wake us up (for (RN) steps)
272 #define WAIT_MAIN(n) \
273 ASSERT_EQ(0, sem_timedwait(&s.semReply[n%2], abstime(ts, waitSlowMs)))
275 // Wakeup worker (for (RN) steps)
276 #define PING_WORKER(n) ASSERT_EQ(0, sem_post(&s.semReply[n%2]))
277 // Wait for worker to wake us up (for (N) steps)
278 #define WAIT_WORKER(n) \
279 ASSERT_EQ(0, sem_timedwait(&s.sem[n%2], abstime(ts, waitSlowMs)))
280 // Worker shall not wake us up (for (N) steps)
281 #define NOT_WAIT_WORKER(n) \
282 ASSERT_EQ(-1, sem_timedwait(&s.sem[n%2], abstime(ts, waitMs)))
284 // Do twice an operation
285 #define TWICE(EXPR) do { \
290 /* Locking in different threads */
292 // Used by ConcurrentLocking test
293 static void thread_ConcurrentRecordLocking(str_ConcurrentRecordLocking
& s
) {
294 struct ceph_mount_info
*const cmount
= s
.cmount
;
296 Inode
*root
= NULL
, *inode
= NULL
;
297 struct ceph_statx stx
;
302 // Get the root inode
303 rc
= ceph_ll_lookup_root(cmount
, &root
);
306 // Get the inode and Fh corresponding to c_file
307 rc
= ceph_ll_create(cmount
, root
, s
.file
, fileMode
, O_RDWR
| O_CREAT
,
308 &inode
, &fh
, &stx
, 0, 0, ceph_mount_perms(cmount
));
311 lock1
.l_type
= F_WRLCK
;
312 lock1
.l_whence
= SEEK_SET
;
315 lock1
.l_pid
= getpid();
316 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
319 lock1
.l_type
= F_WRLCK
;
320 lock1
.l_whence
= SEEK_SET
;
323 lock1
.l_pid
= getpid();
324 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
327 lock1
.l_type
= F_UNLCK
;
328 lock1
.l_whence
= SEEK_SET
;
331 lock1
.l_pid
= getpid();
332 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
335 lock1
.l_type
= F_RDLCK
;
336 lock1
.l_whence
= SEEK_SET
;
339 lock1
.l_pid
= getpid();
340 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
343 WAIT_MAIN(1); // (R1)
344 lock1
.l_type
= F_UNLCK
;
345 lock1
.l_whence
= SEEK_SET
;
348 lock1
.l_pid
= getpid();
349 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
352 WAIT_MAIN(2); // (R2)
353 lock1
.l_type
= F_WRLCK
;
354 lock1
.l_whence
= SEEK_SET
;
357 lock1
.l_pid
= getpid();
358 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
361 WAIT_MAIN(3); // (R3)
362 lock1
.l_type
= F_UNLCK
;
363 lock1
.l_whence
= SEEK_SET
;
366 lock1
.l_pid
= getpid();
367 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
370 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
373 // Used by ConcurrentRecordLocking test
374 static void* thread_ConcurrentRecordLocking_(void *arg
) {
375 str_ConcurrentRecordLocking
*const s
=
376 reinterpret_cast<str_ConcurrentRecordLocking
*>(arg
);
377 thread_ConcurrentRecordLocking(*s
);
381 TEST(LibCephFS
, ConcurrentRecordLocking
) {
382 const pid_t mypid
= getpid();
383 struct ceph_mount_info
*cmount
;
387 sprintf(c_file
, "recordlock_test_%d", mypid
);
389 Inode
*root
= NULL
, *inode
= NULL
;
390 struct ceph_statx stx
;
393 UserPerm
*perms
= ceph_mount_perms(cmount
);
395 // Get the root inode
396 rc
= ceph_ll_lookup_root(cmount
, &root
);
399 // Get the inode and Fh corresponding to c_file
400 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
401 &inode
, &fh
, &stx
, 0, 0, perms
);
405 lock1
.l_type
= F_WRLCK
;
406 lock1
.l_whence
= SEEK_SET
;
409 lock1
.l_pid
= getpid();
410 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
412 // Start locker thread
415 str_ConcurrentRecordLocking s
= { c_file
, cmount
};
417 ASSERT_EQ(0, pthread_create(&thread
, NULL
, thread_ConcurrentRecordLocking_
, &s
));
418 // Synchronization point with thread (failure: thread is dead)
419 WAIT_WORKER(1); // (1)
421 // Shall not have lock immediately
422 NOT_WAIT_WORKER(2); // (2)
425 lock1
.l_type
= F_UNLCK
;
426 lock1
.l_whence
= SEEK_SET
;
429 lock1
.l_pid
= getpid();
430 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
433 // Synchronization point with thread (failure: thread is dead)
434 WAIT_WORKER(2); // (2)
436 // Synchronization point with thread (failure: thread is dead)
437 WAIT_WORKER(3); // (3)
439 // Wait for thread to share lock
440 WAIT_WORKER(4); // (4)
441 lock1
.l_type
= F_WRLCK
;
442 lock1
.l_whence
= SEEK_SET
;
445 lock1
.l_pid
= getpid();
446 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
447 lock1
.l_type
= F_RDLCK
;
448 lock1
.l_whence
= SEEK_SET
;
451 lock1
.l_pid
= getpid();
452 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
454 // Wake up thread to unlock shared lock
455 PING_WORKER(1); // (R1)
456 WAIT_WORKER(5); // (5)
458 // Now we can lock exclusively
459 // Upgrade to exclusive lock (as per POSIX)
460 lock1
.l_type
= F_WRLCK
;
461 lock1
.l_whence
= SEEK_SET
;
464 lock1
.l_pid
= getpid();
465 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
467 // Wake up thread to lock shared lock
468 PING_WORKER(2); // (R2)
470 // Shall not have lock immediately
471 NOT_WAIT_WORKER(6); // (6)
473 // Release lock ; thread will get it
474 lock1
.l_type
= F_UNLCK
;
475 lock1
.l_whence
= SEEK_SET
;
478 lock1
.l_pid
= getpid();
479 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
480 WAIT_WORKER(6); // (6)
482 // We no longer have the lock
483 lock1
.l_type
= F_WRLCK
;
484 lock1
.l_whence
= SEEK_SET
;
487 lock1
.l_pid
= getpid();
488 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
489 lock1
.l_type
= F_RDLCK
;
490 lock1
.l_whence
= SEEK_SET
;
493 lock1
.l_pid
= getpid();
494 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
496 // Wake up thread to unlock exclusive lock
497 PING_WORKER(3); // (R3)
498 WAIT_WORKER(7); // (7)
500 // We can lock it again
501 lock1
.l_type
= F_WRLCK
;
502 lock1
.l_whence
= SEEK_SET
;
505 lock1
.l_pid
= getpid();
506 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
507 lock1
.l_type
= F_UNLCK
;
508 lock1
.l_whence
= SEEK_SET
;
511 lock1
.l_pid
= getpid();
512 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
515 void *retval
= (void*) (uintptr_t) -1;
516 ASSERT_EQ(0, pthread_join(thread
, &retval
));
517 ASSERT_EQ(NULL
, retval
);
519 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
520 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
524 TEST(LibCephFS
, ThreesomeRecordLocking
) {
525 const pid_t mypid
= getpid();
526 struct ceph_mount_info
*cmount
;
530 sprintf(c_file
, "recordlock_test_%d", mypid
);
532 Inode
*root
= NULL
, *inode
= NULL
;
533 struct ceph_statx stx
;
536 UserPerm
*perms
= ceph_mount_perms(cmount
);
538 // Get the root inode
539 rc
= ceph_ll_lookup_root(cmount
, &root
);
542 // Get the inode and Fh corresponding to c_file
543 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
544 &inode
, &fh
, &stx
, 0, 0, perms
);
548 lock1
.l_type
= F_WRLCK
;
549 lock1
.l_whence
= SEEK_SET
;
552 lock1
.l_pid
= getpid();
553 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
555 // Start locker thread
558 str_ConcurrentRecordLocking s
= { c_file
, cmount
};
560 ASSERT_EQ(0, pthread_create(&thread
[0], NULL
, thread_ConcurrentRecordLocking_
, &s
));
561 ASSERT_EQ(0, pthread_create(&thread
[1], NULL
, thread_ConcurrentRecordLocking_
, &s
));
562 // Synchronization point with thread (failure: thread is dead)
563 TWICE(WAIT_WORKER(1)); // (1)
565 // Shall not have lock immediately
566 NOT_WAIT_WORKER(2); // (2)
569 lock1
.l_type
= F_UNLCK
;
570 lock1
.l_whence
= SEEK_SET
;
573 lock1
.l_pid
= getpid();
574 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
577 TWICE(// Synchronization point with thread (failure: thread is dead)
578 WAIT_WORKER(2); // (2)
580 // Synchronization point with thread (failure: thread is dead)
581 WAIT_WORKER(3)); // (3)
583 // Wait for thread to share lock
584 TWICE(WAIT_WORKER(4)); // (4)
585 lock1
.l_type
= F_WRLCK
;
586 lock1
.l_whence
= SEEK_SET
;
589 lock1
.l_pid
= getpid();
590 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
591 lock1
.l_type
= F_RDLCK
;
592 lock1
.l_whence
= SEEK_SET
;
595 lock1
.l_pid
= getpid();
596 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
598 // Wake up thread to unlock shared lock
599 TWICE(PING_WORKER(1); // (R1)
600 WAIT_WORKER(5)); // (5)
602 // Now we can lock exclusively
603 // Upgrade to exclusive lock (as per POSIX)
604 lock1
.l_type
= F_WRLCK
;
605 lock1
.l_whence
= SEEK_SET
;
608 lock1
.l_pid
= getpid();
609 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), true));
611 TWICE( // Wake up thread to lock shared lock
612 PING_WORKER(2); // (R2)
614 // Shall not have lock immediately
615 NOT_WAIT_WORKER(6)); // (6)
617 // Release lock ; thread will get it
618 lock1
.l_type
= F_UNLCK
;
619 lock1
.l_whence
= SEEK_SET
;
622 lock1
.l_pid
= getpid();
623 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
624 TWICE(WAIT_WORKER(6); // (6)
626 // We no longer have the lock
627 lock1
.l_type
= F_WRLCK
;
628 lock1
.l_whence
= SEEK_SET
;
631 lock1
.l_pid
= getpid();
632 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
633 lock1
.l_type
= F_RDLCK
;
634 lock1
.l_whence
= SEEK_SET
;
637 lock1
.l_pid
= getpid();
638 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
640 // Wake up thread to unlock exclusive lock
641 PING_WORKER(3); // (R3)
642 WAIT_WORKER(7); // (7)
645 // We can lock it again
646 lock1
.l_type
= F_WRLCK
;
647 lock1
.l_whence
= SEEK_SET
;
650 lock1
.l_pid
= getpid();
651 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
652 lock1
.l_type
= F_UNLCK
;
653 lock1
.l_whence
= SEEK_SET
;
656 lock1
.l_pid
= getpid();
657 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
660 void *retval
= (void*) (uintptr_t) -1;
661 ASSERT_EQ(0, pthread_join(thread
[0], &retval
));
662 ASSERT_EQ(NULL
, retval
);
663 ASSERT_EQ(0, pthread_join(thread
[1], &retval
));
664 ASSERT_EQ(NULL
, retval
);
666 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
667 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
671 /* Locking in different processes */
673 #define PROCESS_SLOW_MS() \
674 static const long waitMs = 100; \
677 // Used by ConcurrentLocking test
678 static void process_ConcurrentRecordLocking(str_ConcurrentRecordLocking
& s
) {
679 const pid_t mypid
= getpid();
682 struct ceph_mount_info
*cmount
= NULL
;
685 Inode
*root
= NULL
, *inode
= NULL
;
686 struct ceph_statx stx
;
693 // Get the root inode
694 rc
= ceph_ll_lookup_root(cmount
, &root
);
697 // Get the inode and Fh corresponding to c_file
698 rc
= ceph_ll_create(cmount
, root
, s
.file
, fileMode
, O_RDWR
| O_CREAT
,
699 &inode
, &fh
, &stx
, 0, 0, ceph_mount_perms(cmount
));
702 WAIT_MAIN(1); // (R1)
704 lock1
.l_type
= F_WRLCK
;
705 lock1
.l_whence
= SEEK_SET
;
708 lock1
.l_pid
= getpid();
709 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
711 lock1
.l_type
= F_WRLCK
;
712 lock1
.l_whence
= SEEK_SET
;
715 lock1
.l_pid
= getpid();
716 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
719 lock1
.l_type
= F_UNLCK
;
720 lock1
.l_whence
= SEEK_SET
;
723 lock1
.l_pid
= getpid();
724 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
727 lock1
.l_type
= F_RDLCK
;
728 lock1
.l_whence
= SEEK_SET
;
731 lock1
.l_pid
= getpid();
732 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
735 WAIT_MAIN(2); // (R2)
736 lock1
.l_type
= F_UNLCK
;
737 lock1
.l_whence
= SEEK_SET
;
740 lock1
.l_pid
= getpid();
741 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
744 WAIT_MAIN(3); // (R3)
745 lock1
.l_type
= F_WRLCK
;
746 lock1
.l_whence
= SEEK_SET
;
749 lock1
.l_pid
= getpid();
750 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
753 WAIT_MAIN(4); // (R4)
754 lock1
.l_type
= F_UNLCK
;
755 lock1
.l_whence
= SEEK_SET
;
758 lock1
.l_pid
= getpid();
759 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
762 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
769 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
771 TEST(LibCephFS
, DISABLED_InterProcessRecordLocking
) {
773 // Process synchronization
775 const pid_t mypid
= getpid();
776 sprintf(c_file
, "recordlock_test_%d", mypid
);
778 Inode
*root
= NULL
, *inode
= NULL
;
779 struct ceph_statx stx
;
783 // Note: the semaphores MUST be on a shared memory segment
784 str_ConcurrentRecordLocking
*const shs
=
785 reinterpret_cast<str_ConcurrentRecordLocking
*>
786 (mmap(0, sizeof(*shs
), PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANONYMOUS
,
788 str_ConcurrentRecordLocking
&s
= *shs
;
792 // Start locker process
793 const pid_t pid
= fork();
796 process_ConcurrentRecordLocking(s
);
801 struct ceph_mount_info
*cmount
;
803 UserPerm
*perms
= ceph_mount_perms(cmount
);
805 // Get the root inode
806 rc
= ceph_ll_lookup_root(cmount
, &root
);
809 // Get the inode and Fh corresponding to c_file
810 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
811 &inode
, &fh
, &stx
, 0, 0, perms
);
815 lock1
.l_type
= F_WRLCK
;
816 lock1
.l_whence
= SEEK_SET
;
819 lock1
.l_pid
= getpid();
820 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
822 // Synchronization point with process (failure: process is dead)
823 PING_WORKER(1); // (R1)
824 WAIT_WORKER(1); // (1)
826 // Shall not have lock immediately
827 NOT_WAIT_WORKER(2); // (2)
830 lock1
.l_type
= F_UNLCK
;
831 lock1
.l_whence
= SEEK_SET
;
834 lock1
.l_pid
= getpid();
835 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
838 // Synchronization point with process (failure: process is dead)
839 WAIT_WORKER(2); // (2)
841 // Synchronization point with process (failure: process is dead)
842 WAIT_WORKER(3); // (3)
844 // Wait for process to share lock
845 WAIT_WORKER(4); // (4)
846 lock1
.l_type
= F_WRLCK
;
847 lock1
.l_whence
= SEEK_SET
;
850 lock1
.l_pid
= getpid();
851 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
852 lock1
.l_type
= F_RDLCK
;
853 lock1
.l_whence
= SEEK_SET
;
856 lock1
.l_pid
= getpid();
857 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
859 // Wake up process to unlock shared lock
860 PING_WORKER(2); // (R2)
861 WAIT_WORKER(5); // (5)
863 // Now we can lock exclusively
864 // Upgrade to exclusive lock (as per POSIX)
865 lock1
.l_type
= F_WRLCK
;
866 lock1
.l_whence
= SEEK_SET
;
869 lock1
.l_pid
= getpid();
870 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
872 // Wake up process to lock shared lock
873 PING_WORKER(3); // (R3)
875 // Shall not have lock immediately
876 NOT_WAIT_WORKER(6); // (6)
878 // Release lock ; process will get it
879 lock1
.l_type
= F_UNLCK
;
880 lock1
.l_whence
= SEEK_SET
;
883 lock1
.l_pid
= getpid();
884 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
885 WAIT_WORKER(6); // (6)
887 // We no longer have the lock
888 lock1
.l_type
= F_WRLCK
;
889 lock1
.l_whence
= SEEK_SET
;
892 lock1
.l_pid
= getpid();
893 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
894 lock1
.l_type
= F_RDLCK
;
895 lock1
.l_whence
= SEEK_SET
;
898 lock1
.l_pid
= getpid();
899 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
901 // Wake up process to unlock exclusive lock
902 PING_WORKER(4); // (R4)
903 WAIT_WORKER(7); // (7)
905 // We can lock it again
906 lock1
.l_type
= F_WRLCK
;
907 lock1
.l_whence
= SEEK_SET
;
910 lock1
.l_pid
= getpid();
911 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
912 lock1
.l_type
= F_UNLCK
;
913 lock1
.l_whence
= SEEK_SET
;
916 lock1
.l_pid
= getpid();
917 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
921 ASSERT_EQ(pid
, waitpid(pid
, &status
, 0));
922 ASSERT_EQ(EXIT_SUCCESS
, status
);
926 ASSERT_EQ(0, munmap(shs
, sizeof(*shs
)));
927 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
928 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
934 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
935 TEST(LibCephFS
, DISABLED_ThreesomeInterProcessRecordLocking
) {
937 // Process synchronization
939 const pid_t mypid
= getpid();
940 sprintf(c_file
, "recordlock_test_%d", mypid
);
942 Inode
*root
= NULL
, *inode
= NULL
;
943 struct ceph_statx stx
;
947 // Note: the semaphores MUST be on a shared memory segment
948 str_ConcurrentRecordLocking
*const shs
=
949 reinterpret_cast<str_ConcurrentRecordLocking
*>
950 (mmap(0, sizeof(*shs
), PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANONYMOUS
,
952 str_ConcurrentRecordLocking
&s
= *shs
;
956 // Start locker processes
959 ASSERT_GE(pid
[0], 0);
961 process_ConcurrentRecordLocking(s
);
965 ASSERT_GE(pid
[1], 0);
967 process_ConcurrentRecordLocking(s
);
972 struct ceph_mount_info
*cmount
;
975 // Get the root inode
976 rc
= ceph_ll_lookup_root(cmount
, &root
);
979 // Get the inode and Fh corresponding to c_file
980 UserPerm
*perms
= ceph_mount_perms(cmount
);
981 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
982 &inode
, &fh
, &stx
, 0, 0, perms
);
986 lock1
.l_type
= F_WRLCK
;
987 lock1
.l_whence
= SEEK_SET
;
990 lock1
.l_pid
= getpid();
991 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
993 // Synchronization point with process (failure: process is dead)
994 TWICE(PING_WORKER(1)); // (R1)
995 TWICE(WAIT_WORKER(1)); // (1)
997 // Shall not have lock immediately
998 NOT_WAIT_WORKER(2); // (2)
1001 lock1
.l_type
= F_UNLCK
;
1002 lock1
.l_whence
= SEEK_SET
;
1005 lock1
.l_pid
= getpid();
1006 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1009 TWICE(// Synchronization point with process (failure: process is dead)
1010 WAIT_WORKER(2); // (2)
1012 // Synchronization point with process (failure: process is dead)
1013 WAIT_WORKER(3)); // (3)
1015 // Wait for process to share lock
1016 TWICE(WAIT_WORKER(4)); // (4)
1017 lock1
.l_type
= F_WRLCK
;
1018 lock1
.l_whence
= SEEK_SET
;
1021 lock1
.l_pid
= getpid();
1022 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1023 lock1
.l_type
= F_RDLCK
;
1024 lock1
.l_whence
= SEEK_SET
;
1027 lock1
.l_pid
= getpid();
1028 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1030 // Wake up process to unlock shared lock
1031 TWICE(PING_WORKER(2); // (R2)
1032 WAIT_WORKER(5)); // (5)
1034 // Now we can lock exclusively
1035 // Upgrade to exclusive lock (as per POSIX)
1036 lock1
.l_type
= F_WRLCK
;
1037 lock1
.l_whence
= SEEK_SET
;
1040 lock1
.l_pid
= getpid();
1041 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
1043 TWICE( // Wake up process to lock shared lock
1044 PING_WORKER(3); // (R3)
1046 // Shall not have lock immediately
1047 NOT_WAIT_WORKER(6)); // (6)
1049 // Release lock ; process will get it
1050 lock1
.l_type
= F_UNLCK
;
1051 lock1
.l_whence
= SEEK_SET
;
1054 lock1
.l_pid
= getpid();
1055 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1056 TWICE(WAIT_WORKER(6); // (6)
1058 // We no longer have the lock
1059 lock1
.l_type
= F_WRLCK
;
1060 lock1
.l_whence
= SEEK_SET
;
1063 lock1
.l_pid
= getpid();
1064 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
1065 lock1
.l_type
= F_RDLCK
;
1066 lock1
.l_whence
= SEEK_SET
;
1069 lock1
.l_pid
= getpid();
1070 ASSERT_EQ(-CEPHFS_EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, ceph_pthread_self(), false));
1072 // Wake up process to unlock exclusive lock
1073 PING_WORKER(4); // (R4)
1074 WAIT_WORKER(7); // (7)
1077 // We can lock it again
1078 lock1
.l_type
= F_WRLCK
;
1079 lock1
.l_whence
= SEEK_SET
;
1082 lock1
.l_pid
= getpid();
1083 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1084 lock1
.l_type
= F_UNLCK
;
1085 lock1
.l_whence
= SEEK_SET
;
1088 lock1
.l_pid
= getpid();
1089 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1093 ASSERT_EQ(pid
[0], waitpid(pid
[0], &status
, 0));
1094 ASSERT_EQ(EXIT_SUCCESS
, status
);
1095 ASSERT_EQ(pid
[1], waitpid(pid
[1], &status
, 0));
1096 ASSERT_EQ(EXIT_SUCCESS
, status
);
1100 ASSERT_EQ(0, munmap(shs
, sizeof(*shs
)));
1101 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
1102 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));