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 read_lock(cls_method_context_t hctx, const string& name, lock_info_t *lock)
40 string key = LOCK_PREFIX;
43 int r = cls_cxx_getxattr(hctx, key.c_str(), &bl);
46 *lock = lock_info_t();
50 CLS_ERR("error reading xattr %s: %d", key.c_str(), r);
56 bufferlist::iterator it = bl.begin();
58 } catch (const buffer::error &err) {
59 CLS_ERR("error decoding %s", key.c_str());
63 /* now trim expired locks */
65 utime_t now = ceph_clock_now();
67 map<locker_id_t, locker_info_t>::iterator iter = lock->lockers.begin();
69 while (iter != lock->lockers.end()) {
70 map<locker_id_t, locker_info_t>::iterator next = iter;
73 struct locker_info_t& info = iter->second;
74 if (!info.expiration.is_zero() && info.expiration < now) {
75 CLS_LOG(20, "expiring locker");
76 lock->lockers.erase(iter);
85 static int write_lock(cls_method_context_t hctx, const string& name, const lock_info_t& lock)
87 string key = LOCK_PREFIX;
91 ::encode(lock, lock_bl, cls_get_client_features(hctx));
93 int r = cls_cxx_setxattr(hctx, key.c_str(), &lock_bl);
101 * helper function to add a lock and update disk state.
104 * @param name Lock name
105 * @param lock_type Type of lock (exclusive / shared)
106 * @param duration Duration of lock (in seconds). Zero means it doesn't expire.
107 * @param flags lock flags
108 * @param cookie The cookie to set in the lock
109 * @param tag The tag to match with the lock (can only lock with matching tags)
110 * @param lock_description The lock description to set (if not empty)
111 * @param locker_description The locker description
113 * @return 0 on success, or -errno on failure
115 static int lock_obj(cls_method_context_t hctx,
117 ClsLockType lock_type,
119 const string& description,
121 const string& cookie,
124 bool exclusive = lock_type == LOCK_EXCLUSIVE;
126 bool fail_if_exists = (flags & LOCK_FLAG_RENEW) == 0;
128 CLS_LOG(20, "requested lock_type=%s fail_if_exists=%d", cls_lock_type_str(lock_type), fail_if_exists);
129 if (lock_type != LOCK_EXCLUSIVE &&
130 lock_type != LOCK_SHARED)
136 // see if there's already a locker
137 int r = read_lock(hctx, name, &linfo);
138 if (r < 0 && r != -ENOENT) {
139 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
142 map<locker_id_t, locker_info_t>& lockers = linfo.lockers;
143 map<locker_id_t, locker_info_t>::iterator iter;
148 r = cls_get_request_origin(hctx, &inst);
149 id.locker = inst.name;
152 /* check this early, before we check fail_if_exists, otherwise we might
153 * remove the locker entry and not check it later */
154 if (lockers.size() && tag != linfo.tag) {
155 CLS_LOG(20, "cannot take lock on object, conflicting tag");
159 ClsLockType existing_lock_type = linfo.lock_type;
160 CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type));
161 iter = lockers.find(id);
162 if (iter != lockers.end()) {
163 if (fail_if_exists) {
166 lockers.erase(iter); // remove old entry
170 if (!lockers.empty()) {
172 CLS_LOG(20, "could not exclusive-lock object, already locked");
176 if (existing_lock_type != lock_type) {
177 CLS_LOG(20, "cannot take lock on object, conflicting lock type");
182 linfo.lock_type = lock_type;
185 if (!duration.is_zero()) {
186 expiration = ceph_clock_now();
187 expiration += duration;
190 struct locker_info_t info(expiration, inst.addr, description);
192 linfo.lockers[id] = info;
194 r = write_lock(hctx, name, linfo);
202 * Set an exclusive lock on an object for the activating client, if possible.
205 * @param cls_lock_lock_op request input
207 * @returns 0 on success, -EINVAL if it can't decode the lock_cookie,
208 * -EBUSY if the object is already locked, or -errno on (unexpected) failure.
210 static int lock_op(cls_method_context_t hctx,
211 bufferlist *in, bufferlist *out)
213 CLS_LOG(20, "lock_op");
216 bufferlist::iterator iter = in->begin();
218 } catch (const buffer::error &err) {
222 return lock_obj(hctx,
223 op.name, op.type, op.duration, op.description,
224 op.flags, op.cookie, op.tag);
228 * helper function to remove a lock from on disk and clean up state.
230 * @param name The lock name
231 * @param locker The locker entity name
232 * @param cookie The user-defined cookie associated with the lock.
234 * @return 0 on success, -ENOENT if there is no such lock (either
235 * entity or cookie is wrong), or -errno on other error.
237 static int remove_lock(cls_method_context_t hctx,
239 entity_name_t& locker,
240 const string& cookie)
242 // get current lockers
244 int r = read_lock(hctx, name, &linfo);
246 CLS_ERR("Could not read list of current lockers off disk: %s", cpp_strerror(r).c_str());
250 map<locker_id_t, locker_info_t>& lockers = linfo.lockers;
251 struct locker_id_t id(locker, cookie);
253 // remove named locker from set
254 map<locker_id_t, locker_info_t>::iterator iter = lockers.find(id);
255 if (iter == lockers.end()) { // no such key
260 r = write_lock(hctx, name, linfo);
266 * Unlock an object which the activating client currently has locked.
269 * @param cls_lock_unlock_op request input
271 * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT
272 * if there is no such lock (either entity or cookie is wrong), or
273 * -errno on other (unexpected) error.
275 static int unlock_op(cls_method_context_t hctx,
276 bufferlist *in, bufferlist *out)
278 CLS_LOG(20, "unlock_op");
279 cls_lock_unlock_op op;
281 bufferlist::iterator iter = in->begin();
283 } catch (const buffer::error& err) {
288 int r = cls_get_request_origin(hctx, &inst);
290 return remove_lock(hctx, op.name, inst.name, op.cookie);
294 * Break the lock on an object held by any client.
297 * @param cls_lock_break_op request input
299 * @return 0 on success, -EINVAL if it can't decode the locker and
300 * cookie, -ENOENT if there is no such lock (either entity or cookie
301 * is wrong), or -errno on other (unexpected) error.
303 static int break_lock(cls_method_context_t hctx,
304 bufferlist *in, bufferlist *out)
306 CLS_LOG(20, "break_lock");
307 cls_lock_break_op op;
309 bufferlist::iterator iter = in->begin();
311 } catch (const buffer::error& err) {
315 return remove_lock(hctx, op.name, op.locker, op.cookie);
320 * Retrieve lock info: lockers, tag, exclusive
323 * @param cls_lock_list_lockers_op request input
326 * @param cls_lock_list_lockers_reply result
328 * @return 0 on success, -errno on failure.
330 static int get_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
332 CLS_LOG(20, "get_info");
333 cls_lock_get_info_op op;
335 bufferlist::iterator iter = in->begin();
337 } catch (const buffer::error& err) {
341 // get current lockers
343 int r = read_lock(hctx, op.name, &linfo);
345 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
349 struct cls_lock_get_info_reply ret;
351 map<locker_id_t, locker_info_t>::iterator iter;
352 for (iter = linfo.lockers.begin(); iter != linfo.lockers.end(); ++iter) {
353 ret.lockers[iter->first] = iter->second;
355 ret.lock_type = linfo.lock_type;
358 ::encode(ret, *out, cls_get_client_features(hctx));
365 * Retrieve a list of locks for this object
368 * @param in is ignored.
371 * @param out contains encoded cls_list_locks_reply
373 * @return 0 on success, -errno on failure.
375 static int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
377 CLS_LOG(20, "list_locks");
379 map<string, bufferlist> attrs;
381 int r = cls_cxx_getxattrs(hctx, &attrs);
385 cls_lock_list_locks_reply ret;
387 map<string, bufferlist>::iterator iter;
388 size_t pos = sizeof(LOCK_PREFIX) - 1;
389 for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
390 const string& attr = iter->first;
391 if (attr.substr(0, pos).compare(LOCK_PREFIX) == 0) {
392 ret.locks.push_back(attr.substr(pos));
402 * Assert that the object is currently locked
405 * @param cls_lock_assert_op request input
410 * @return 0 on success, -errno on failure.
412 int assert_locked(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
414 CLS_LOG(20, "assert_locked");
416 cls_lock_assert_op op;
418 bufferlist::iterator iter = in->begin();
420 } catch (const buffer::error& err) {
424 if (op.type != LOCK_EXCLUSIVE && op.type != LOCK_SHARED) {
428 if (op.name.empty()) {
432 // see if there's already a locker
434 int r = read_lock(hctx, op.name, &linfo);
436 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
440 if (linfo.lockers.empty()) {
441 CLS_LOG(20, "object not locked");
445 if (linfo.lock_type != op.type) {
446 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
447 cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type));
451 if (linfo.tag != op.tag) {
452 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(),
458 r = cls_get_request_origin(hctx, &inst);
462 id.cookie = op.cookie;
463 id.locker = inst.name;
465 map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id);
466 if (iter == linfo.lockers.end()) {
467 CLS_LOG(20, "not locked by assert client");
474 * Update the cookie associated with an object lock
477 * @param cls_lock_set_cookie_op request input
482 * @return 0 on success, -errno on failure.
484 int set_cookie(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
486 CLS_LOG(20, "set_cookie");
488 cls_lock_set_cookie_op op;
490 bufferlist::iterator iter = in->begin();
492 } catch (const buffer::error& err) {
496 if (op.type != LOCK_EXCLUSIVE && op.type != LOCK_SHARED) {
500 if (op.name.empty()) {
504 // see if there's already a locker
506 int r = read_lock(hctx, op.name, &linfo);
508 CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str());
512 if (linfo.lockers.empty()) {
513 CLS_LOG(20, "object not locked");
517 if (linfo.lock_type != op.type) {
518 CLS_LOG(20, "lock type mismatch: current=%s, assert=%s",
519 cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type));
523 if (linfo.tag != op.tag) {
524 CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(),
530 r = cls_get_request_origin(hctx, &inst);
534 id.cookie = op.cookie;
535 id.locker = inst.name;
537 map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id);
538 if (iter == linfo.lockers.end()) {
539 CLS_LOG(20, "not locked by client");
543 id.cookie = op.new_cookie;
544 if (linfo.lockers.count(id) != 0) {
545 CLS_LOG(20, "lock cookie in-use");
549 locker_info_t locker_info(iter->second);
550 linfo.lockers.erase(iter);
552 linfo.lockers[id] = locker_info;
553 r = write_lock(hctx, op.name, linfo);
555 CLS_ERR("Could not update lock info: %s", cpp_strerror(r).c_str());
563 CLS_LOG(20, "Loaded lock class!");
565 cls_handle_t h_class;
566 cls_method_handle_t h_lock_op;
567 cls_method_handle_t h_unlock_op;
568 cls_method_handle_t h_break_lock;
569 cls_method_handle_t h_get_info;
570 cls_method_handle_t h_list_locks;
571 cls_method_handle_t h_assert_locked;
572 cls_method_handle_t h_set_cookie;
574 cls_register("lock", &h_class);
575 cls_register_cxx_method(h_class, "lock",
576 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
577 lock_op, &h_lock_op);
578 cls_register_cxx_method(h_class, "unlock",
579 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
580 unlock_op, &h_unlock_op);
581 cls_register_cxx_method(h_class, "break_lock",
582 CLS_METHOD_RD | CLS_METHOD_WR,
583 break_lock, &h_break_lock);
584 cls_register_cxx_method(h_class, "get_info",
586 get_info, &h_get_info);
587 cls_register_cxx_method(h_class, "list_locks",
589 list_locks, &h_list_locks);
590 cls_register_cxx_method(h_class, "assert_locked",
591 CLS_METHOD_RD | CLS_METHOD_PROMOTE,
592 assert_locked, &h_assert_locked);
593 cls_register_cxx_method(h_class, "set_cookie",
594 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
595 set_cookie, &h_set_cookie);