summaryrefslogtreecommitdiffstats
path: root/src/cls/lock
diff options
context:
space:
mode:
Diffstat (limited to 'src/cls/lock')
-rw-r--r--src/cls/lock/cls_lock.cc648
-rw-r--r--src/cls/lock/cls_lock_client.cc286
-rw-r--r--src/cls/lock/cls_lock_client.h141
-rw-r--r--src/cls/lock/cls_lock_ops.cc213
-rw-r--r--src/cls/lock/cls_lock_ops.h245
-rw-r--r--src/cls/lock/cls_lock_types.cc98
-rw-r--r--src/cls/lock/cls_lock_types.h174
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