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/cephfs/libcephfs.h"
24 #include <sys/fcntl.h>
27 #include <sys/types.h>
30 #include <sys/xattr.h>
33 #include <semaphore.h>
41 // Startup common: create and mount ceph fs
42 #define STARTUP_CEPH() do { \
43 ASSERT_EQ(0, ceph_create(&cmount, NULL)); \
44 ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); \
45 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL)); \
46 ASSERT_EQ(0, ceph_mount(cmount, NULL)); \
49 // Cleanup common: unmount and release ceph fs
50 #define CLEANUP_CEPH() do { \
51 ASSERT_EQ(0, ceph_unmount(cmount)); \
52 ASSERT_EQ(0, ceph_release(cmount)); \
55 static const mode_t fileMode = S_IRWXU | S_IRWXG | S_IRWXO;
57 // Default wait time for normal and "slow" operations
58 // (5" should be enough in case of network congestion)
59 static const long waitMs = 10;
60 static const long waitSlowMs = 5000;
62 // Get the absolute struct timespec reference from now + 'ms' milliseconds
63 static const struct timespec* abstime(struct timespec &ts, long ms) {
64 if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
67 ts.tv_nsec += ms * 1000000;
68 ts.tv_sec += ts.tv_nsec / 1000000000;
69 ts.tv_nsec %= 1000000000;
75 TEST(LibCephFS, BasicRecordLocking) {
76 struct ceph_mount_info *cmount = NULL;
80 sprintf(c_file, "recordlock_test_%d", getpid());
82 Inode *root = NULL, *inode = NULL;
83 struct ceph_statx stx;
85 struct flock lock1, lock2;
86 UserPerm *perms = ceph_mount_perms(cmount);
89 rc = ceph_ll_lookup_root(cmount, &root);
92 // Get the inode and Fh corresponding to c_file
93 rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
94 &inode, &fh, &stx, 0, 0, perms);
98 lock1.l_type = F_WRLCK;
99 lock1.l_whence = SEEK_SET;
102 lock1.l_pid = getpid();
103 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
105 lock2.l_type = F_WRLCK;
106 lock2.l_whence = SEEK_SET;
109 lock2.l_pid = getpid();
110 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock2, 43, false));
112 // Now try a conflicting read lock
113 lock2.l_type = F_RDLCK;
114 lock2.l_whence = SEEK_SET;
117 lock2.l_pid = getpid();
118 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock2, 43, false));
121 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
122 ASSERT_EQ(lock2.l_type, F_WRLCK);
123 ASSERT_EQ(lock2.l_start, 0);
124 ASSERT_EQ(lock2.l_len, 1024);
125 ASSERT_EQ(lock2.l_pid, getpid());
127 // Extend the range of the write lock
128 lock1.l_type = F_WRLCK;
129 lock1.l_whence = SEEK_SET;
130 lock1.l_start = 1024;
132 lock1.l_pid = getpid();
133 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
136 lock2.l_type = F_RDLCK;
137 lock2.l_whence = SEEK_SET;
140 lock2.l_pid = getpid();
141 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
142 ASSERT_EQ(lock2.l_type, F_WRLCK);
143 ASSERT_EQ(lock2.l_start, 0);
144 ASSERT_EQ(lock2.l_len, 2048);
145 ASSERT_EQ(lock2.l_pid, getpid());
147 // Now release part of the range
148 lock1.l_type = F_UNLCK;
149 lock1.l_whence = SEEK_SET;
152 lock1.l_pid = getpid();
153 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
155 // Now do a getlk to check 1st part
156 lock2.l_type = F_RDLCK;
157 lock2.l_whence = SEEK_SET;
160 lock2.l_pid = getpid();
161 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
162 ASSERT_EQ(lock2.l_type, F_WRLCK);
163 ASSERT_EQ(lock2.l_start, 0);
164 ASSERT_EQ(lock2.l_len, 512);
165 ASSERT_EQ(lock2.l_pid, getpid());
167 // Now do a getlk to check 2nd part
168 lock2.l_type = F_RDLCK;
169 lock2.l_whence = SEEK_SET;
170 lock2.l_start = 2000;
172 lock2.l_pid = getpid();
173 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
174 ASSERT_EQ(lock2.l_type, F_WRLCK);
175 ASSERT_EQ(lock2.l_start, 1536);
176 ASSERT_EQ(lock2.l_len, 512);
177 ASSERT_EQ(lock2.l_pid, getpid());
179 // Now do a getlk to check released part
180 lock2.l_type = F_RDLCK;
181 lock2.l_whence = SEEK_SET;
184 lock2.l_pid = getpid();
185 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
186 ASSERT_EQ(lock2.l_type, F_UNLCK);
187 ASSERT_EQ(lock2.l_start, 512);
188 ASSERT_EQ(lock2.l_len, 1024);
189 ASSERT_EQ(lock2.l_pid, getpid());
191 // Now downgrade the 1st part of the lock
192 lock1.l_type = F_RDLCK;
193 lock1.l_whence = SEEK_SET;
196 lock1.l_pid = getpid();
197 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
199 // Now do a getlk to check 1st part
200 lock2.l_type = F_WRLCK;
201 lock2.l_whence = SEEK_SET;
204 lock2.l_pid = getpid();
205 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
206 ASSERT_EQ(lock2.l_type, F_RDLCK);
207 ASSERT_EQ(lock2.l_start, 0);
208 ASSERT_EQ(lock2.l_len, 512);
209 ASSERT_EQ(lock2.l_pid, getpid());
211 // Now upgrade the 1st part of the lock
212 lock1.l_type = F_WRLCK;
213 lock1.l_whence = SEEK_SET;
216 lock1.l_pid = getpid();
217 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, 42, false));
219 // Now do a getlk to check 1st part
220 lock2.l_type = F_WRLCK;
221 lock2.l_whence = SEEK_SET;
224 lock2.l_pid = getpid();
225 ASSERT_EQ(0, ceph_ll_getlk(cmount, fh, &lock2, 43));
226 ASSERT_EQ(lock2.l_type, F_WRLCK);
227 ASSERT_EQ(lock2.l_start, 0);
228 ASSERT_EQ(lock2.l_len, 512);
229 ASSERT_EQ(lock2.l_pid, getpid());
231 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
232 ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
236 /* Locking in different threads */
238 // Used by ConcurrentLocking test
239 struct str_ConcurrentRecordLocking {
241 struct ceph_mount_info *cmount; // !NULL if shared
244 void sem_init(int pshared) {
245 ASSERT_EQ(0, ::sem_init(&sem[0], pshared, 0));
246 ASSERT_EQ(0, ::sem_init(&sem[1], pshared, 0));
247 ASSERT_EQ(0, ::sem_init(&semReply[0], pshared, 0));
248 ASSERT_EQ(0, ::sem_init(&semReply[1], pshared, 0));
251 ASSERT_EQ(0, ::sem_destroy(&sem[0]));
252 ASSERT_EQ(0, ::sem_destroy(&sem[1]));
253 ASSERT_EQ(0, ::sem_destroy(&semReply[0]));
254 ASSERT_EQ(0, ::sem_destroy(&semReply[1]));
258 // Wakeup main (for (N) steps)
259 #define PING_MAIN(n) ASSERT_EQ(0, sem_post(&s.sem[n%2]))
260 // Wait for main to wake us up (for (RN) steps)
261 #define WAIT_MAIN(n) \
262 ASSERT_EQ(0, sem_timedwait(&s.semReply[n%2], abstime(ts, waitSlowMs)))
264 // Wakeup worker (for (RN) steps)
265 #define PING_WORKER(n) ASSERT_EQ(0, sem_post(&s.semReply[n%2]))
266 // Wait for worker to wake us up (for (N) steps)
267 #define WAIT_WORKER(n) \
268 ASSERT_EQ(0, sem_timedwait(&s.sem[n%2], abstime(ts, waitSlowMs)))
269 // Worker shall not wake us up (for (N) steps)
270 #define NOT_WAIT_WORKER(n) \
271 ASSERT_EQ(-1, sem_timedwait(&s.sem[n%2], abstime(ts, waitMs)))
273 // Do twice an operation
274 #define TWICE(EXPR) do { \
279 /* Locking in different threads */
281 // Used by ConcurrentLocking test
282 static void thread_ConcurrentRecordLocking(str_ConcurrentRecordLocking& s) {
283 struct ceph_mount_info *const cmount = s.cmount;
285 Inode *root = NULL, *inode = NULL;
286 struct ceph_statx stx;
291 // Get the root inode
292 rc = ceph_ll_lookup_root(cmount, &root);
295 // Get the inode and Fh corresponding to c_file
296 rc = ceph_ll_create(cmount, root, s.file, fileMode, O_RDWR | O_CREAT,
297 &inode, &fh, &stx, 0, 0, ceph_mount_perms(cmount));
300 lock1.l_type = F_WRLCK;
301 lock1.l_whence = SEEK_SET;
304 lock1.l_pid = getpid();
305 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
308 lock1.l_type = F_WRLCK;
309 lock1.l_whence = SEEK_SET;
312 lock1.l_pid = getpid();
313 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
316 lock1.l_type = F_UNLCK;
317 lock1.l_whence = SEEK_SET;
320 lock1.l_pid = getpid();
321 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
324 lock1.l_type = F_RDLCK;
325 lock1.l_whence = SEEK_SET;
328 lock1.l_pid = getpid();
329 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
332 WAIT_MAIN(1); // (R1)
333 lock1.l_type = F_UNLCK;
334 lock1.l_whence = SEEK_SET;
337 lock1.l_pid = getpid();
338 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
341 WAIT_MAIN(2); // (R2)
342 lock1.l_type = F_WRLCK;
343 lock1.l_whence = SEEK_SET;
346 lock1.l_pid = getpid();
347 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
350 WAIT_MAIN(3); // (R3)
351 lock1.l_type = F_UNLCK;
352 lock1.l_whence = SEEK_SET;
355 lock1.l_pid = getpid();
356 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
359 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
362 // Used by ConcurrentRecordLocking test
363 static void* thread_ConcurrentRecordLocking_(void *arg) {
364 str_ConcurrentRecordLocking *const s =
365 reinterpret_cast<str_ConcurrentRecordLocking*>(arg);
366 thread_ConcurrentRecordLocking(*s);
370 TEST(LibCephFS, ConcurrentRecordLocking) {
371 const pid_t mypid = getpid();
372 struct ceph_mount_info *cmount;
376 sprintf(c_file, "recordlock_test_%d", mypid);
378 Inode *root = NULL, *inode = NULL;
379 struct ceph_statx stx;
382 UserPerm *perms = ceph_mount_perms(cmount);
384 // Get the root inode
385 rc = ceph_ll_lookup_root(cmount, &root);
388 // Get the inode and Fh corresponding to c_file
389 rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
390 &inode, &fh, &stx, 0, 0, perms);
394 lock1.l_type = F_WRLCK;
395 lock1.l_whence = SEEK_SET;
398 lock1.l_pid = getpid();
399 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
401 // Start locker thread
404 str_ConcurrentRecordLocking s = { c_file, cmount };
406 ASSERT_EQ(0, pthread_create(&thread, NULL, thread_ConcurrentRecordLocking_, &s));
407 // Synchronization point with thread (failure: thread is dead)
408 WAIT_WORKER(1); // (1)
410 // Shall not have lock immediately
411 NOT_WAIT_WORKER(2); // (2)
414 lock1.l_type = F_UNLCK;
415 lock1.l_whence = SEEK_SET;
418 lock1.l_pid = getpid();
419 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
422 // Synchronization point with thread (failure: thread is dead)
423 WAIT_WORKER(2); // (2)
425 // Synchronization point with thread (failure: thread is dead)
426 WAIT_WORKER(3); // (3)
428 // Wait for thread to share lock
429 WAIT_WORKER(4); // (4)
430 lock1.l_type = F_WRLCK;
431 lock1.l_whence = SEEK_SET;
434 lock1.l_pid = getpid();
435 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
436 lock1.l_type = F_RDLCK;
437 lock1.l_whence = SEEK_SET;
440 lock1.l_pid = getpid();
441 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
443 // Wake up thread to unlock shared lock
444 PING_WORKER(1); // (R1)
445 WAIT_WORKER(5); // (5)
447 // Now we can lock exclusively
448 // Upgrade to exclusive lock (as per POSIX)
449 lock1.l_type = F_WRLCK;
450 lock1.l_whence = SEEK_SET;
453 lock1.l_pid = getpid();
454 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
456 // Wake up thread to lock shared lock
457 PING_WORKER(2); // (R2)
459 // Shall not have lock immediately
460 NOT_WAIT_WORKER(6); // (6)
462 // Release lock ; thread will get it
463 lock1.l_type = F_UNLCK;
464 lock1.l_whence = SEEK_SET;
467 lock1.l_pid = getpid();
468 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
469 WAIT_WORKER(6); // (6)
471 // We no longer have the lock
472 lock1.l_type = F_WRLCK;
473 lock1.l_whence = SEEK_SET;
476 lock1.l_pid = getpid();
477 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
478 lock1.l_type = F_RDLCK;
479 lock1.l_whence = SEEK_SET;
482 lock1.l_pid = getpid();
483 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
485 // Wake up thread to unlock exclusive lock
486 PING_WORKER(3); // (R3)
487 WAIT_WORKER(7); // (7)
489 // We can lock it again
490 lock1.l_type = F_WRLCK;
491 lock1.l_whence = SEEK_SET;
494 lock1.l_pid = getpid();
495 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
496 lock1.l_type = F_UNLCK;
497 lock1.l_whence = SEEK_SET;
500 lock1.l_pid = getpid();
501 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
504 void *retval = (void*) (uintptr_t) -1;
505 ASSERT_EQ(0, pthread_join(thread, &retval));
506 ASSERT_EQ(NULL, retval);
508 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
509 ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
513 TEST(LibCephFS, ThreesomeRecordLocking) {
514 const pid_t mypid = getpid();
515 struct ceph_mount_info *cmount;
519 sprintf(c_file, "recordlock_test_%d", mypid);
521 Inode *root = NULL, *inode = NULL;
522 struct ceph_statx stx;
525 UserPerm *perms = ceph_mount_perms(cmount);
527 // Get the root inode
528 rc = ceph_ll_lookup_root(cmount, &root);
531 // Get the inode and Fh corresponding to c_file
532 rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
533 &inode, &fh, &stx, 0, 0, perms);
537 lock1.l_type = F_WRLCK;
538 lock1.l_whence = SEEK_SET;
541 lock1.l_pid = getpid();
542 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
544 // Start locker thread
547 str_ConcurrentRecordLocking s = { c_file, cmount };
549 ASSERT_EQ(0, pthread_create(&thread[0], NULL, thread_ConcurrentRecordLocking_, &s));
550 ASSERT_EQ(0, pthread_create(&thread[1], NULL, thread_ConcurrentRecordLocking_, &s));
551 // Synchronization point with thread (failure: thread is dead)
552 TWICE(WAIT_WORKER(1)); // (1)
554 // Shall not have lock immediately
555 NOT_WAIT_WORKER(2); // (2)
558 lock1.l_type = F_UNLCK;
559 lock1.l_whence = SEEK_SET;
562 lock1.l_pid = getpid();
563 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
566 TWICE(// Synchronization point with thread (failure: thread is dead)
567 WAIT_WORKER(2); // (2)
569 // Synchronization point with thread (failure: thread is dead)
570 WAIT_WORKER(3)); // (3)
572 // Wait for thread to share lock
573 TWICE(WAIT_WORKER(4)); // (4)
574 lock1.l_type = F_WRLCK;
575 lock1.l_whence = SEEK_SET;
578 lock1.l_pid = getpid();
579 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
580 lock1.l_type = F_RDLCK;
581 lock1.l_whence = SEEK_SET;
584 lock1.l_pid = getpid();
585 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
587 // Wake up thread to unlock shared lock
588 TWICE(PING_WORKER(1); // (R1)
589 WAIT_WORKER(5)); // (5)
591 // Now we can lock exclusively
592 // Upgrade to exclusive lock (as per POSIX)
593 lock1.l_type = F_WRLCK;
594 lock1.l_whence = SEEK_SET;
597 lock1.l_pid = getpid();
598 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), true));
600 TWICE( // Wake up thread to lock shared lock
601 PING_WORKER(2); // (R2)
603 // Shall not have lock immediately
604 NOT_WAIT_WORKER(6)); // (6)
606 // Release lock ; thread will get it
607 lock1.l_type = F_UNLCK;
608 lock1.l_whence = SEEK_SET;
611 lock1.l_pid = getpid();
612 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
613 TWICE(WAIT_WORKER(6); // (6)
615 // We no longer have the lock
616 lock1.l_type = F_WRLCK;
617 lock1.l_whence = SEEK_SET;
620 lock1.l_pid = getpid();
621 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
622 lock1.l_type = F_RDLCK;
623 lock1.l_whence = SEEK_SET;
626 lock1.l_pid = getpid();
627 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
629 // Wake up thread to unlock exclusive lock
630 PING_WORKER(3); // (R3)
631 WAIT_WORKER(7); // (7)
634 // We can lock it again
635 lock1.l_type = F_WRLCK;
636 lock1.l_whence = SEEK_SET;
639 lock1.l_pid = getpid();
640 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
641 lock1.l_type = F_UNLCK;
642 lock1.l_whence = SEEK_SET;
645 lock1.l_pid = getpid();
646 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
649 void *retval = (void*) (uintptr_t) -1;
650 ASSERT_EQ(0, pthread_join(thread[0], &retval));
651 ASSERT_EQ(NULL, retval);
652 ASSERT_EQ(0, pthread_join(thread[1], &retval));
653 ASSERT_EQ(NULL, retval);
655 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
656 ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
660 /* Locking in different processes */
662 #define PROCESS_SLOW_MS() \
663 static const long waitMs = 100; \
666 // Used by ConcurrentLocking test
667 static void process_ConcurrentRecordLocking(str_ConcurrentRecordLocking& s) {
668 const pid_t mypid = getpid();
671 struct ceph_mount_info *cmount = NULL;
674 Inode *root = NULL, *inode = NULL;
675 struct ceph_statx stx;
682 // Get the root inode
683 rc = ceph_ll_lookup_root(cmount, &root);
686 // Get the inode and Fh corresponding to c_file
687 rc = ceph_ll_create(cmount, root, s.file, fileMode, O_RDWR | O_CREAT,
688 &inode, &fh, &stx, 0, 0, ceph_mount_perms(cmount));
691 WAIT_MAIN(1); // (R1)
693 lock1.l_type = F_WRLCK;
694 lock1.l_whence = SEEK_SET;
697 lock1.l_pid = getpid();
698 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
700 lock1.l_type = F_WRLCK;
701 lock1.l_whence = SEEK_SET;
704 lock1.l_pid = getpid();
705 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
708 lock1.l_type = F_UNLCK;
709 lock1.l_whence = SEEK_SET;
712 lock1.l_pid = getpid();
713 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
716 lock1.l_type = F_RDLCK;
717 lock1.l_whence = SEEK_SET;
720 lock1.l_pid = getpid();
721 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
724 WAIT_MAIN(2); // (R2)
725 lock1.l_type = F_UNLCK;
726 lock1.l_whence = SEEK_SET;
729 lock1.l_pid = getpid();
730 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
733 WAIT_MAIN(3); // (R3)
734 lock1.l_type = F_WRLCK;
735 lock1.l_whence = SEEK_SET;
738 lock1.l_pid = getpid();
739 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
742 WAIT_MAIN(4); // (R4)
743 lock1.l_type = F_UNLCK;
744 lock1.l_whence = SEEK_SET;
747 lock1.l_pid = getpid();
748 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
751 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
758 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
759 TEST(LibCephFS, DISABLED_InterProcessRecordLocking) {
761 // Process synchronization
763 const pid_t mypid = getpid();
764 sprintf(c_file, "recordlock_test_%d", mypid);
766 Inode *root = NULL, *inode = NULL;
767 struct ceph_statx stx;
771 // Note: the semaphores MUST be on a shared memory segment
772 str_ConcurrentRecordLocking *const shs =
773 reinterpret_cast<str_ConcurrentRecordLocking*>
774 (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
776 str_ConcurrentRecordLocking &s = *shs;
780 // Start locker process
781 const pid_t pid = fork();
784 process_ConcurrentRecordLocking(s);
789 struct ceph_mount_info *cmount;
791 UserPerm *perms = ceph_mount_perms(cmount);
793 // Get the root inode
794 rc = ceph_ll_lookup_root(cmount, &root);
797 // Get the inode and Fh corresponding to c_file
798 rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
799 &inode, &fh, &stx, 0, 0, perms);
803 lock1.l_type = F_WRLCK;
804 lock1.l_whence = SEEK_SET;
807 lock1.l_pid = getpid();
808 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
810 // Synchronization point with process (failure: process is dead)
811 PING_WORKER(1); // (R1)
812 WAIT_WORKER(1); // (1)
814 // Shall not have lock immediately
815 NOT_WAIT_WORKER(2); // (2)
818 lock1.l_type = F_UNLCK;
819 lock1.l_whence = SEEK_SET;
822 lock1.l_pid = getpid();
823 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
826 // Synchronization point with process (failure: process is dead)
827 WAIT_WORKER(2); // (2)
829 // Synchronization point with process (failure: process is dead)
830 WAIT_WORKER(3); // (3)
832 // Wait for process to share lock
833 WAIT_WORKER(4); // (4)
834 lock1.l_type = F_WRLCK;
835 lock1.l_whence = SEEK_SET;
838 lock1.l_pid = getpid();
839 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
840 lock1.l_type = F_RDLCK;
841 lock1.l_whence = SEEK_SET;
844 lock1.l_pid = getpid();
845 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
847 // Wake up process to unlock shared lock
848 PING_WORKER(2); // (R2)
849 WAIT_WORKER(5); // (5)
851 // Now we can lock exclusively
852 // Upgrade to exclusive lock (as per POSIX)
853 lock1.l_type = F_WRLCK;
854 lock1.l_whence = SEEK_SET;
857 lock1.l_pid = getpid();
858 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
860 // Wake up process to lock shared lock
861 PING_WORKER(3); // (R3)
863 // Shall not have lock immediately
864 NOT_WAIT_WORKER(6); // (6)
866 // Release lock ; process will get it
867 lock1.l_type = F_UNLCK;
868 lock1.l_whence = SEEK_SET;
871 lock1.l_pid = getpid();
872 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
873 WAIT_WORKER(6); // (6)
875 // We no longer have the lock
876 lock1.l_type = F_WRLCK;
877 lock1.l_whence = SEEK_SET;
880 lock1.l_pid = getpid();
881 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
882 lock1.l_type = F_RDLCK;
883 lock1.l_whence = SEEK_SET;
886 lock1.l_pid = getpid();
887 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
889 // Wake up process to unlock exclusive lock
890 PING_WORKER(4); // (R4)
891 WAIT_WORKER(7); // (7)
893 // We can lock it again
894 lock1.l_type = F_WRLCK;
895 lock1.l_whence = SEEK_SET;
898 lock1.l_pid = getpid();
899 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
900 lock1.l_type = F_UNLCK;
901 lock1.l_whence = SEEK_SET;
904 lock1.l_pid = getpid();
905 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
909 ASSERT_EQ(pid, waitpid(pid, &status, 0));
910 ASSERT_EQ(EXIT_SUCCESS, status);
914 ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
915 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
916 ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));
920 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
921 TEST(LibCephFS, DISABLED_ThreesomeInterProcessRecordLocking) {
923 // Process synchronization
925 const pid_t mypid = getpid();
926 sprintf(c_file, "recordlock_test_%d", mypid);
928 Inode *root = NULL, *inode = NULL;
929 struct ceph_statx stx;
933 // Note: the semaphores MUST be on a shared memory segment
934 str_ConcurrentRecordLocking *const shs =
935 reinterpret_cast<str_ConcurrentRecordLocking*>
936 (mmap(0, sizeof(*shs), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
938 str_ConcurrentRecordLocking &s = *shs;
942 // Start locker processes
945 ASSERT_GE(pid[0], 0);
947 process_ConcurrentRecordLocking(s);
951 ASSERT_GE(pid[1], 0);
953 process_ConcurrentRecordLocking(s);
958 struct ceph_mount_info *cmount;
961 // Get the root inode
962 rc = ceph_ll_lookup_root(cmount, &root);
965 // Get the inode and Fh corresponding to c_file
966 UserPerm *perms = ceph_mount_perms(cmount);
967 rc = ceph_ll_create(cmount, root, c_file, fileMode, O_RDWR | O_CREAT,
968 &inode, &fh, &stx, 0, 0, perms);
972 lock1.l_type = F_WRLCK;
973 lock1.l_whence = SEEK_SET;
976 lock1.l_pid = getpid();
977 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
979 // Synchronization point with process (failure: process is dead)
980 TWICE(PING_WORKER(1)); // (R1)
981 TWICE(WAIT_WORKER(1)); // (1)
983 // Shall not have lock immediately
984 NOT_WAIT_WORKER(2); // (2)
987 lock1.l_type = F_UNLCK;
988 lock1.l_whence = SEEK_SET;
991 lock1.l_pid = getpid();
992 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
995 TWICE(// Synchronization point with process (failure: process is dead)
996 WAIT_WORKER(2); // (2)
998 // Synchronization point with process (failure: process is dead)
999 WAIT_WORKER(3)); // (3)
1001 // Wait for process to share lock
1002 TWICE(WAIT_WORKER(4)); // (4)
1003 lock1.l_type = F_WRLCK;
1004 lock1.l_whence = SEEK_SET;
1007 lock1.l_pid = getpid();
1008 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
1009 lock1.l_type = F_RDLCK;
1010 lock1.l_whence = SEEK_SET;
1013 lock1.l_pid = getpid();
1014 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
1016 // Wake up process to unlock shared lock
1017 TWICE(PING_WORKER(2); // (R2)
1018 WAIT_WORKER(5)); // (5)
1020 // Now we can lock exclusively
1021 // Upgrade to exclusive lock (as per POSIX)
1022 lock1.l_type = F_WRLCK;
1023 lock1.l_whence = SEEK_SET;
1026 lock1.l_pid = getpid();
1027 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, true));
1029 TWICE( // Wake up process to lock shared lock
1030 PING_WORKER(3); // (R3)
1032 // Shall not have lock immediately
1033 NOT_WAIT_WORKER(6)); // (6)
1035 // Release lock ; process will get it
1036 lock1.l_type = F_UNLCK;
1037 lock1.l_whence = SEEK_SET;
1040 lock1.l_pid = getpid();
1041 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
1042 TWICE(WAIT_WORKER(6); // (6)
1044 // We no longer have the lock
1045 lock1.l_type = F_WRLCK;
1046 lock1.l_whence = SEEK_SET;
1049 lock1.l_pid = getpid();
1050 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
1051 lock1.l_type = F_RDLCK;
1052 lock1.l_whence = SEEK_SET;
1055 lock1.l_pid = getpid();
1056 ASSERT_EQ(-EAGAIN, ceph_ll_setlk(cmount, fh, &lock1, pthread_self(), false));
1058 // Wake up process to unlock exclusive lock
1059 PING_WORKER(4); // (R4)
1060 WAIT_WORKER(7); // (7)
1063 // We can lock it again
1064 lock1.l_type = F_WRLCK;
1065 lock1.l_whence = SEEK_SET;
1068 lock1.l_pid = getpid();
1069 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
1070 lock1.l_type = F_UNLCK;
1071 lock1.l_whence = SEEK_SET;
1074 lock1.l_pid = getpid();
1075 ASSERT_EQ(0, ceph_ll_setlk(cmount, fh, &lock1, mypid, false));
1079 ASSERT_EQ(pid[0], waitpid(pid[0], &status, 0));
1080 ASSERT_EQ(EXIT_SUCCESS, status);
1081 ASSERT_EQ(pid[1], waitpid(pid[1], &status, 0));
1082 ASSERT_EQ(EXIT_SUCCESS, status);
1086 ASSERT_EQ(0, munmap(shs, sizeof(*shs)));
1087 ASSERT_EQ(0, ceph_ll_close(cmount, fh));
1088 ASSERT_EQ(0, ceph_ll_unlink(cmount, root, c_file, perms));