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 typedef struct lock_info_s
{
38 map
<locker_id_t
, locker_info_t
> lockers
; // map of lockers
39 ClsLockType lock_type
; // lock type (exclusive / shared)
40 string tag
; // tag: operations on lock can only succeed with this tag
41 // as long as set of non expired lockers
44 void encode(bufferlist
&bl
, uint64_t features
) const {
45 ENCODE_START(1, 1, bl
);
46 ::encode(lockers
, bl
, features
);
47 uint8_t t
= (uint8_t)lock_type
;
52 void decode(bufferlist::iterator
&bl
) {
53 DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl
);
54 ::decode(lockers
, bl
);
57 lock_type
= (ClsLockType
)t
;
61 lock_info_s() : lock_type(LOCK_NONE
) {}
63 WRITE_CLASS_ENCODER_FEATURES(lock_info_t
)
65 static int read_lock(cls_method_context_t hctx
, const string
& name
, lock_info_t
*lock
)
68 string key
= LOCK_PREFIX
;
71 int r
= cls_cxx_getxattr(hctx
, key
.c_str(), &bl
);
74 *lock
= lock_info_t();
78 CLS_ERR("error reading xattr %s: %d", key
.c_str(), r
);
84 bufferlist::iterator it
= bl
.begin();
86 } catch (const buffer::error
&err
) {
87 CLS_ERR("error decoding %s", key
.c_str());
91 /* now trim expired locks */
93 utime_t now
= ceph_clock_now();
95 map
<locker_id_t
, locker_info_t
>::iterator iter
= lock
->lockers
.begin();
97 while (iter
!= lock
->lockers
.end()) {
98 map
<locker_id_t
, locker_info_t
>::iterator next
= iter
;
101 struct locker_info_t
& info
= iter
->second
;
102 if (!info
.expiration
.is_zero() && info
.expiration
< now
) {
103 CLS_LOG(20, "expiring locker");
104 lock
->lockers
.erase(iter
);
113 static int write_lock(cls_method_context_t hctx
, const string
& name
, const lock_info_t
& lock
)
115 string key
= LOCK_PREFIX
;
119 ::encode(lock
, lock_bl
, cls_get_client_features(hctx
));
121 int r
= cls_cxx_setxattr(hctx
, key
.c_str(), &lock_bl
);
129 * helper function to add a lock and update disk state.
132 * @param name Lock name
133 * @param lock_type Type of lock (exclusive / shared)
134 * @param duration Duration of lock (in seconds). Zero means it doesn't expire.
135 * @param flags lock flags
136 * @param cookie The cookie to set in the lock
137 * @param tag The tag to match with the lock (can only lock with matching tags)
138 * @param lock_description The lock description to set (if not empty)
139 * @param locker_description The locker description
141 * @return 0 on success, or -errno on failure
143 static int lock_obj(cls_method_context_t hctx
,
145 ClsLockType lock_type
,
147 const string
& description
,
149 const string
& cookie
,
152 bool exclusive
= lock_type
== LOCK_EXCLUSIVE
;
154 bool fail_if_exists
= (flags
& LOCK_FLAG_RENEW
) == 0;
156 CLS_LOG(20, "requested lock_type=%s fail_if_exists=%d", cls_lock_type_str(lock_type
), fail_if_exists
);
157 if (lock_type
!= LOCK_EXCLUSIVE
&&
158 lock_type
!= LOCK_SHARED
)
164 // see if there's already a locker
165 int r
= read_lock(hctx
, name
, &linfo
);
166 if (r
< 0 && r
!= -ENOENT
) {
167 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
170 map
<locker_id_t
, locker_info_t
>& lockers
= linfo
.lockers
;
171 map
<locker_id_t
, locker_info_t
>::iterator iter
;
176 r
= cls_get_request_origin(hctx
, &inst
);
177 id
.locker
= inst
.name
;
180 /* check this early, before we check fail_if_exists, otherwise we might
181 * remove the locker entry and not check it later */
182 if (lockers
.size() && tag
!= linfo
.tag
) {
183 CLS_LOG(20, "cannot take lock on object, conflicting tag");
187 ClsLockType existing_lock_type
= linfo
.lock_type
;
188 CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type
));
189 iter
= lockers
.find(id
);
190 if (iter
!= lockers
.end()) {
191 if (fail_if_exists
) {
194 lockers
.erase(iter
); // remove old entry
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 r
= write_lock(hctx
, name
, linfo
);
294 * Unlock an object which the activating client currently has locked.
297 * @param cls_lock_unlock_op request input
299 * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT
300 * if there is no such lock (either entity or cookie is wrong), or
301 * -errno on other (unexpected) error.
303 static int unlock_op(cls_method_context_t hctx
,
304 bufferlist
*in
, bufferlist
*out
)
306 CLS_LOG(20, "unlock_op");
307 cls_lock_unlock_op op
;
309 bufferlist::iterator iter
= in
->begin();
311 } catch (const buffer::error
& err
) {
316 int r
= cls_get_request_origin(hctx
, &inst
);
318 return remove_lock(hctx
, op
.name
, inst
.name
, op
.cookie
);
322 * Break the lock on an object held by any client.
325 * @param cls_lock_break_op request input
327 * @return 0 on success, -EINVAL if it can't decode the locker and
328 * cookie, -ENOENT if there is no such lock (either entity or cookie
329 * is wrong), or -errno on other (unexpected) error.
331 static int break_lock(cls_method_context_t hctx
,
332 bufferlist
*in
, bufferlist
*out
)
334 CLS_LOG(20, "break_lock");
335 cls_lock_break_op op
;
337 bufferlist::iterator iter
= in
->begin();
339 } catch (const buffer::error
& err
) {
343 return remove_lock(hctx
, op
.name
, op
.locker
, op
.cookie
);
348 * Retrieve lock info: lockers, tag, exclusive
351 * @param cls_lock_list_lockers_op request input
354 * @param cls_lock_list_lockers_reply result
356 * @return 0 on success, -errno on failure.
358 static int get_info(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
360 CLS_LOG(20, "get_info");
361 cls_lock_get_info_op op
;
363 bufferlist::iterator iter
= in
->begin();
365 } catch (const buffer::error
& err
) {
369 // get current lockers
371 int r
= read_lock(hctx
, op
.name
, &linfo
);
373 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
377 struct cls_lock_get_info_reply ret
;
379 map
<locker_id_t
, locker_info_t
>::iterator iter
;
380 for (iter
= linfo
.lockers
.begin(); iter
!= linfo
.lockers
.end(); ++iter
) {
381 ret
.lockers
[iter
->first
] = iter
->second
;
383 ret
.lock_type
= linfo
.lock_type
;
386 ::encode(ret
, *out
, cls_get_client_features(hctx
));
393 * Retrieve a list of locks for this object
396 * @param in is ignored.
399 * @param out contains encoded cls_list_locks_reply
401 * @return 0 on success, -errno on failure.
403 static int list_locks(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
405 CLS_LOG(20, "list_locks");
407 map
<string
, bufferlist
> attrs
;
409 int r
= cls_cxx_getxattrs(hctx
, &attrs
);
413 cls_lock_list_locks_reply ret
;
415 map
<string
, bufferlist
>::iterator iter
;
416 size_t pos
= sizeof(LOCK_PREFIX
) - 1;
417 for (iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
418 const string
& attr
= iter
->first
;
419 if (attr
.substr(0, pos
).compare(LOCK_PREFIX
) == 0) {
420 ret
.locks
.push_back(attr
.substr(pos
));
430 * Assert that the object is currently locked
433 * @param cls_lock_assert_op request input
438 * @return 0 on success, -errno on failure.
440 int assert_locked(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
442 CLS_LOG(20, "assert_locked");
444 cls_lock_assert_op op
;
446 bufferlist::iterator iter
= in
->begin();
448 } catch (const buffer::error
& err
) {
452 if (op
.type
!= LOCK_EXCLUSIVE
&& op
.type
!= LOCK_SHARED
) {
456 if (op
.name
.empty()) {
460 // see if there's already a locker
462 int r
= read_lock(hctx
, op
.name
, &linfo
);
464 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
468 if (linfo
.lockers
.empty()) {
469 CLS_LOG(20, "object not locked");
473 if (linfo
.lock_type
!= op
.type
) {
474 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
475 cls_lock_type_str(linfo
.lock_type
), cls_lock_type_str(op
.type
));
479 if (linfo
.tag
!= op
.tag
) {
480 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo
.tag
.c_str(),
486 r
= cls_get_request_origin(hctx
, &inst
);
490 id
.cookie
= op
.cookie
;
491 id
.locker
= inst
.name
;
493 map
<locker_id_t
, locker_info_t
>::iterator iter
= linfo
.lockers
.find(id
);
494 if (iter
== linfo
.lockers
.end()) {
495 CLS_LOG(20, "not locked by assert client");
502 * Update the cookie associated with an object lock
505 * @param cls_lock_set_cookie_op request input
510 * @return 0 on success, -errno on failure.
512 int set_cookie(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
514 CLS_LOG(20, "set_cookie");
516 cls_lock_set_cookie_op op
;
518 bufferlist::iterator iter
= in
->begin();
520 } catch (const buffer::error
& err
) {
524 if (op
.type
!= LOCK_EXCLUSIVE
&& op
.type
!= LOCK_SHARED
) {
528 if (op
.name
.empty()) {
532 // see if there's already a locker
534 int r
= read_lock(hctx
, op
.name
, &linfo
);
536 CLS_ERR("Could not read lock info: %s", cpp_strerror(r
).c_str());
540 if (linfo
.lockers
.empty()) {
541 CLS_LOG(20, "object not locked");
545 if (linfo
.lock_type
!= op
.type
) {
546 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
547 cls_lock_type_str(linfo
.lock_type
), cls_lock_type_str(op
.type
));
551 if (linfo
.tag
!= op
.tag
) {
552 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo
.tag
.c_str(),
558 r
= cls_get_request_origin(hctx
, &inst
);
562 id
.cookie
= op
.cookie
;
563 id
.locker
= inst
.name
;
565 map
<locker_id_t
, locker_info_t
>::iterator iter
= linfo
.lockers
.find(id
);
566 if (iter
== linfo
.lockers
.end()) {
567 CLS_LOG(20, "not locked by client");
571 id
.cookie
= op
.new_cookie
;
572 if (linfo
.lockers
.count(id
) != 0) {
573 CLS_LOG(20, "lock cookie in-use");
577 locker_info_t
locker_info(iter
->second
);
578 linfo
.lockers
.erase(iter
);
580 linfo
.lockers
[id
] = locker_info
;
581 r
= write_lock(hctx
, op
.name
, linfo
);
583 CLS_ERR("Could not update lock info: %s", cpp_strerror(r
).c_str());
591 CLS_LOG(20, "Loaded lock class!");
593 cls_handle_t h_class
;
594 cls_method_handle_t h_lock_op
;
595 cls_method_handle_t h_unlock_op
;
596 cls_method_handle_t h_break_lock
;
597 cls_method_handle_t h_get_info
;
598 cls_method_handle_t h_list_locks
;
599 cls_method_handle_t h_assert_locked
;
600 cls_method_handle_t h_set_cookie
;
602 cls_register("lock", &h_class
);
603 cls_register_cxx_method(h_class
, "lock",
604 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
605 lock_op
, &h_lock_op
);
606 cls_register_cxx_method(h_class
, "unlock",
607 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
608 unlock_op
, &h_unlock_op
);
609 cls_register_cxx_method(h_class
, "break_lock",
610 CLS_METHOD_RD
| CLS_METHOD_WR
,
611 break_lock
, &h_break_lock
);
612 cls_register_cxx_method(h_class
, "get_info",
614 get_info
, &h_get_info
);
615 cls_register_cxx_method(h_class
, "list_locks",
617 list_locks
, &h_list_locks
);
618 cls_register_cxx_method(h_class
, "assert_locked",
619 CLS_METHOD_RD
| CLS_METHOD_PROMOTE
,
620 assert_locked
, &h_assert_locked
);
621 cls_register_cxx_method(h_class
, "set_cookie",
622 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
623 set_cookie
, &h_set_cookie
);