diff options
Diffstat (limited to 'src/cls/lock')
-rw-r--r-- | src/cls/lock/cls_lock.cc | 648 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_client.cc | 286 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_client.h | 141 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_ops.cc | 213 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_ops.h | 245 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_types.cc | 98 | ||||
-rw-r--r-- | src/cls/lock/cls_lock_types.h | 174 |
7 files changed, 1805 insertions, 0 deletions
diff --git a/src/cls/lock/cls_lock.cc b/src/cls/lock/cls_lock.cc new file mode 100644 index 000000000..96d28461d --- /dev/null +++ b/src/cls/lock/cls_lock.cc @@ -0,0 +1,648 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +/** \file + * + * This is an OSD class that implements methods for object + * advisory locking. + * + */ + +#include <errno.h> +#include <map> +#include <sstream> + +#include "include/types.h" +#include "include/utime.h" +#include "objclass/objclass.h" + +#include "common/errno.h" +#include "common/Clock.h" + +#include "cls/lock/cls_lock_types.h" +#include "cls/lock/cls_lock_ops.h" + +#include "global/global_context.h" + +#include "include/compat.h" + +using std::map; +using std::string; + +using ceph::bufferlist; +using namespace rados::cls::lock; + +CLS_VER(1,0) +CLS_NAME(lock) + +#define LOCK_PREFIX "lock." + +static int clean_lock(cls_method_context_t hctx) +{ + int r = cls_cxx_remove(hctx); + if (r < 0) + return r; + + return 0; +} + +static int read_lock(cls_method_context_t hctx, + const string& name, + lock_info_t *lock) +{ + bufferlist bl; + string key = LOCK_PREFIX; + key.append(name); + + int r = cls_cxx_getxattr(hctx, key.c_str(), &bl); + if (r < 0) { + if (r == -ENODATA) { + *lock = lock_info_t(); + return 0; + } + if (r != -ENOENT) { + CLS_ERR("error reading xattr %s: %d", key.c_str(), r); + } + return r; + } + + try { + auto it = bl.cbegin(); + decode(*lock, it); + } catch (const ceph::buffer::error &err) { + CLS_ERR("error decoding %s", key.c_str()); + return -EIO; + } + + /* now trim expired locks */ + + utime_t now = ceph_clock_now(); + + auto iter = lock->lockers.begin(); + + while (iter != lock->lockers.end()) { + struct locker_info_t& info = iter->second; + if (!info.expiration.is_zero() && info.expiration < now) { + CLS_LOG(20, "expiring locker"); + iter = lock->lockers.erase(iter); + } else { + ++iter; + } + } + + if (lock->lockers.empty() && cls_lock_is_ephemeral(lock->lock_type)) { + r = clean_lock(hctx); + if (r < 0) { + CLS_ERR("error, on read, cleaning lock object %s", cpp_strerror(r).c_str()); + } + } + + return 0; +} + +static int write_lock(cls_method_context_t hctx, const string& name, const lock_info_t& lock) +{ + using ceph::encode; + string key = LOCK_PREFIX; + key.append(name); + + bufferlist lock_bl; + encode(lock, lock_bl, cls_get_client_features(hctx)); + + int r = cls_cxx_setxattr(hctx, key.c_str(), &lock_bl); + if (r < 0) + return r; + + return 0; +} + +/** + * helper function to add a lock and update disk state. + * + * Input: + * @param name Lock name + * @param lock_type Type of lock (exclusive / shared) + * @param duration Duration of lock (in seconds). Zero means it doesn't expire. + * @param flags lock flags + * @param cookie The cookie to set in the lock + * @param tag The tag to match with the lock (can only lock with matching tags) + * @param lock_description The lock description to set (if not empty) + * @param locker_description The locker description + * + * @return 0 on success, or -errno on failure + */ +static int lock_obj(cls_method_context_t hctx, + const string& name, + ClsLockType lock_type, + utime_t duration, + const string& description, + uint8_t flags, + const string& cookie, + const string& tag) +{ + bool exclusive = cls_lock_is_exclusive(lock_type); + lock_info_t linfo; + bool fail_if_exists = (flags & LOCK_FLAG_MAY_RENEW) == 0; + bool fail_if_does_not_exist = flags & LOCK_FLAG_MUST_RENEW; + + CLS_LOG(20, + "requested lock_type=%s fail_if_exists=%d fail_if_does_not_exist=%d", + cls_lock_type_str(lock_type), fail_if_exists, fail_if_does_not_exist); + if (!cls_lock_is_valid(lock_type)) { + return -EINVAL; + } + + if (name.empty()) + return -EINVAL; + + if (!fail_if_exists && fail_if_does_not_exist) { + // at most one of LOCK_FLAG_MAY_RENEW and LOCK_FLAG_MUST_RENEW may + // be set since they have different implications if the lock does + // not already exist + return -EINVAL; + } + + // see if there's already a locker + int r = read_lock(hctx, name, &linfo); + if (r < 0 && r != -ENOENT) { + CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str()); + return r; + } + + auto& lockers = linfo.lockers; + + locker_id_t id; + id.cookie = cookie; + entity_inst_t inst; + r = cls_get_request_origin(hctx, &inst); + id.locker = inst.name; + ceph_assert(r == 0); + + /* check this early, before we check fail_if_exists, otherwise we might + * remove the locker entry and not check it later */ + if (lockers.size() && tag != linfo.tag) { + CLS_LOG(20, "cannot take lock on object, conflicting tag"); + return -EBUSY; + } + + ClsLockType existing_lock_type = linfo.lock_type; + CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type)); + auto iter = lockers.find(id); + if (iter != lockers.end()) { + if (fail_if_exists && !fail_if_does_not_exist) { + return -EEXIST; + } else { + lockers.erase(iter); // remove old entry + } + } else if (fail_if_does_not_exist) { + return -ENOENT; + } + + if (!lockers.empty()) { + if (exclusive) { + auto locker_lister = + [&lockers]() -> std::string { + std::stringstream locker_list; + locker_list << lockers; + return locker_list.str(); + }; + CLS_LOG(20, "could not exclusive-lock object, already locked by %s", + locker_lister().c_str()); + return -EBUSY; + } + + if (existing_lock_type != lock_type) { + CLS_LOG(20, "cannot take lock on object, conflicting lock type"); + return -EBUSY; + } + } + + linfo.lock_type = lock_type; + linfo.tag = tag; + utime_t expiration; + if (!duration.is_zero()) { + expiration = ceph_clock_now(); + expiration += duration; + + } + // make all addrs of type legacy, because v2 clients speak v2 or v1, + // even depending on which OSD they are talking to, and the type + // isn't what uniquely identifies them. also, storing a v1 addr + // here means that old clients who get this locker_info won't see an + // old "msgr2:" prefix. + inst.addr.set_type(entity_addr_t::TYPE_LEGACY); + + struct locker_info_t info(expiration, inst.addr, description); + + linfo.lockers[id] = info; + + r = write_lock(hctx, name, linfo); + if (r < 0) + return r; + + return 0; +} + +/** + * Set an exclusive lock on an object for the activating client, if possible. + * + * Input: + * @param cls_lock_lock_op request input + * + * @returns 0 on success, -EINVAL if it can't decode the lock_cookie, + * -EBUSY if the object is already locked, or -errno on (unexpected) failure. + */ +static int lock_op(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "lock_op"); + cls_lock_lock_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error &err) { + return -EINVAL; + } + + return lock_obj(hctx, + op.name, op.type, op.duration, op.description, + op.flags, op.cookie, op.tag); +} + +/** + * helper function to remove a lock from on disk and clean up state. + * + * @param name The lock name + * @param locker The locker entity name + * @param cookie The user-defined cookie associated with the lock. + * + * @return 0 on success, -ENOENT if there is no such lock (either + * entity or cookie is wrong), or -errno on other error. + */ +static int remove_lock(cls_method_context_t hctx, + const string& name, + entity_name_t& locker, + const string& cookie) +{ + // get current lockers + lock_info_t linfo; + int r = read_lock(hctx, name, &linfo); + if (r < 0) { + CLS_ERR("Could not read list of current lockers off disk: %s", cpp_strerror(r).c_str()); + return r; + } + + auto& lockers = linfo.lockers; + struct locker_id_t id(locker, cookie); + + // remove named locker from set + auto iter = lockers.find(id); + if (iter == lockers.end()) { // no such key + CLS_LOG(10, "locker %s [name: %s.%ld, cookie: %s] does not exist", name.c_str(), + locker.type_str(), locker.num(), cookie.c_str()); + return -ENOENT; + } + lockers.erase(iter); + + if (cls_lock_is_ephemeral(linfo.lock_type)) { + ceph_assert(lockers.empty()); + r = clean_lock(hctx); + } else { + r = write_lock(hctx, name, linfo); + } + + return r; +} + +/** + * Unlock an object which the activating client currently has locked. + * + * Input: + * @param cls_lock_unlock_op request input + * + * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT + * if there is no such lock (either entity or cookie is wrong), or + * -errno on other (unexpected) error. + */ +static int unlock_op(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "unlock_op"); + cls_lock_unlock_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error& err) { + return -EINVAL; + } + + entity_inst_t inst; + int r = cls_get_request_origin(hctx, &inst); + ceph_assert(r == 0); + return remove_lock(hctx, op.name, inst.name, op.cookie); +} + +/** + * Break the lock on an object held by any client. + * + * Input: + * @param cls_lock_break_op request input + * + * @return 0 on success, -EINVAL if it can't decode the locker and + * cookie, -ENOENT if there is no such lock (either entity or cookie + * is wrong), or -errno on other (unexpected) error. + */ +static int break_lock(cls_method_context_t hctx, + bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "break_lock"); + cls_lock_break_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error& err) { + return -EINVAL; + } + + return remove_lock(hctx, op.name, op.locker, op.cookie); +} + + +/** + * Retrieve lock info: lockers, tag, exclusive + * + * Input: + * @param cls_lock_list_lockers_op request input + * + * Output: + * @param cls_lock_list_lockers_reply result + * + * @return 0 on success, -errno on failure. + */ +static int get_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_info"); + cls_lock_get_info_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error& err) { + return -EINVAL; + } + + // get current lockers + lock_info_t linfo; + int r = read_lock(hctx, op.name, &linfo); + if (r < 0) { + CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str()); + return r; + } + + struct cls_lock_get_info_reply ret; + + for (auto iter = linfo.lockers.begin(); iter != linfo.lockers.end(); ++iter) { + ret.lockers[iter->first] = iter->second; + } + ret.lock_type = linfo.lock_type; + ret.tag = linfo.tag; + + encode(ret, *out, cls_get_client_features(hctx)); + + return 0; +} + + +/** + * Retrieve a list of locks for this object + * + * Input: + * @param in is ignored. + * + * Output: + * @param out contains encoded cls_list_locks_reply + * + * @return 0 on success, -errno on failure. + */ +static int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "list_locks"); + + map<string, bufferlist> attrs; + + int r = cls_cxx_getxattrs(hctx, &attrs); + if (r < 0) + return r; + + cls_lock_list_locks_reply ret; + + size_t pos = sizeof(LOCK_PREFIX) - 1; + for (auto iter = attrs.begin(); iter != attrs.end(); ++iter) { + const string& attr = iter->first; + if (attr.substr(0, pos).compare(LOCK_PREFIX) == 0) { + ret.locks.push_back(attr.substr(pos)); + } + } + + encode(ret, *out); + + return 0; +} + +/** + * Assert that the object is currently locked + * + * Input: + * @param cls_lock_assert_op request input + * + * Output: + * @param none + * + * @return 0 on success, -errno on failure. + */ +int assert_locked(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "assert_locked"); + + cls_lock_assert_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error& err) { + return -EINVAL; + } + + if (!cls_lock_is_valid(op.type)) { + return -EINVAL; + } + + if (op.name.empty()) { + return -EINVAL; + } + + // see if there's already a locker + lock_info_t linfo; + int r = read_lock(hctx, op.name, &linfo); + if (r < 0) { + CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str()); + return r; + } + + if (linfo.lockers.empty()) { + CLS_LOG(20, "object not locked"); + return -EBUSY; + } + + if (linfo.lock_type != op.type) { + CLS_LOG(20, "lock type mismatch: current=%s, assert=%s", + cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type)); + return -EBUSY; + } + + if (linfo.tag != op.tag) { + CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(), + op.tag.c_str()); + return -EBUSY; + } + + entity_inst_t inst; + r = cls_get_request_origin(hctx, &inst); + ceph_assert(r == 0); + + locker_id_t id; + id.cookie = op.cookie; + id.locker = inst.name; + + auto iter = linfo.lockers.find(id); + if (iter == linfo.lockers.end()) { + CLS_LOG(20, "not locked by assert client"); + return -EBUSY; + } + return 0; +} + +/** + * Update the cookie associated with an object lock + * + * Input: + * @param cls_lock_set_cookie_op request input + * + * Output: + * @param none + * + * @return 0 on success, -errno on failure. + */ +int set_cookie(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "set_cookie"); + + cls_lock_set_cookie_op op; + try { + auto iter = in->cbegin(); + decode(op, iter); + } catch (const ceph::buffer::error& err) { + return -EINVAL; + } + + if (!cls_lock_is_valid(op.type)) { + return -EINVAL; + } + + if (op.name.empty()) { + return -EINVAL; + } + + // see if there's already a locker + lock_info_t linfo; + int r = read_lock(hctx, op.name, &linfo); + if (r < 0) { + CLS_ERR("Could not read lock info: %s", cpp_strerror(r).c_str()); + return r; + } + + if (linfo.lockers.empty()) { + CLS_LOG(20, "object not locked"); + return -EBUSY; + } + + if (linfo.lock_type != op.type) { + CLS_LOG(20, "lock type mismatch: current=%s, assert=%s", + cls_lock_type_str(linfo.lock_type), cls_lock_type_str(op.type)); + return -EBUSY; + } + + if (linfo.tag != op.tag) { + CLS_LOG(20, "lock tag mismatch: current=%s, assert=%s", linfo.tag.c_str(), + op.tag.c_str()); + return -EBUSY; + } + + entity_inst_t inst; + r = cls_get_request_origin(hctx, &inst); + ceph_assert(r == 0); + + locker_id_t id; + id.cookie = op.cookie; + id.locker = inst.name; + + map<locker_id_t, locker_info_t>::iterator iter = linfo.lockers.find(id); + if (iter == linfo.lockers.end()) { + CLS_LOG(20, "not locked by client"); + return -EBUSY; + } + + id.cookie = op.new_cookie; + if (linfo.lockers.count(id) != 0) { + CLS_LOG(20, "lock cookie in-use"); + return -EBUSY; + } + + locker_info_t locker_info(iter->second); + linfo.lockers.erase(iter); + + linfo.lockers[id] = locker_info; + r = write_lock(hctx, op.name, linfo); + if (r < 0) { + CLS_ERR("Could not update lock info: %s", cpp_strerror(r).c_str()); + return r; + } + return 0; +} + +CLS_INIT(lock) +{ + CLS_LOG(20, "Loaded lock class!"); + + cls_handle_t h_class; + cls_method_handle_t h_lock_op; + cls_method_handle_t h_unlock_op; + cls_method_handle_t h_break_lock; + cls_method_handle_t h_get_info; + cls_method_handle_t h_list_locks; + cls_method_handle_t h_assert_locked; + cls_method_handle_t h_set_cookie; + + cls_register("lock", &h_class); + cls_register_cxx_method(h_class, "lock", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + lock_op, &h_lock_op); + cls_register_cxx_method(h_class, "unlock", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + unlock_op, &h_unlock_op); + cls_register_cxx_method(h_class, "break_lock", + CLS_METHOD_RD | CLS_METHOD_WR, + break_lock, &h_break_lock); + cls_register_cxx_method(h_class, "get_info", + CLS_METHOD_RD, + get_info, &h_get_info); + cls_register_cxx_method(h_class, "list_locks", + CLS_METHOD_RD, + list_locks, &h_list_locks); + cls_register_cxx_method(h_class, "assert_locked", + CLS_METHOD_RD | CLS_METHOD_PROMOTE, + assert_locked, &h_assert_locked); + cls_register_cxx_method(h_class, "set_cookie", + CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE, + set_cookie, &h_set_cookie); + + return; +} diff --git a/src/cls/lock/cls_lock_client.cc b/src/cls/lock/cls_lock_client.cc new file mode 100644 index 000000000..305659867 --- /dev/null +++ b/src/cls/lock/cls_lock_client.cc @@ -0,0 +1,286 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/types.h" +#include "msg/msg_types.h" +#include "include/rados/librados.hpp" +#include "include/utime.h" + +#include "cls/lock/cls_lock_ops.h" +#include "cls/lock/cls_lock_client.h" + +using std::map; + +using namespace librados; + + +namespace rados { + namespace cls { + namespace lock { + + void lock(ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& description, + const utime_t& duration, uint8_t flags) + { + cls_lock_lock_op op; + op.name = name; + op.type = type; + op.cookie = cookie; + op.tag = tag; + op.description = description; + op.duration = duration; + op.flags = flags; + bufferlist in; + encode(op, in); + rados_op->exec("lock", "lock", in); + } + + int lock(IoCtx *ioctx, + const std::string& oid, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& description, const utime_t& duration, + uint8_t flags) + { + ObjectWriteOperation op; + lock(&op, name, type, cookie, tag, description, duration, flags); + return ioctx->operate(oid, &op); + } + + void unlock(ObjectWriteOperation *rados_op, + const std::string& name, const std::string& cookie) + { + cls_lock_unlock_op op; + op.name = name; + op.cookie = cookie; + bufferlist in; + encode(op, in); + + rados_op->exec("lock", "unlock", in); + } + + int unlock(IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie) + { + ObjectWriteOperation op; + unlock(&op, name, cookie); + return ioctx->operate(oid, &op); + } + + int aio_unlock(IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie, + librados::AioCompletion *completion) + { + ObjectWriteOperation op; + unlock(&op, name, cookie); + return ioctx->aio_operate(oid, completion, &op); + } + + void break_lock(ObjectWriteOperation *rados_op, + const std::string& name, const std::string& cookie, + const entity_name_t& locker) + { + cls_lock_break_op op; + op.name = name; + op.cookie = cookie; + op.locker = locker; + bufferlist in; + encode(op, in); + rados_op->exec("lock", "break_lock", in); + } + + int break_lock(IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie, + const entity_name_t& locker) + { + ObjectWriteOperation op; + break_lock(&op, name, cookie, locker); + return ioctx->operate(oid, &op); + } + + int list_locks(IoCtx *ioctx, const std::string& oid, std::list<std::string> *locks) + { + bufferlist in, out; + int r = ioctx->exec(oid, "lock", "list_locks", in, out); + if (r < 0) + return r; + + cls_lock_list_locks_reply ret; + auto iter = std::cbegin(out); + try { + decode(ret, iter); + } catch (ceph::buffer::error& err) { + return -EBADMSG; + } + + *locks = ret.locks; + + return 0; + } + + void get_lock_info_start(ObjectReadOperation *rados_op, + const std::string& name) + { + bufferlist in; + cls_lock_get_info_op op; + op.name = name; + encode(op, in); + rados_op->exec("lock", "get_info", in); + } + + int get_lock_info_finish(bufferlist::const_iterator *iter, + map<locker_id_t, locker_info_t> *lockers, + ClsLockType *type, std::string *tag) + { + cls_lock_get_info_reply ret; + try { + decode(ret, *iter); + } catch (ceph::buffer::error& err) { + return -EBADMSG; + } + + if (lockers) { + *lockers = ret.lockers; + } + + if (type) { + *type = ret.lock_type; + } + + if (tag) { + *tag = ret.tag; + } + + return 0; + } + + int get_lock_info(IoCtx *ioctx, const std::string& oid, const std::string& name, + map<locker_id_t, locker_info_t> *lockers, + ClsLockType *type, std::string *tag) + { + ObjectReadOperation op; + get_lock_info_start(&op, name); + bufferlist out; + int r = ioctx->operate(oid, &op, &out); + if (r < 0) + return r; + auto it = std::cbegin(out); + return get_lock_info_finish(&it, lockers, type, tag); + } + + void assert_locked(librados::ObjectOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag) + { + cls_lock_assert_op op; + op.name = name; + op.type = type; + op.cookie = cookie; + op.tag = tag; + bufferlist in; + encode(op, in); + rados_op->exec("lock", "assert_locked", in); + } + + void set_cookie(librados::ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& new_cookie) + { + cls_lock_set_cookie_op op; + op.name = name; + op.type = type; + op.cookie = cookie; + op.tag = tag; + op.new_cookie = new_cookie; + bufferlist in; + encode(op, in); + rados_op->exec("lock", "set_cookie", in); + } + + void Lock::assert_locked_shared(ObjectOperation *op) + { + assert_locked(op, name, ClsLockType::SHARED, cookie, tag); + } + + void Lock::assert_locked_exclusive(ObjectOperation *op) + { + assert_locked(op, name, ClsLockType::EXCLUSIVE, cookie, tag); + } + + void Lock::assert_locked_exclusive_ephemeral(ObjectOperation *op) + { + assert_locked(op, name, ClsLockType::EXCLUSIVE_EPHEMERAL, cookie, tag); + } + + void Lock::lock_shared(ObjectWriteOperation *op) + { + lock(op, name, ClsLockType::SHARED, + cookie, tag, description, duration, flags); + } + + int Lock::lock_shared(IoCtx *ioctx, const std::string& oid) + { + return lock(ioctx, oid, name, ClsLockType::SHARED, + cookie, tag, description, duration, flags); + } + + void Lock::lock_exclusive(ObjectWriteOperation *op) + { + lock(op, name, ClsLockType::EXCLUSIVE, + cookie, tag, description, duration, flags); + } + + int Lock::lock_exclusive(IoCtx *ioctx, const std::string& oid) + { + return lock(ioctx, oid, name, ClsLockType::EXCLUSIVE, + cookie, tag, description, duration, flags); + } + + void Lock::lock_exclusive_ephemeral(ObjectWriteOperation *op) + { + lock(op, name, ClsLockType::EXCLUSIVE_EPHEMERAL, + cookie, tag, description, duration, flags); + } + + int Lock::lock_exclusive_ephemeral(IoCtx *ioctx, const std::string& oid) + { + return lock(ioctx, oid, name, ClsLockType::EXCLUSIVE_EPHEMERAL, + cookie, tag, description, duration, flags); + } + + void Lock::unlock(ObjectWriteOperation *op) + { + rados::cls::lock::unlock(op, name, cookie); + } + + int Lock::unlock(IoCtx *ioctx, const std::string& oid) + { + return rados::cls::lock::unlock(ioctx, oid, name, cookie); + } + + void Lock::break_lock(ObjectWriteOperation *op, const entity_name_t& locker) + { + rados::cls::lock::break_lock(op, name, cookie, locker); + } + + int Lock::break_lock(IoCtx *ioctx, const std::string& oid, const entity_name_t& locker) + { + return rados::cls::lock::break_lock(ioctx, oid, name, cookie, locker); + } + } // namespace lock + } // namespace cls +} // namespace rados diff --git a/src/cls/lock/cls_lock_client.h b/src/cls/lock/cls_lock_client.h new file mode 100644 index 000000000..92e1396bf --- /dev/null +++ b/src/cls/lock/cls_lock_client.h @@ -0,0 +1,141 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_LOCK_CLIENT_H +#define CEPH_CLS_LOCK_CLIENT_H + +#include <chrono> + +#include "include/rados/librados_fwd.hpp" +#include "cls/lock/cls_lock_types.h" + +namespace rados { + namespace cls { + namespace lock { + extern void lock(librados::ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& description, const utime_t& duration, + uint8_t flags); + + extern int lock(librados::IoCtx *ioctx, + const std::string& oid, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& description, const utime_t& duration, + uint8_t flags); + + extern void unlock(librados::ObjectWriteOperation *rados_op, + const std::string& name, const std::string& cookie); + + extern int unlock(librados::IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie); + + extern int aio_unlock(librados::IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie, + librados::AioCompletion *completion); + + extern void break_lock(librados::ObjectWriteOperation *op, + const std::string& name, const std::string& cookie, + const entity_name_t& locker); + + extern int break_lock(librados::IoCtx *ioctx, const std::string& oid, + const std::string& name, const std::string& cookie, + const entity_name_t& locker); + + extern int list_locks(librados::IoCtx *ioctx, const std::string& oid, + std::list<std::string> *locks); + extern void get_lock_info_start(librados::ObjectReadOperation *rados_op, + const std::string& name); + extern int get_lock_info_finish(ceph::bufferlist::const_iterator *out, + std::map<locker_id_t, locker_info_t> *lockers, + ClsLockType *type, std::string *tag); + + extern int get_lock_info(librados::IoCtx *ioctx, const std::string& oid, + const std::string& name, + std::map<locker_id_t, locker_info_t> *lockers, + ClsLockType *type, std::string *tag); + + extern void assert_locked(librados::ObjectOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, + const std::string& tag); + + extern void set_cookie(librados::ObjectWriteOperation *rados_op, + const std::string& name, ClsLockType type, + const std::string& cookie, const std::string& tag, + const std::string& new_cookie); + + class Lock { + std::string name; + std::string cookie; + std::string tag; + std::string description; + utime_t duration; + uint8_t flags; + + public: + + Lock(const std::string& _n) : name(_n), flags(0) {} + + void set_cookie(const std::string& c) { cookie = c; } + void set_tag(const std::string& t) { tag = t; } + void set_description(const std::string& desc) { description = desc; } + void set_duration(const utime_t& e) { duration = e; } + void set_duration(const ceph::timespan& d) { + duration = utime_t(ceph::real_clock::zero() + d); + } + + void set_may_renew(bool renew) { + if (renew) { + flags |= LOCK_FLAG_MAY_RENEW; + flags &= ~LOCK_FLAG_MUST_RENEW; // if may then not must + } else { + flags &= ~LOCK_FLAG_MAY_RENEW; + } + } + + void set_must_renew(bool renew) { + if (renew) { + flags |= LOCK_FLAG_MUST_RENEW; + flags &= ~LOCK_FLAG_MAY_RENEW; // if must then not may + } else { + flags &= ~LOCK_FLAG_MUST_RENEW; + } + } + + void assert_locked_shared(librados::ObjectOperation *rados_op); + void assert_locked_exclusive(librados::ObjectOperation *rados_op); + void assert_locked_exclusive_ephemeral(librados::ObjectOperation *rados_op); + + /* ObjectWriteOperation */ + void lock_shared(librados::ObjectWriteOperation *ioctx); + void lock_exclusive(librados::ObjectWriteOperation *ioctx); + + // Be careful when using an exclusive ephemeral lock; it is + // intended strictly for cases when a lock object exists + // solely for a lock in a given process and the object is no + // longer needed when the lock is unlocked or expired, as the + // cls back-end will make an effort to delete it. + void lock_exclusive_ephemeral(librados::ObjectWriteOperation *ioctx); + void unlock(librados::ObjectWriteOperation *ioctx); + void break_lock(librados::ObjectWriteOperation *ioctx, + const entity_name_t& locker); + + /* IoCtx */ + int lock_shared(librados::IoCtx *ioctx, const std::string& oid); + int lock_exclusive(librados::IoCtx *ioctx, const std::string& oid); + + // NB: see above comment on exclusive ephemeral locks + int lock_exclusive_ephemeral(librados::IoCtx *ioctx, + const std::string& oid); + int unlock(librados::IoCtx *ioctx, const std::string& oid); + int break_lock(librados::IoCtx *ioctx, const std::string& oid, + const entity_name_t& locker); + }; + + } // namespace lock + } // namespace cls +} // namespace rados + +#endif diff --git a/src/cls/lock/cls_lock_ops.cc b/src/cls/lock/cls_lock_ops.cc new file mode 100644 index 000000000..1f878be48 --- /dev/null +++ b/src/cls/lock/cls_lock_ops.cc @@ -0,0 +1,213 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "msg/msg_types.h" +#include "common/Formatter.h" + +#include "cls/lock/cls_lock_ops.h" + +using namespace rados::cls::lock; +using std::list; +using std::map; +using std::string; + +static void generate_lock_id(locker_id_t& i, int n, const string& cookie) +{ + i.locker = entity_name_t::CLIENT(n); + i.cookie = cookie; +} + +void cls_lock_lock_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("type", cls_lock_type_str(type)); + f->dump_string("cookie", cookie); + f->dump_string("tag", tag); + f->dump_string("description", description); + f->dump_stream("duration") << duration; + f->dump_int("flags", (int)flags); +} + +void cls_lock_lock_op::generate_test_instances(list<cls_lock_lock_op*>& o) +{ + cls_lock_lock_op *i = new cls_lock_lock_op; + i->name = "name"; + i->type = ClsLockType::SHARED; + i->cookie = "cookie"; + i->tag = "tag"; + i->description = "description"; + i->duration = utime_t(5, 0); + i->flags = LOCK_FLAG_MAY_RENEW; + o.push_back(i); + o.push_back(new cls_lock_lock_op); +} + +void cls_lock_unlock_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("cookie", cookie); +} + +void cls_lock_unlock_op::generate_test_instances(list<cls_lock_unlock_op*>& o) +{ + cls_lock_unlock_op *i = new cls_lock_unlock_op; + i->name = "name"; + i->cookie = "cookie"; + o.push_back(i); + o.push_back(new cls_lock_unlock_op); +} + +void cls_lock_break_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("cookie", cookie); + f->dump_stream("locker") << locker; +} + +void cls_lock_break_op::generate_test_instances(list<cls_lock_break_op*>& o) +{ + cls_lock_break_op *i = new cls_lock_break_op; + i->name = "name"; + i->cookie = "cookie"; + i->locker = entity_name_t::CLIENT(1); + o.push_back(i); + o.push_back(new cls_lock_break_op); +} + +void cls_lock_get_info_op::dump(Formatter *f) const +{ + f->dump_string("name", name); +} + +void cls_lock_get_info_op::generate_test_instances(list<cls_lock_get_info_op*>& o) +{ + cls_lock_get_info_op *i = new cls_lock_get_info_op; + i->name = "name"; + o.push_back(i); + o.push_back(new cls_lock_get_info_op); +} + +static void generate_test_addr(entity_addr_t& a, int nonce, int port) +{ + a.set_type(entity_addr_t::TYPE_LEGACY); + a.set_nonce(nonce); + a.set_family(AF_INET); + a.set_in4_quad(0, 127); + a.set_in4_quad(1, 0); + a.set_in4_quad(2, 1); + a.set_in4_quad(3, 2); + a.set_port(port); +} + +void cls_lock_get_info_reply::dump(Formatter *f) const +{ + f->dump_string("lock_type", cls_lock_type_str(lock_type)); + f->dump_string("tag", tag); + f->open_array_section("lockers"); + map<locker_id_t, locker_info_t>::const_iterator iter; + for (iter = lockers.begin(); iter != lockers.end(); ++iter) { + const locker_id_t& id = iter->first; + const locker_info_t& info = iter->second; + f->open_object_section("object"); + f->dump_stream("locker") << id.locker; + f->dump_string("description", info.description); + f->dump_string("cookie", id.cookie); + f->dump_stream("expiration") << info.expiration; + f->dump_string("addr", info.addr.get_legacy_str()); + f->close_section(); + } + f->close_section(); +} + +void cls_lock_get_info_reply::generate_test_instances(list<cls_lock_get_info_reply*>& o) +{ + cls_lock_get_info_reply *i = new cls_lock_get_info_reply; + i->lock_type = ClsLockType::SHARED; + i->tag = "tag"; + locker_id_t id1, id2; + entity_addr_t addr1, addr2; + generate_lock_id(id1, 1, "cookie1"); + generate_test_addr(addr1, 10, 20); + i->lockers[id1] = locker_info_t(utime_t(10, 0), addr1, "description1"); + generate_lock_id(id2, 2, "cookie2"); + generate_test_addr(addr2, 30, 40); + i->lockers[id2] = locker_info_t(utime_t(20, 0), addr2, "description2"); + + o.push_back(i); + o.push_back(new cls_lock_get_info_reply); +} + +void cls_lock_list_locks_reply::dump(Formatter *f) const +{ + list<string>::const_iterator iter; + f->open_array_section("locks"); + for (iter = locks.begin(); iter != locks.end(); ++iter) { + f->open_array_section("object"); + f->dump_string("lock", *iter); + f->close_section(); + } + f->close_section(); +} + +void cls_lock_list_locks_reply::generate_test_instances(list<cls_lock_list_locks_reply*>& o) +{ + cls_lock_list_locks_reply *i = new cls_lock_list_locks_reply; + i->locks.push_back("lock1"); + i->locks.push_back("lock2"); + i->locks.push_back("lock3"); + + o.push_back(i); + o.push_back(new cls_lock_list_locks_reply); +} + +void cls_lock_assert_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("type", cls_lock_type_str(type)); + f->dump_string("cookie", cookie); + f->dump_string("tag", tag); +} + +void cls_lock_assert_op::generate_test_instances(list<cls_lock_assert_op*>& o) +{ + cls_lock_assert_op *i = new cls_lock_assert_op; + i->name = "name"; + i->type = ClsLockType::SHARED; + i->cookie = "cookie"; + i->tag = "tag"; + o.push_back(i); + o.push_back(new cls_lock_assert_op); +} + +void cls_lock_set_cookie_op::dump(Formatter *f) const +{ + f->dump_string("name", name); + f->dump_string("type", cls_lock_type_str(type)); + f->dump_string("cookie", cookie); + f->dump_string("tag", tag); + f->dump_string("new_cookie", new_cookie); +} + +void cls_lock_set_cookie_op::generate_test_instances(list<cls_lock_set_cookie_op*>& o) +{ + cls_lock_set_cookie_op *i = new cls_lock_set_cookie_op; + i->name = "name"; + i->type = ClsLockType::SHARED; + i->cookie = "cookie"; + i->tag = "tag"; + i->new_cookie = "new cookie"; + o.push_back(i); + o.push_back(new cls_lock_set_cookie_op); +} + diff --git a/src/cls/lock/cls_lock_ops.h b/src/cls/lock/cls_lock_ops.h new file mode 100644 index 000000000..4de050f18 --- /dev/null +++ b/src/cls/lock/cls_lock_ops.h @@ -0,0 +1,245 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_LOCK_OPS_H +#define CEPH_CLS_LOCK_OPS_H + +#include "include/types.h" +#include "include/utime.h" +#include "cls/lock/cls_lock_types.h" + +struct cls_lock_lock_op +{ + std::string name; + ClsLockType type; + std::string cookie; + std::string tag; + std::string description; + utime_t duration; + uint8_t flags; + + cls_lock_lock_op() : type(ClsLockType::NONE), flags(0) {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + uint8_t t = (uint8_t)type; + encode(t, bl); + encode(cookie, bl); + encode(tag, bl); + encode(description, bl); + encode(duration, bl); + encode(flags, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + uint8_t t; + decode(t, bl); + type = (ClsLockType)t; + decode(cookie, bl); + decode(tag, bl); + decode(description, bl); + decode(duration, bl); + decode(flags, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_lock_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_lock_op) + +struct cls_lock_unlock_op +{ + std::string name; + std::string cookie; + + cls_lock_unlock_op() {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + encode(cookie, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + decode(cookie, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_unlock_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_unlock_op) + +struct cls_lock_break_op +{ + std::string name; + entity_name_t locker; + std::string cookie; + + cls_lock_break_op() {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + encode(locker, bl); + encode(cookie, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + decode(locker, bl); + decode(cookie, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_break_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_break_op) + +struct cls_lock_get_info_op +{ + std::string name; + + cls_lock_get_info_op() {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_get_info_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_get_info_op) + +struct cls_lock_get_info_reply +{ + std::map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t> lockers; + ClsLockType lock_type; + std::string tag; + + cls_lock_get_info_reply() : lock_type(ClsLockType::NONE) {} + + void encode(ceph::buffer::list &bl, uint64_t features) const { + ENCODE_START(1, 1, bl); + encode(lockers, bl, features); + uint8_t t = (uint8_t)lock_type; + encode(t, bl); + encode(tag, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(lockers, bl); + uint8_t t; + decode(t, bl); + lock_type = (ClsLockType)t; + decode(tag, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_get_info_reply*>& o); +}; +WRITE_CLASS_ENCODER_FEATURES(cls_lock_get_info_reply) + +struct cls_lock_list_locks_reply +{ + std::list<std::string> locks; + + cls_lock_list_locks_reply() {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(locks, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(locks, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_list_locks_reply*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_list_locks_reply) + +struct cls_lock_assert_op +{ + std::string name; + ClsLockType type; + std::string cookie; + std::string tag; + + cls_lock_assert_op() : type(ClsLockType::NONE) {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + uint8_t t = (uint8_t)type; + encode(t, bl); + encode(cookie, bl); + encode(tag, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + uint8_t t; + decode(t, bl); + type = (ClsLockType)t; + decode(cookie, bl); + decode(tag, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_assert_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_assert_op) + +struct cls_lock_set_cookie_op +{ + std::string name; + ClsLockType type; + std::string cookie; + std::string tag; + std::string new_cookie; + + cls_lock_set_cookie_op() : type(ClsLockType::NONE) {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(name, bl); + uint8_t t = (uint8_t)type; + encode(t, bl); + encode(cookie, bl); + encode(tag, bl); + encode(new_cookie, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(name, bl); + uint8_t t; + decode(t, bl); + type = (ClsLockType)t; + decode(cookie, bl); + decode(tag, bl); + decode(new_cookie, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_lock_set_cookie_op*>& o); +}; +WRITE_CLASS_ENCODER(cls_lock_set_cookie_op) + +#endif diff --git a/src/cls/lock/cls_lock_types.cc b/src/cls/lock/cls_lock_types.cc new file mode 100644 index 000000000..904ed268e --- /dev/null +++ b/src/cls/lock/cls_lock_types.cc @@ -0,0 +1,98 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "common/Formatter.h" + +#include "cls/lock/cls_lock_types.h" + +using namespace rados::cls::lock; + +static void generate_lock_id(locker_id_t& i, int n, const std::string& cookie) +{ + i.locker = entity_name_t::CLIENT(n); + i.cookie = cookie; +} + +void locker_id_t::dump(ceph::Formatter *f) const +{ + f->dump_stream("locker") << locker; + f->dump_string("cookie", cookie); +} + +void locker_id_t::generate_test_instances(std::list<locker_id_t*>& o) +{ + locker_id_t *i = new locker_id_t; + generate_lock_id(*i, 1, "cookie"); + o.push_back(i); + o.push_back(new locker_id_t); +} + +void locker_info_t::dump(ceph::Formatter *f) const +{ + f->dump_stream("expiration") << expiration; + f->dump_string("addr", addr.get_legacy_str()); + f->dump_string("description", description); +} + +static void generate_test_addr(entity_addr_t& a, int nonce, int port) +{ + a.set_type(entity_addr_t::TYPE_LEGACY); + a.set_nonce(nonce); + a.set_family(AF_INET); + a.set_in4_quad(0, 127); + a.set_in4_quad(1, 0); + a.set_in4_quad(2, 1); + a.set_in4_quad(3, 2); + a.set_port(port); +} + +void locker_info_t::generate_test_instances(std::list<locker_info_t*>& o) +{ + locker_info_t *i = new locker_info_t; + i->expiration = utime_t(5, 0); + generate_test_addr(i->addr, 1, 2); + i->description = "description"; + o.push_back(i); + o.push_back(new locker_info_t); +} + +void lock_info_t::dump(ceph::Formatter *f) const +{ + f->dump_int("lock_type", static_cast<int>(lock_type)); + f->dump_string("tag", tag); + f->open_array_section("lockers"); + for (auto &i : lockers) { + f->open_object_section("locker"); + f->dump_object("id", i.first); + f->dump_object("info", i.second); + f->close_section(); + } + f->close_section(); +} + +void lock_info_t::generate_test_instances(std::list<lock_info_t *>& o) +{ + lock_info_t *i = new lock_info_t; + locker_id_t id; + locker_info_t info; + generate_lock_id(id, 1, "cookie"); + info.expiration = utime_t(5, 0); + generate_test_addr(info.addr, 1, 2); + info.description = "description"; + i->lockers[id] = info; + i->lock_type = ClsLockType::EXCLUSIVE; + i->tag = "tag"; + o.push_back(i); + o.push_back(new lock_info_t); +} diff --git a/src/cls/lock/cls_lock_types.h b/src/cls/lock/cls_lock_types.h new file mode 100644 index 000000000..13f3e1478 --- /dev/null +++ b/src/cls/lock/cls_lock_types.h @@ -0,0 +1,174 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_LOCK_TYPES_H +#define CEPH_CLS_LOCK_TYPES_H + +#include "include/encoding.h" +#include "include/types.h" +#include "include/utime.h" +#include "msg/msg_types.h" + +/* lock flags */ +#define LOCK_FLAG_MAY_RENEW 0x1 /* idempotent lock acquire */ +#define LOCK_FLAG_MUST_RENEW 0x2 /* lock must already be acquired */ + +enum class ClsLockType { + NONE = 0, + EXCLUSIVE = 1, + SHARED = 2, + EXCLUSIVE_EPHEMERAL = 3, /* lock object is removed @ unlock */ +}; + +inline const char *cls_lock_type_str(ClsLockType type) +{ + switch (type) { + case ClsLockType::NONE: + return "none"; + case ClsLockType::EXCLUSIVE: + return "exclusive"; + case ClsLockType::SHARED: + return "shared"; + case ClsLockType::EXCLUSIVE_EPHEMERAL: + return "exclusive-ephemeral"; + default: + return "<unknown>"; + } +} + +inline bool cls_lock_is_exclusive(ClsLockType type) { + return ClsLockType::EXCLUSIVE == type || ClsLockType::EXCLUSIVE_EPHEMERAL == type; +} + +inline bool cls_lock_is_ephemeral(ClsLockType type) { + return ClsLockType::EXCLUSIVE_EPHEMERAL == type; +} + +inline bool cls_lock_is_valid(ClsLockType type) { + return ClsLockType::SHARED == type || + ClsLockType::EXCLUSIVE == type || + ClsLockType::EXCLUSIVE_EPHEMERAL == type; +} + +namespace rados { + namespace cls { + namespace lock { + + /* + * locker_id_t: the locker id, needs to be unique in a single lock + */ + struct locker_id_t { + entity_name_t locker; // locker's client name + std::string cookie; // locker's cookie. + + locker_id_t() {} + locker_id_t(entity_name_t& _n, const std::string& _c) : locker(_n), cookie(_c) {} + + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(locker, bl); + encode(cookie, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(locker, bl); + decode(cookie, bl); + DECODE_FINISH(bl); + } + + bool operator<(const locker_id_t& rhs) const { + if (locker == rhs.locker) + return cookie.compare(rhs.cookie) < 0; + if (locker < rhs.locker) + return true; + return false; + } + void dump(ceph::Formatter *f) const; + friend std::ostream& operator<<(std::ostream& out, + const locker_id_t& data) { + out << data.locker; + return out; + } + static void generate_test_instances(std::list<locker_id_t*>& o); + }; + WRITE_CLASS_ENCODER(locker_id_t) + + struct locker_info_t + { + utime_t expiration; // expiration: non-zero means epoch of locker expiration + entity_addr_t addr; // addr: locker address + std::string description; // description: locker description, may be empty + + locker_info_t() {} + locker_info_t(const utime_t& _e, const entity_addr_t& _a, + const std::string& _d) : expiration(_e), addr(_a), description(_d) {} + + void encode(ceph::buffer::list &bl, uint64_t features) const { + ENCODE_START(1, 1, bl); + encode(expiration, bl); + encode(addr, bl, features); + encode(description, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(expiration, bl); + decode(addr, bl); + decode(description, bl); + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + friend std::ostream& operator<<(std::ostream& out, + const locker_info_t& data) { + using ceph::operator <<; + out << "{addr:" << data.addr << ", exp:"; + + const auto& exp = data.expiration; + if (exp.is_zero()) { + out << "never}"; + } else { + out << exp.to_real_time() << "}"; + } + + return out; + } + static void generate_test_instances(std::list<locker_info_t *>& o); + }; + WRITE_CLASS_ENCODER_FEATURES(locker_info_t) + + struct lock_info_t { + std::map<locker_id_t, locker_info_t> lockers; // map of lockers + ClsLockType lock_type; // lock type (exclusive / shared) + std::string tag; // tag: operations on lock can only succeed with this tag + // as long as set of non expired lockers + // is bigger than 0. + + void encode(ceph::buffer::list &bl, uint64_t features) const { + ENCODE_START(1, 1, bl); + encode(lockers, bl, features); + uint8_t t = (uint8_t)lock_type; + encode(t, bl); + encode(tag, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + decode(lockers, bl); + uint8_t t; + decode(t, bl); + lock_type = (ClsLockType)t; + decode(tag, bl); + DECODE_FINISH(bl); + } + + lock_info_t() : lock_type(ClsLockType::NONE) {} + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<lock_info_t *>& o); + }; + WRITE_CLASS_ENCODER_FEATURES(lock_info_t); + } + } +} + +#endif |