1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 * This is an OSD class that implements methods for object
14 #include "include/types.h"
15 #include "include/utime.h"
16 #include "objclass/objclass.h"
18 #include "common/errno.h"
19 #include "common/Clock.h"
21 #include "cls/lock/cls_lock_types.h"
22 #include "cls/lock/cls_lock_ops.h"
24 #include "global/global_context.h"
26 #include "include/compat.h"
29 using namespace rados::cls::lock
;
35 #define LOCK_PREFIX "lock."
37 static int clean_lock(cls_method_context_t hctx
)
39 int r
= cls_cxx_remove(hctx
);
46 static int read_lock(cls_method_context_t hctx
,
51 string key
= LOCK_PREFIX
;
54 int r
= cls_cxx_getxattr(hctx
, key
.c_str(), &bl
);
57 *lock
= lock_info_t();
61 CLS_ERR("error reading xattr %s: %d", key
.c_str(), r
);
67 bufferlist::iterator it
= bl
.begin();
69 } catch (const buffer::error
&err
) {
70 CLS_ERR("error decoding %s", key
.c_str());
74 /* now trim expired locks */
76 utime_t now
= ceph_clock_now();
78 map
<locker_id_t
, locker_info_t
>::iterator iter
= lock
->lockers
.begin();
80 while (iter
!= lock
->lockers
.end()) {
81 struct locker_info_t
& info
= iter
->second
;
82 if (!info
.expiration
.is_zero() && info
.expiration
< now
) {
83 CLS_LOG(20, "expiring locker");
84 iter
= lock
->lockers
.erase(iter
);
90 if (lock
->lockers
.empty() && cls_lock_is_ephemeral(lock
->lock_type
)) {
93 CLS_ERR("error, on read, cleaning lock object %s", cpp_strerror(r
).c_str());
100 static int write_lock(cls_method_context_t hctx
, const string
& name
, const lock_info_t
& lock
)
102 string key
= LOCK_PREFIX
;
106 ::encode(lock
, lock_bl
, cls_get_client_features(hctx
));
108 int r
= cls_cxx_setxattr(hctx
, key
.c_str(), &lock_bl
);
116 * helper function to add a lock and update disk state.
119 * @param name Lock name
120 * @param lock_type Type of lock (exclusive / shared)
121 * @param duration Duration of lock (in seconds). Zero means it doesn't expire.
122 * @param flags lock flags
123 * @param cookie The cookie to set in the lock
124 * @param tag The tag to match with the lock (can only lock with matching tags)
125 * @param lock_description The lock description to set (if not empty)
126 * @param locker_description The locker description
128 * @return 0 on success, or -errno on failure
130 static int lock_obj(cls_method_context_t hctx
,
132 ClsLockType lock_type
,
134 const string
& description
,
136 const string
& cookie
,
139 bool exclusive
= cls_lock_is_exclusive(lock_type
);
141 bool fail_if_exists
= (flags
& LOCK_FLAG_MAY_RENEW
) == 0;
142 bool fail_if_does_not_exist
= flags
& LOCK_FLAG_MUST_RENEW
;
145 "requested lock_type=%s fail_if_exists=%d fail_if_does_not_exist=%d",
146 cls_lock_type_str(lock_type
), fail_if_exists
, fail_if_does_not_exist
);
147 if (!cls_lock_is_valid(lock_type
)) {
154 if (!fail_if_exists
&& fail_if_does_not_exist
) {
155 // at most one of LOCK_FLAG_MAY_RENEW and LOCK_FLAG_MUST_RENEW may
156 // be set since they have different implications if the lock does
161 // see if there's already a locker
162 int r
= read_lock(hctx
, name
, &linfo
);
163 if (r
< 0 && r
!= -ENOENT
) {
164 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
168 map
<locker_id_t
, locker_info_t
>& lockers
= linfo
.lockers
;
169 map
<locker_id_t
, locker_info_t
>::iterator iter
;
174 r
= cls_get_request_origin(hctx
, &inst
);
175 id
.locker
= inst
.name
;
178 /* check this early, before we check fail_if_exists, otherwise we might
179 * remove the locker entry and not check it later */
180 if (lockers
.size() && tag
!= linfo
.tag
) {
181 CLS_LOG(20, "cannot take lock on object, conflicting tag");
185 ClsLockType existing_lock_type
= linfo
.lock_type
;
186 CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type
));
187 iter
= lockers
.find(id
);
188 if (iter
!= lockers
.end()) {
189 if (fail_if_exists
&& !fail_if_does_not_exist
) {
192 lockers
.erase(iter
); // remove old entry
194 } else if (fail_if_does_not_exist
) {
198 if (!lockers
.empty()) {
200 CLS_LOG(20, "could not exclusive-lock object, already locked");
204 if (existing_lock_type
!= lock_type
) {
205 CLS_LOG(20, "cannot take lock on object, conflicting lock type");
210 linfo
.lock_type
= lock_type
;
213 if (!duration
.is_zero()) {
214 expiration
= ceph_clock_now();
215 expiration
+= duration
;
218 struct locker_info_t
info(expiration
, inst
.addr
, description
);
220 linfo
.lockers
[id
] = info
;
222 r
= write_lock(hctx
, name
, linfo
);
230 * Set an exclusive lock on an object for the activating client, if possible.
233 * @param cls_lock_lock_op request input
235 * @returns 0 on success, -EINVAL if it can't decode the lock_cookie,
236 * -EBUSY if the object is already locked, or -errno on (unexpected) failure.
238 static int lock_op(cls_method_context_t hctx
,
239 bufferlist
*in
, bufferlist
*out
)
241 CLS_LOG(20, "lock_op");
244 bufferlist::iterator iter
= in
->begin();
246 } catch (const buffer::error
&err
) {
250 return lock_obj(hctx
,
251 op
.name
, op
.type
, op
.duration
, op
.description
,
252 op
.flags
, op
.cookie
, op
.tag
);
256 * helper function to remove a lock from on disk and clean up state.
258 * @param name The lock name
259 * @param locker The locker entity name
260 * @param cookie The user-defined cookie associated with the lock.
262 * @return 0 on success, -ENOENT if there is no such lock (either
263 * entity or cookie is wrong), or -errno on other error.
265 static int remove_lock(cls_method_context_t hctx
,
267 entity_name_t
& locker
,
268 const string
& cookie
)
270 // get current lockers
272 int r
= read_lock(hctx
, name
, &linfo
);
274 CLS_ERR("Could not read list of current lockers off disk: %s", cpp_strerror(r
).c_str());
278 map
<locker_id_t
, locker_info_t
>& lockers
= linfo
.lockers
;
279 struct locker_id_t
id(locker
, cookie
);
281 // remove named locker from set
282 map
<locker_id_t
, locker_info_t
>::iterator iter
= lockers
.find(id
);
283 if (iter
== lockers
.end()) { // no such key
288 if (cls_lock_is_ephemeral(linfo
.lock_type
)) {
289 ceph_assert(lockers
.empty());
290 r
= clean_lock(hctx
);
292 r
= write_lock(hctx
, name
, linfo
);
299 * Unlock an object which the activating client currently has locked.
302 * @param cls_lock_unlock_op request input
304 * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT
305 * if there is no such lock (either entity or cookie is wrong), or
306 * -errno on other (unexpected) error.
308 static int unlock_op(cls_method_context_t hctx
,
309 bufferlist
*in
, bufferlist
*out
)
311 CLS_LOG(20, "unlock_op");
312 cls_lock_unlock_op op
;
314 bufferlist::iterator iter
= in
->begin();
316 } catch (const buffer::error
& err
) {
321 int r
= cls_get_request_origin(hctx
, &inst
);
323 return remove_lock(hctx
, op
.name
, inst
.name
, op
.cookie
);
327 * Break the lock on an object held by any client.
330 * @param cls_lock_break_op request input
332 * @return 0 on success, -EINVAL if it can't decode the locker and
333 * cookie, -ENOENT if there is no such lock (either entity or cookie
334 * is wrong), or -errno on other (unexpected) error.
336 static int break_lock(cls_method_context_t hctx
,
337 bufferlist
*in
, bufferlist
*out
)
339 CLS_LOG(20, "break_lock");
340 cls_lock_break_op op
;
342 bufferlist::iterator iter
= in
->begin();
344 } catch (const buffer::error
& err
) {
348 return remove_lock(hctx
, op
.name
, op
.locker
, op
.cookie
);
353 * Retrieve lock info: lockers, tag, exclusive
356 * @param cls_lock_list_lockers_op request input
359 * @param cls_lock_list_lockers_reply result
361 * @return 0 on success, -errno on failure.
363 static int get_info(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
365 CLS_LOG(20, "get_info");
366 cls_lock_get_info_op op
;
368 bufferlist::iterator iter
= in
->begin();
370 } catch (const buffer::error
& err
) {
374 // get current lockers
376 int r
= read_lock(hctx
, op
.name
, &linfo
);
378 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
382 struct cls_lock_get_info_reply ret
;
384 map
<locker_id_t
, locker_info_t
>::iterator iter
;
385 for (iter
= linfo
.lockers
.begin(); iter
!= linfo
.lockers
.end(); ++iter
) {
386 ret
.lockers
[iter
->first
] = iter
->second
;
388 ret
.lock_type
= linfo
.lock_type
;
391 ::encode(ret
, *out
, cls_get_client_features(hctx
));
398 * Retrieve a list of locks for this object
401 * @param in is ignored.
404 * @param out contains encoded cls_list_locks_reply
406 * @return 0 on success, -errno on failure.
408 static int list_locks(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
410 CLS_LOG(20, "list_locks");
412 map
<string
, bufferlist
> attrs
;
414 int r
= cls_cxx_getxattrs(hctx
, &attrs
);
418 cls_lock_list_locks_reply ret
;
420 map
<string
, bufferlist
>::iterator iter
;
421 size_t pos
= sizeof(LOCK_PREFIX
) - 1;
422 for (iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
423 const string
& attr
= iter
->first
;
424 if (attr
.substr(0, pos
).compare(LOCK_PREFIX
) == 0) {
425 ret
.locks
.push_back(attr
.substr(pos
));
435 * Assert that the object is currently locked
438 * @param cls_lock_assert_op request input
443 * @return 0 on success, -errno on failure.
445 int assert_locked(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
447 CLS_LOG(20, "assert_locked");
449 cls_lock_assert_op op
;
451 bufferlist::iterator iter
= in
->begin();
453 } catch (const buffer::error
& err
) {
457 if (!cls_lock_is_valid(op
.type
)) {
461 if (op
.name
.empty()) {
465 // see if there's already a locker
467 int r
= read_lock(hctx
, op
.name
, &linfo
);
469 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
473 if (linfo
.lockers
.empty()) {
474 CLS_LOG(20, "object not locked");
478 if (linfo
.lock_type
!= op
.type
) {
479 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
480 cls_lock_type_str(linfo
.lock_type
), cls_lock_type_str(op
.type
));
484 if (linfo
.tag
!= op
.tag
) {
485 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo
.tag
.c_str(),
491 r
= cls_get_request_origin(hctx
, &inst
);
495 id
.cookie
= op
.cookie
;
496 id
.locker
= inst
.name
;
498 map
<locker_id_t
, locker_info_t
>::iterator iter
= linfo
.lockers
.find(id
);
499 if (iter
== linfo
.lockers
.end()) {
500 CLS_LOG(20, "not locked by assert client");
507 * Update the cookie associated with an object lock
510 * @param cls_lock_set_cookie_op request input
515 * @return 0 on success, -errno on failure.
517 int set_cookie(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
519 CLS_LOG(20, "set_cookie");
521 cls_lock_set_cookie_op op
;
523 bufferlist::iterator iter
= in
->begin();
525 } catch (const buffer::error
& err
) {
529 if (!cls_lock_is_valid(op
.type
)) {
533 if (op
.name
.empty()) {
537 // see if there's already a locker
539 int r
= read_lock(hctx
, op
.name
, &linfo
);
541 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
545 if (linfo
.lockers
.empty()) {
546 CLS_LOG(20, "object not locked");
550 if (linfo
.lock_type
!= op
.type
) {
551 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
552 cls_lock_type_str(linfo
.lock_type
), cls_lock_type_str(op
.type
));
556 if (linfo
.tag
!= op
.tag
) {
557 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo
.tag
.c_str(),
563 r
= cls_get_request_origin(hctx
, &inst
);
567 id
.cookie
= op
.cookie
;
568 id
.locker
= inst
.name
;
570 map
<locker_id_t
, locker_info_t
>::iterator iter
= linfo
.lockers
.find(id
);
571 if (iter
== linfo
.lockers
.end()) {
572 CLS_LOG(20, "not locked by client");
576 id
.cookie
= op
.new_cookie
;
577 if (linfo
.lockers
.count(id
) != 0) {
578 CLS_LOG(20, "lock cookie in-use");
582 locker_info_t
locker_info(iter
->second
);
583 linfo
.lockers
.erase(iter
);
585 linfo
.lockers
[id
] = locker_info
;
586 r
= write_lock(hctx
, op
.name
, linfo
);
588 CLS_ERR("Could not update lock info: %s", cpp_strerror(r
).c_str());
596 CLS_LOG(20, "Loaded lock class!");
598 cls_handle_t h_class
;
599 cls_method_handle_t h_lock_op
;
600 cls_method_handle_t h_unlock_op
;
601 cls_method_handle_t h_break_lock
;
602 cls_method_handle_t h_get_info
;
603 cls_method_handle_t h_list_locks
;
604 cls_method_handle_t h_assert_locked
;
605 cls_method_handle_t h_set_cookie
;
607 cls_register("lock", &h_class
);
608 cls_register_cxx_method(h_class
, "lock",
609 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
610 lock_op
, &h_lock_op
);
611 cls_register_cxx_method(h_class
, "unlock",
612 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
613 unlock_op
, &h_unlock_op
);
614 cls_register_cxx_method(h_class
, "break_lock",
615 CLS_METHOD_RD
| CLS_METHOD_WR
,
616 break_lock
, &h_break_lock
);
617 cls_register_cxx_method(h_class
, "get_info",
619 get_info
, &h_get_info
);
620 cls_register_cxx_method(h_class
, "list_locks",
622 list_locks
, &h_list_locks
);
623 cls_register_cxx_method(h_class
, "assert_locked",
624 CLS_METHOD_RD
| CLS_METHOD_PROMOTE
,
625 assert_locked
, &h_assert_locked
);
626 cls_register_cxx_method(h_class
, "set_cookie",
627 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
628 set_cookie
, &h_set_cookie
);