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 #include "include/ceph_assert.h"
43 // Startup common: create and mount ceph fs
44 #define STARTUP_CEPH() do { \
45 ASSERT_EQ(0, ceph_create(&cmount, NULL)); \
46 ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL)); \
47 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL)); \
48 ASSERT_EQ(0, ceph_mount(cmount, NULL)); \
51 // Cleanup common: unmount and release ceph fs
52 #define CLEANUP_CEPH() do { \
53 ASSERT_EQ(0, ceph_unmount(cmount)); \
54 ASSERT_EQ(0, ceph_release(cmount)); \
57 static const mode_t fileMode
= S_IRWXU
| S_IRWXG
| S_IRWXO
;
59 // Default wait time for normal and "slow" operations
60 // (5" should be enough in case of network congestion)
61 static const long waitMs
= 10;
62 static const long waitSlowMs
= 5000;
64 // Get the absolute struct timespec reference from now + 'ms' milliseconds
65 static const struct timespec
* abstime(struct timespec
&ts
, long ms
) {
66 if (clock_gettime(CLOCK_REALTIME
, &ts
) == -1) {
69 ts
.tv_nsec
+= ms
* 1000000;
70 ts
.tv_sec
+= ts
.tv_nsec
/ 1000000000;
71 ts
.tv_nsec
%= 1000000000;
77 TEST(LibCephFS
, BasicRecordLocking
) {
78 struct ceph_mount_info
*cmount
= NULL
;
82 sprintf(c_file
, "recordlock_test_%d", getpid());
84 Inode
*root
= NULL
, *inode
= NULL
;
85 struct ceph_statx stx
;
87 struct flock lock1
, lock2
;
88 UserPerm
*perms
= ceph_mount_perms(cmount
);
91 rc
= ceph_ll_lookup_root(cmount
, &root
);
94 // Get the inode and Fh corresponding to c_file
95 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
96 &inode
, &fh
, &stx
, 0, 0, perms
);
100 lock1
.l_type
= F_WRLCK
;
101 lock1
.l_whence
= SEEK_SET
;
104 lock1
.l_pid
= getpid();
105 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
107 lock2
.l_type
= F_WRLCK
;
108 lock2
.l_whence
= SEEK_SET
;
111 lock2
.l_pid
= getpid();
112 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock2
, 43, false));
114 // Now try a conflicting read lock
115 lock2
.l_type
= F_RDLCK
;
116 lock2
.l_whence
= SEEK_SET
;
119 lock2
.l_pid
= getpid();
120 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock2
, 43, false));
123 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
124 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
125 ASSERT_EQ(lock2
.l_start
, 0);
126 ASSERT_EQ(lock2
.l_len
, 1024);
127 ASSERT_EQ(lock2
.l_pid
, getpid());
129 // Extend the range of the write lock
130 lock1
.l_type
= F_WRLCK
;
131 lock1
.l_whence
= SEEK_SET
;
132 lock1
.l_start
= 1024;
134 lock1
.l_pid
= getpid();
135 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
138 lock2
.l_type
= F_RDLCK
;
139 lock2
.l_whence
= SEEK_SET
;
142 lock2
.l_pid
= getpid();
143 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
144 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
145 ASSERT_EQ(lock2
.l_start
, 0);
146 ASSERT_EQ(lock2
.l_len
, 2048);
147 ASSERT_EQ(lock2
.l_pid
, getpid());
149 // Now release part of the range
150 lock1
.l_type
= F_UNLCK
;
151 lock1
.l_whence
= SEEK_SET
;
154 lock1
.l_pid
= getpid();
155 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
157 // Now do a getlk to check 1st part
158 lock2
.l_type
= F_RDLCK
;
159 lock2
.l_whence
= SEEK_SET
;
162 lock2
.l_pid
= getpid();
163 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
164 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
165 ASSERT_EQ(lock2
.l_start
, 0);
166 ASSERT_EQ(lock2
.l_len
, 512);
167 ASSERT_EQ(lock2
.l_pid
, getpid());
169 // Now do a getlk to check 2nd part
170 lock2
.l_type
= F_RDLCK
;
171 lock2
.l_whence
= SEEK_SET
;
172 lock2
.l_start
= 2000;
174 lock2
.l_pid
= getpid();
175 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
176 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
177 ASSERT_EQ(lock2
.l_start
, 1536);
178 ASSERT_EQ(lock2
.l_len
, 512);
179 ASSERT_EQ(lock2
.l_pid
, getpid());
181 // Now do a getlk to check released part
182 lock2
.l_type
= F_RDLCK
;
183 lock2
.l_whence
= SEEK_SET
;
186 lock2
.l_pid
= getpid();
187 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
188 ASSERT_EQ(lock2
.l_type
, F_UNLCK
);
189 ASSERT_EQ(lock2
.l_start
, 512);
190 ASSERT_EQ(lock2
.l_len
, 1024);
191 ASSERT_EQ(lock2
.l_pid
, getpid());
193 // Now downgrade the 1st part of the lock
194 lock1
.l_type
= F_RDLCK
;
195 lock1
.l_whence
= SEEK_SET
;
198 lock1
.l_pid
= getpid();
199 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
201 // Now do a getlk to check 1st part
202 lock2
.l_type
= F_WRLCK
;
203 lock2
.l_whence
= SEEK_SET
;
206 lock2
.l_pid
= getpid();
207 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
208 ASSERT_EQ(lock2
.l_type
, F_RDLCK
);
209 ASSERT_EQ(lock2
.l_start
, 0);
210 ASSERT_EQ(lock2
.l_len
, 512);
211 ASSERT_EQ(lock2
.l_pid
, getpid());
213 // Now upgrade the 1st part of the lock
214 lock1
.l_type
= F_WRLCK
;
215 lock1
.l_whence
= SEEK_SET
;
218 lock1
.l_pid
= getpid();
219 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, 42, false));
221 // Now do a getlk to check 1st part
222 lock2
.l_type
= F_WRLCK
;
223 lock2
.l_whence
= SEEK_SET
;
226 lock2
.l_pid
= getpid();
227 ASSERT_EQ(0, ceph_ll_getlk(cmount
, fh
, &lock2
, 43));
228 ASSERT_EQ(lock2
.l_type
, F_WRLCK
);
229 ASSERT_EQ(lock2
.l_start
, 0);
230 ASSERT_EQ(lock2
.l_len
, 512);
231 ASSERT_EQ(lock2
.l_pid
, getpid());
233 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
234 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
238 /* Locking in different threads */
240 // Used by ConcurrentLocking test
241 struct str_ConcurrentRecordLocking
{
243 struct ceph_mount_info
*cmount
; // !NULL if shared
246 void sem_init(int pshared
) {
247 ASSERT_EQ(0, ::sem_init(&sem
[0], pshared
, 0));
248 ASSERT_EQ(0, ::sem_init(&sem
[1], pshared
, 0));
249 ASSERT_EQ(0, ::sem_init(&semReply
[0], pshared
, 0));
250 ASSERT_EQ(0, ::sem_init(&semReply
[1], pshared
, 0));
253 ASSERT_EQ(0, ::sem_destroy(&sem
[0]));
254 ASSERT_EQ(0, ::sem_destroy(&sem
[1]));
255 ASSERT_EQ(0, ::sem_destroy(&semReply
[0]));
256 ASSERT_EQ(0, ::sem_destroy(&semReply
[1]));
260 // Wakeup main (for (N) steps)
261 #define PING_MAIN(n) ASSERT_EQ(0, sem_post(&s.sem[n%2]))
262 // Wait for main to wake us up (for (RN) steps)
263 #define WAIT_MAIN(n) \
264 ASSERT_EQ(0, sem_timedwait(&s.semReply[n%2], abstime(ts, waitSlowMs)))
266 // Wakeup worker (for (RN) steps)
267 #define PING_WORKER(n) ASSERT_EQ(0, sem_post(&s.semReply[n%2]))
268 // Wait for worker to wake us up (for (N) steps)
269 #define WAIT_WORKER(n) \
270 ASSERT_EQ(0, sem_timedwait(&s.sem[n%2], abstime(ts, waitSlowMs)))
271 // Worker shall not wake us up (for (N) steps)
272 #define NOT_WAIT_WORKER(n) \
273 ASSERT_EQ(-1, sem_timedwait(&s.sem[n%2], abstime(ts, waitMs)))
275 // Do twice an operation
276 #define TWICE(EXPR) do { \
281 /* Locking in different threads */
283 // Used by ConcurrentLocking test
284 static void thread_ConcurrentRecordLocking(str_ConcurrentRecordLocking
& s
) {
285 struct ceph_mount_info
*const cmount
= s
.cmount
;
287 Inode
*root
= NULL
, *inode
= NULL
;
288 struct ceph_statx stx
;
293 // Get the root inode
294 rc
= ceph_ll_lookup_root(cmount
, &root
);
297 // Get the inode and Fh corresponding to c_file
298 rc
= ceph_ll_create(cmount
, root
, s
.file
, fileMode
, O_RDWR
| O_CREAT
,
299 &inode
, &fh
, &stx
, 0, 0, ceph_mount_perms(cmount
));
302 lock1
.l_type
= F_WRLCK
;
303 lock1
.l_whence
= SEEK_SET
;
306 lock1
.l_pid
= getpid();
307 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
310 lock1
.l_type
= F_WRLCK
;
311 lock1
.l_whence
= SEEK_SET
;
314 lock1
.l_pid
= getpid();
315 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
318 lock1
.l_type
= F_UNLCK
;
319 lock1
.l_whence
= SEEK_SET
;
322 lock1
.l_pid
= getpid();
323 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
326 lock1
.l_type
= F_RDLCK
;
327 lock1
.l_whence
= SEEK_SET
;
330 lock1
.l_pid
= getpid();
331 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
334 WAIT_MAIN(1); // (R1)
335 lock1
.l_type
= F_UNLCK
;
336 lock1
.l_whence
= SEEK_SET
;
339 lock1
.l_pid
= getpid();
340 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
343 WAIT_MAIN(2); // (R2)
344 lock1
.l_type
= F_WRLCK
;
345 lock1
.l_whence
= SEEK_SET
;
348 lock1
.l_pid
= getpid();
349 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
352 WAIT_MAIN(3); // (R3)
353 lock1
.l_type
= F_UNLCK
;
354 lock1
.l_whence
= SEEK_SET
;
357 lock1
.l_pid
= getpid();
358 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
361 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
364 // Used by ConcurrentRecordLocking test
365 static void* thread_ConcurrentRecordLocking_(void *arg
) {
366 str_ConcurrentRecordLocking
*const s
=
367 reinterpret_cast<str_ConcurrentRecordLocking
*>(arg
);
368 thread_ConcurrentRecordLocking(*s
);
372 TEST(LibCephFS
, ConcurrentRecordLocking
) {
373 const pid_t mypid
= getpid();
374 struct ceph_mount_info
*cmount
;
378 sprintf(c_file
, "recordlock_test_%d", mypid
);
380 Inode
*root
= NULL
, *inode
= NULL
;
381 struct ceph_statx stx
;
384 UserPerm
*perms
= ceph_mount_perms(cmount
);
386 // Get the root inode
387 rc
= ceph_ll_lookup_root(cmount
, &root
);
390 // Get the inode and Fh corresponding to c_file
391 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
392 &inode
, &fh
, &stx
, 0, 0, perms
);
396 lock1
.l_type
= F_WRLCK
;
397 lock1
.l_whence
= SEEK_SET
;
400 lock1
.l_pid
= getpid();
401 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
403 // Start locker thread
406 str_ConcurrentRecordLocking s
= { c_file
, cmount
};
408 ASSERT_EQ(0, pthread_create(&thread
, NULL
, thread_ConcurrentRecordLocking_
, &s
));
409 // Synchronization point with thread (failure: thread is dead)
410 WAIT_WORKER(1); // (1)
412 // Shall not have lock immediately
413 NOT_WAIT_WORKER(2); // (2)
416 lock1
.l_type
= F_UNLCK
;
417 lock1
.l_whence
= SEEK_SET
;
420 lock1
.l_pid
= getpid();
421 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
424 // Synchronization point with thread (failure: thread is dead)
425 WAIT_WORKER(2); // (2)
427 // Synchronization point with thread (failure: thread is dead)
428 WAIT_WORKER(3); // (3)
430 // Wait for thread to share lock
431 WAIT_WORKER(4); // (4)
432 lock1
.l_type
= F_WRLCK
;
433 lock1
.l_whence
= SEEK_SET
;
436 lock1
.l_pid
= getpid();
437 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
438 lock1
.l_type
= F_RDLCK
;
439 lock1
.l_whence
= SEEK_SET
;
442 lock1
.l_pid
= getpid();
443 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
445 // Wake up thread to unlock shared lock
446 PING_WORKER(1); // (R1)
447 WAIT_WORKER(5); // (5)
449 // Now we can lock exclusively
450 // Upgrade to exclusive lock (as per POSIX)
451 lock1
.l_type
= F_WRLCK
;
452 lock1
.l_whence
= SEEK_SET
;
455 lock1
.l_pid
= getpid();
456 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
458 // Wake up thread to lock shared lock
459 PING_WORKER(2); // (R2)
461 // Shall not have lock immediately
462 NOT_WAIT_WORKER(6); // (6)
464 // Release lock ; thread will get it
465 lock1
.l_type
= F_UNLCK
;
466 lock1
.l_whence
= SEEK_SET
;
469 lock1
.l_pid
= getpid();
470 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
471 WAIT_WORKER(6); // (6)
473 // We no longer have the lock
474 lock1
.l_type
= F_WRLCK
;
475 lock1
.l_whence
= SEEK_SET
;
478 lock1
.l_pid
= getpid();
479 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
480 lock1
.l_type
= F_RDLCK
;
481 lock1
.l_whence
= SEEK_SET
;
484 lock1
.l_pid
= getpid();
485 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
487 // Wake up thread to unlock exclusive lock
488 PING_WORKER(3); // (R3)
489 WAIT_WORKER(7); // (7)
491 // We can lock it again
492 lock1
.l_type
= F_WRLCK
;
493 lock1
.l_whence
= SEEK_SET
;
496 lock1
.l_pid
= getpid();
497 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
498 lock1
.l_type
= F_UNLCK
;
499 lock1
.l_whence
= SEEK_SET
;
502 lock1
.l_pid
= getpid();
503 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
506 void *retval
= (void*) (uintptr_t) -1;
507 ASSERT_EQ(0, pthread_join(thread
, &retval
));
508 ASSERT_EQ(NULL
, retval
);
510 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
511 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
515 TEST(LibCephFS
, ThreesomeRecordLocking
) {
516 const pid_t mypid
= getpid();
517 struct ceph_mount_info
*cmount
;
521 sprintf(c_file
, "recordlock_test_%d", mypid
);
523 Inode
*root
= NULL
, *inode
= NULL
;
524 struct ceph_statx stx
;
527 UserPerm
*perms
= ceph_mount_perms(cmount
);
529 // Get the root inode
530 rc
= ceph_ll_lookup_root(cmount
, &root
);
533 // Get the inode and Fh corresponding to c_file
534 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
535 &inode
, &fh
, &stx
, 0, 0, perms
);
539 lock1
.l_type
= F_WRLCK
;
540 lock1
.l_whence
= SEEK_SET
;
543 lock1
.l_pid
= getpid();
544 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
546 // Start locker thread
549 str_ConcurrentRecordLocking s
= { c_file
, cmount
};
551 ASSERT_EQ(0, pthread_create(&thread
[0], NULL
, thread_ConcurrentRecordLocking_
, &s
));
552 ASSERT_EQ(0, pthread_create(&thread
[1], NULL
, thread_ConcurrentRecordLocking_
, &s
));
553 // Synchronization point with thread (failure: thread is dead)
554 TWICE(WAIT_WORKER(1)); // (1)
556 // Shall not have lock immediately
557 NOT_WAIT_WORKER(2); // (2)
560 lock1
.l_type
= F_UNLCK
;
561 lock1
.l_whence
= SEEK_SET
;
564 lock1
.l_pid
= getpid();
565 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
568 TWICE(// Synchronization point with thread (failure: thread is dead)
569 WAIT_WORKER(2); // (2)
571 // Synchronization point with thread (failure: thread is dead)
572 WAIT_WORKER(3)); // (3)
574 // Wait for thread to share lock
575 TWICE(WAIT_WORKER(4)); // (4)
576 lock1
.l_type
= F_WRLCK
;
577 lock1
.l_whence
= SEEK_SET
;
580 lock1
.l_pid
= getpid();
581 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
582 lock1
.l_type
= F_RDLCK
;
583 lock1
.l_whence
= SEEK_SET
;
586 lock1
.l_pid
= getpid();
587 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
589 // Wake up thread to unlock shared lock
590 TWICE(PING_WORKER(1); // (R1)
591 WAIT_WORKER(5)); // (5)
593 // Now we can lock exclusively
594 // Upgrade to exclusive lock (as per POSIX)
595 lock1
.l_type
= F_WRLCK
;
596 lock1
.l_whence
= SEEK_SET
;
599 lock1
.l_pid
= getpid();
600 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), true));
602 TWICE( // Wake up thread to lock shared lock
603 PING_WORKER(2); // (R2)
605 // Shall not have lock immediately
606 NOT_WAIT_WORKER(6)); // (6)
608 // Release lock ; thread will get it
609 lock1
.l_type
= F_UNLCK
;
610 lock1
.l_whence
= SEEK_SET
;
613 lock1
.l_pid
= getpid();
614 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
615 TWICE(WAIT_WORKER(6); // (6)
617 // We no longer have the lock
618 lock1
.l_type
= F_WRLCK
;
619 lock1
.l_whence
= SEEK_SET
;
622 lock1
.l_pid
= getpid();
623 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
624 lock1
.l_type
= F_RDLCK
;
625 lock1
.l_whence
= SEEK_SET
;
628 lock1
.l_pid
= getpid();
629 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
631 // Wake up thread to unlock exclusive lock
632 PING_WORKER(3); // (R3)
633 WAIT_WORKER(7); // (7)
636 // We can lock it again
637 lock1
.l_type
= F_WRLCK
;
638 lock1
.l_whence
= SEEK_SET
;
641 lock1
.l_pid
= getpid();
642 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
643 lock1
.l_type
= F_UNLCK
;
644 lock1
.l_whence
= SEEK_SET
;
647 lock1
.l_pid
= getpid();
648 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
651 void *retval
= (void*) (uintptr_t) -1;
652 ASSERT_EQ(0, pthread_join(thread
[0], &retval
));
653 ASSERT_EQ(NULL
, retval
);
654 ASSERT_EQ(0, pthread_join(thread
[1], &retval
));
655 ASSERT_EQ(NULL
, retval
);
657 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
658 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
662 /* Locking in different processes */
664 #define PROCESS_SLOW_MS() \
665 static const long waitMs = 100; \
668 // Used by ConcurrentLocking test
669 static void process_ConcurrentRecordLocking(str_ConcurrentRecordLocking
& s
) {
670 const pid_t mypid
= getpid();
673 struct ceph_mount_info
*cmount
= NULL
;
676 Inode
*root
= NULL
, *inode
= NULL
;
677 struct ceph_statx stx
;
684 // Get the root inode
685 rc
= ceph_ll_lookup_root(cmount
, &root
);
688 // Get the inode and Fh corresponding to c_file
689 rc
= ceph_ll_create(cmount
, root
, s
.file
, fileMode
, O_RDWR
| O_CREAT
,
690 &inode
, &fh
, &stx
, 0, 0, ceph_mount_perms(cmount
));
693 WAIT_MAIN(1); // (R1)
695 lock1
.l_type
= F_WRLCK
;
696 lock1
.l_whence
= SEEK_SET
;
699 lock1
.l_pid
= getpid();
700 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
702 lock1
.l_type
= F_WRLCK
;
703 lock1
.l_whence
= SEEK_SET
;
706 lock1
.l_pid
= getpid();
707 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
710 lock1
.l_type
= F_UNLCK
;
711 lock1
.l_whence
= SEEK_SET
;
714 lock1
.l_pid
= getpid();
715 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
718 lock1
.l_type
= F_RDLCK
;
719 lock1
.l_whence
= SEEK_SET
;
722 lock1
.l_pid
= getpid();
723 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
726 WAIT_MAIN(2); // (R2)
727 lock1
.l_type
= F_UNLCK
;
728 lock1
.l_whence
= SEEK_SET
;
731 lock1
.l_pid
= getpid();
732 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
735 WAIT_MAIN(3); // (R3)
736 lock1
.l_type
= F_WRLCK
;
737 lock1
.l_whence
= SEEK_SET
;
740 lock1
.l_pid
= getpid();
741 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
744 WAIT_MAIN(4); // (R4)
745 lock1
.l_type
= F_UNLCK
;
746 lock1
.l_whence
= SEEK_SET
;
749 lock1
.l_pid
= getpid();
750 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
753 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
760 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
761 TEST(LibCephFS
, DISABLED_InterProcessRecordLocking
) {
763 // Process synchronization
765 const pid_t mypid
= getpid();
766 sprintf(c_file
, "recordlock_test_%d", mypid
);
768 Inode
*root
= NULL
, *inode
= NULL
;
769 struct ceph_statx stx
;
773 // Note: the semaphores MUST be on a shared memory segment
774 str_ConcurrentRecordLocking
*const shs
=
775 reinterpret_cast<str_ConcurrentRecordLocking
*>
776 (mmap(0, sizeof(*shs
), PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANONYMOUS
,
778 str_ConcurrentRecordLocking
&s
= *shs
;
782 // Start locker process
783 const pid_t pid
= fork();
786 process_ConcurrentRecordLocking(s
);
791 struct ceph_mount_info
*cmount
;
793 UserPerm
*perms
= ceph_mount_perms(cmount
);
795 // Get the root inode
796 rc
= ceph_ll_lookup_root(cmount
, &root
);
799 // Get the inode and Fh corresponding to c_file
800 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
801 &inode
, &fh
, &stx
, 0, 0, perms
);
805 lock1
.l_type
= F_WRLCK
;
806 lock1
.l_whence
= SEEK_SET
;
809 lock1
.l_pid
= getpid();
810 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
812 // Synchronization point with process (failure: process is dead)
813 PING_WORKER(1); // (R1)
814 WAIT_WORKER(1); // (1)
816 // Shall not have lock immediately
817 NOT_WAIT_WORKER(2); // (2)
820 lock1
.l_type
= F_UNLCK
;
821 lock1
.l_whence
= SEEK_SET
;
824 lock1
.l_pid
= getpid();
825 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
828 // Synchronization point with process (failure: process is dead)
829 WAIT_WORKER(2); // (2)
831 // Synchronization point with process (failure: process is dead)
832 WAIT_WORKER(3); // (3)
834 // Wait for process to share lock
835 WAIT_WORKER(4); // (4)
836 lock1
.l_type
= F_WRLCK
;
837 lock1
.l_whence
= SEEK_SET
;
840 lock1
.l_pid
= getpid();
841 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
842 lock1
.l_type
= F_RDLCK
;
843 lock1
.l_whence
= SEEK_SET
;
846 lock1
.l_pid
= getpid();
847 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
849 // Wake up process to unlock shared lock
850 PING_WORKER(2); // (R2)
851 WAIT_WORKER(5); // (5)
853 // Now we can lock exclusively
854 // Upgrade to exclusive lock (as per POSIX)
855 lock1
.l_type
= F_WRLCK
;
856 lock1
.l_whence
= SEEK_SET
;
859 lock1
.l_pid
= getpid();
860 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
862 // Wake up process to lock shared lock
863 PING_WORKER(3); // (R3)
865 // Shall not have lock immediately
866 NOT_WAIT_WORKER(6); // (6)
868 // Release lock ; process will get it
869 lock1
.l_type
= F_UNLCK
;
870 lock1
.l_whence
= SEEK_SET
;
873 lock1
.l_pid
= getpid();
874 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
875 WAIT_WORKER(6); // (6)
877 // We no longer have the lock
878 lock1
.l_type
= F_WRLCK
;
879 lock1
.l_whence
= SEEK_SET
;
882 lock1
.l_pid
= getpid();
883 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
884 lock1
.l_type
= F_RDLCK
;
885 lock1
.l_whence
= SEEK_SET
;
888 lock1
.l_pid
= getpid();
889 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
891 // Wake up process to unlock exclusive lock
892 PING_WORKER(4); // (R4)
893 WAIT_WORKER(7); // (7)
895 // We can lock it again
896 lock1
.l_type
= F_WRLCK
;
897 lock1
.l_whence
= SEEK_SET
;
900 lock1
.l_pid
= getpid();
901 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
902 lock1
.l_type
= F_UNLCK
;
903 lock1
.l_whence
= SEEK_SET
;
906 lock1
.l_pid
= getpid();
907 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
911 ASSERT_EQ(pid
, waitpid(pid
, &status
, 0));
912 ASSERT_EQ(EXIT_SUCCESS
, status
);
916 ASSERT_EQ(0, munmap(shs
, sizeof(*shs
)));
917 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
918 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));
922 // Disabled because of fork() issues (http://tracker.ceph.com/issues/16556)
923 TEST(LibCephFS
, DISABLED_ThreesomeInterProcessRecordLocking
) {
925 // Process synchronization
927 const pid_t mypid
= getpid();
928 sprintf(c_file
, "recordlock_test_%d", mypid
);
930 Inode
*root
= NULL
, *inode
= NULL
;
931 struct ceph_statx stx
;
935 // Note: the semaphores MUST be on a shared memory segment
936 str_ConcurrentRecordLocking
*const shs
=
937 reinterpret_cast<str_ConcurrentRecordLocking
*>
938 (mmap(0, sizeof(*shs
), PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANONYMOUS
,
940 str_ConcurrentRecordLocking
&s
= *shs
;
944 // Start locker processes
947 ASSERT_GE(pid
[0], 0);
949 process_ConcurrentRecordLocking(s
);
953 ASSERT_GE(pid
[1], 0);
955 process_ConcurrentRecordLocking(s
);
960 struct ceph_mount_info
*cmount
;
963 // Get the root inode
964 rc
= ceph_ll_lookup_root(cmount
, &root
);
967 // Get the inode and Fh corresponding to c_file
968 UserPerm
*perms
= ceph_mount_perms(cmount
);
969 rc
= ceph_ll_create(cmount
, root
, c_file
, fileMode
, O_RDWR
| O_CREAT
,
970 &inode
, &fh
, &stx
, 0, 0, perms
);
974 lock1
.l_type
= F_WRLCK
;
975 lock1
.l_whence
= SEEK_SET
;
978 lock1
.l_pid
= getpid();
979 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
981 // Synchronization point with process (failure: process is dead)
982 TWICE(PING_WORKER(1)); // (R1)
983 TWICE(WAIT_WORKER(1)); // (1)
985 // Shall not have lock immediately
986 NOT_WAIT_WORKER(2); // (2)
989 lock1
.l_type
= F_UNLCK
;
990 lock1
.l_whence
= SEEK_SET
;
993 lock1
.l_pid
= getpid();
994 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
997 TWICE(// Synchronization point with process (failure: process is dead)
998 WAIT_WORKER(2); // (2)
1000 // Synchronization point with process (failure: process is dead)
1001 WAIT_WORKER(3)); // (3)
1003 // Wait for process to share lock
1004 TWICE(WAIT_WORKER(4)); // (4)
1005 lock1
.l_type
= F_WRLCK
;
1006 lock1
.l_whence
= SEEK_SET
;
1009 lock1
.l_pid
= getpid();
1010 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1011 lock1
.l_type
= F_RDLCK
;
1012 lock1
.l_whence
= SEEK_SET
;
1015 lock1
.l_pid
= getpid();
1016 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1018 // Wake up process to unlock shared lock
1019 TWICE(PING_WORKER(2); // (R2)
1020 WAIT_WORKER(5)); // (5)
1022 // Now we can lock exclusively
1023 // Upgrade to exclusive lock (as per POSIX)
1024 lock1
.l_type
= F_WRLCK
;
1025 lock1
.l_whence
= SEEK_SET
;
1028 lock1
.l_pid
= getpid();
1029 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, true));
1031 TWICE( // Wake up process to lock shared lock
1032 PING_WORKER(3); // (R3)
1034 // Shall not have lock immediately
1035 NOT_WAIT_WORKER(6)); // (6)
1037 // Release lock ; process will get it
1038 lock1
.l_type
= F_UNLCK
;
1039 lock1
.l_whence
= SEEK_SET
;
1042 lock1
.l_pid
= getpid();
1043 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1044 TWICE(WAIT_WORKER(6); // (6)
1046 // We no longer have the lock
1047 lock1
.l_type
= F_WRLCK
;
1048 lock1
.l_whence
= SEEK_SET
;
1051 lock1
.l_pid
= getpid();
1052 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
1053 lock1
.l_type
= F_RDLCK
;
1054 lock1
.l_whence
= SEEK_SET
;
1057 lock1
.l_pid
= getpid();
1058 ASSERT_EQ(-EAGAIN
, ceph_ll_setlk(cmount
, fh
, &lock1
, pthread_self(), false));
1060 // Wake up process to unlock exclusive lock
1061 PING_WORKER(4); // (R4)
1062 WAIT_WORKER(7); // (7)
1065 // We can lock it again
1066 lock1
.l_type
= F_WRLCK
;
1067 lock1
.l_whence
= SEEK_SET
;
1070 lock1
.l_pid
= getpid();
1071 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1072 lock1
.l_type
= F_UNLCK
;
1073 lock1
.l_whence
= SEEK_SET
;
1076 lock1
.l_pid
= getpid();
1077 ASSERT_EQ(0, ceph_ll_setlk(cmount
, fh
, &lock1
, mypid
, false));
1081 ASSERT_EQ(pid
[0], waitpid(pid
[0], &status
, 0));
1082 ASSERT_EQ(EXIT_SUCCESS
, status
);
1083 ASSERT_EQ(pid
[1], waitpid(pid
[1], &status
, 0));
1084 ASSERT_EQ(EXIT_SUCCESS
, status
);
1088 ASSERT_EQ(0, munmap(shs
, sizeof(*shs
)));
1089 ASSERT_EQ(0, ceph_ll_close(cmount
, fh
));
1090 ASSERT_EQ(0, ceph_ll_unlink(cmount
, root
, c_file
, perms
));