From: Jason Dillaman Date: Tue, 18 Nov 2014 08:56:41 +0000 (-0500) Subject: cls_lock: New assert_locked operation X-Git-Tag: v0.93~193^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=71e84e048f73034cb126b9fe25e4571806c351c3;p=ceph.git cls_lock: New assert_locked operation The assert_locked operation can be combined with other RADOS ops to prevent an update to a locked object when the client doesn't own the lock. It will not attempt to acquire the lock if the object is not currently locked. Signed-off-by: Jason Dillaman --- diff --git a/src/cls/lock/cls_lock.cc b/src/cls/lock/cls_lock.cc index b4772e0d08f8..cefb870d173b 100644 --- a/src/cls/lock/cls_lock.cc +++ b/src/cls/lock/cls_lock.cc @@ -42,6 +42,7 @@ 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; #define LOCK_PREFIX "lock." @@ -438,6 +439,78 @@ static int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *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 { + 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::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!"); @@ -458,6 +531,9 @@ void __cls_init() 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; } diff --git a/src/cls/lock/cls_lock_client.cc b/src/cls/lock/cls_lock_client.cc index 54af41cd0492..30466fbd7e5b 100644 --- a/src/cls/lock/cls_lock_client.cc +++ b/src/cls/lock/cls_lock_client.cc @@ -175,6 +175,30 @@ namespace rados { 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, diff --git a/src/cls/lock/cls_lock_client.h b/src/cls/lock/cls_lock_client.h index 4e2144c79b4c..b60d25e098a9 100644 --- a/src/cls/lock/cls_lock_client.h +++ b/src/cls/lock/cls_lock_client.h @@ -14,7 +14,6 @@ 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, @@ -55,6 +54,11 @@ namespace rados { map *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; @@ -79,6 +83,9 @@ namespace rados { } } + 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); diff --git a/src/cls/lock/cls_lock_ops.cc b/src/cls/lock/cls_lock_ops.cc index 557e7d4d5d06..7de832623d26 100644 --- a/src/cls/lock/cls_lock_ops.cc +++ b/src/cls/lock/cls_lock_ops.cc @@ -169,4 +169,22 @@ void cls_lock_list_locks_reply::generate_test_instances(listdump_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& 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); +} diff --git a/src/cls/lock/cls_lock_ops.h b/src/cls/lock/cls_lock_ops.h index 95f745e5c4bf..d2076f1169a7 100644 --- a/src/cls/lock/cls_lock_ops.h +++ b/src/cls/lock/cls_lock_ops.h @@ -170,4 +170,37 @@ struct cls_lock_list_locks_reply }; 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& o); +}; +WRITE_CLASS_ENCODER(cls_lock_assert_op) + #endif diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc index ead03bb21835..72dbb8ecad0b 100644 --- a/src/test/cls_lock/test_cls_lock.cc +++ b/src/test/cls_lock/test_cls_lock.cc @@ -298,3 +298,43 @@ TEST(ClsLock, TestLockDuration) { 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)); +} diff --git a/src/test/encoding/types.h b/src/test/encoding/types.h index 4a70574f8e5e..684364e1841a 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -314,6 +314,7 @@ TYPE(cls_lock_break_op) 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)