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;
#define LOCK_PREFIX "lock."
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 {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error& err) {
+ return -EINVAL;
+ }
+
+ if (op.type != LOCK_EXCLUSIVE && op.type != LOCK_SHARED) {
+ 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);
+ 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 assert client");
+ return -EBUSY;
+ }
+ return 0;
+}
+
void __cls_init()
{
CLS_LOG(20, "Loaded lock class!");
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,
+ assert_locked, &h_assert_locked);
return;
}
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 Lock::assert_locked_exclusive(ObjectOperation *op)
+ {
+ assert_locked(op, name, LOCK_EXCLUSIVE, cookie, tag);
+ }
+
+ void Lock::assert_locked_shared(ObjectOperation *op)
+ {
+ assert_locked(op, name, LOCK_SHARED, cookie, tag);
+ }
+
void Lock::lock_shared(ObjectWriteOperation *op)
{
lock(op, name, LOCK_SHARED,
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,
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);
+
class Lock {
std::string name;
std::string cookie;
}
}
+ void assert_locked_exclusive(librados::ObjectOperation *rados_op);
+ void assert_locked_shared(librados::ObjectOperation *rados_op);
+
/* ObjectWriteOperation */
void lock_exclusive(librados::ObjectWriteOperation *ioctx);
void lock_shared(librados::ObjectWriteOperation *ioctx);
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 = LOCK_SHARED;
+ i->cookie = "cookie";
+ i->tag = "tag";
+ o.push_back(i);
+ o.push_back(new cls_lock_assert_op);
+}
};
WRITE_CLASS_ENCODER(cls_lock_list_locks_reply)
+struct cls_lock_assert_op
+{
+ string name;
+ ClsLockType type;
+ string cookie;
+ string tag;
+
+ cls_lock_assert_op() : type(LOCK_NONE) {}
+
+ void encode(bufferlist &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(bufferlist::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(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_assert_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_assert_op)
+
#endif
ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
}
+
+TEST(ClsLock, TestAssertLocked) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ librados::ObjectWriteOperation op1;
+ l.assert_locked_exclusive(&op1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ l.assert_locked_shared(&op2);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op2));
+
+ l.set_tag("tag");
+ librados::ObjectWriteOperation op3;
+ l.assert_locked_exclusive(&op3);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op3));
+ l.set_tag("");
+
+ l.set_cookie("cookie");
+ librados::ObjectWriteOperation op4;
+ l.assert_locked_exclusive(&op4);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+ l.set_cookie("");
+
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ librados::ObjectWriteOperation op5;
+ l.assert_locked_exclusive(&op5);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
TYPE(cls_lock_get_info_op)
TYPE(cls_lock_get_info_reply)
TYPE(cls_lock_list_locks_reply)
+TYPE(cls_lock_assert_op)
#include "cls/replica_log/cls_replica_log_types.h"
TYPE(cls_replica_log_item_marker)