From: Jason Dillaman Date: Mon, 15 Aug 2016 20:50:43 +0000 (-0400) Subject: cls_lock: support updating the lock cookie without releasing the lock X-Git-Tag: v11.0.1~392^2~8 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=377f57652f8ddae53f44c59f21e89c51b2bf0f7b;p=ceph.git cls_lock: support updating the lock cookie without releasing the lock Fixes: http://tracker.ceph.com/issues/17015 Signed-off-by: Jason Dillaman --- diff --git a/src/cls/lock/cls_lock.cc b/src/cls/lock/cls_lock.cc index dc61a62663b3..c8f3359cee55 100644 --- a/src/cls/lock/cls_lock.cc +++ b/src/cls/lock/cls_lock.cc @@ -45,6 +45,7 @@ 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; #define LOCK_PREFIX "lock." @@ -512,6 +513,94 @@ int assert_locked(cls_method_context_t hctx, bufferlist *in, bufferlist *out) 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 { + 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 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; +} + void __cls_init() { CLS_LOG(20, "Loaded lock class!"); @@ -535,6 +624,9 @@ void __cls_init() 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 index 30466fbd7e5b..fc2790b92d75 100644 --- a/src/cls/lock/cls_lock_client.cc +++ b/src/cls/lock/cls_lock_client.cc @@ -189,6 +189,22 @@ namespace rados { 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_exclusive(ObjectOperation *op) { assert_locked(op, name, LOCK_EXCLUSIVE, cookie, tag); diff --git a/src/cls/lock/cls_lock_client.h b/src/cls/lock/cls_lock_client.h index b60d25e098a9..13fb3f154b0d 100644 --- a/src/cls/lock/cls_lock_client.h +++ b/src/cls/lock/cls_lock_client.h @@ -59,6 +59,11 @@ namespace rados { 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; diff --git a/src/cls/lock/cls_lock_ops.cc b/src/cls/lock/cls_lock_ops.cc index 7de832623d26..4b9a8a3d6ae8 100644 --- a/src/cls/lock/cls_lock_ops.cc +++ b/src/cls/lock/cls_lock_ops.cc @@ -188,3 +188,24 @@ void cls_lock_assert_op::generate_test_instances(list& o) 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& o) +{ + cls_lock_set_cookie_op *i = new cls_lock_set_cookie_op; + i->name = "name"; + i->type = LOCK_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 index 9cfe41c4b9d6..dbdddfe21407 100644 --- a/src/cls/lock/cls_lock_ops.h +++ b/src/cls/lock/cls_lock_ops.h @@ -203,4 +203,40 @@ struct cls_lock_assert_op }; WRITE_CLASS_ENCODER(cls_lock_assert_op) +struct cls_lock_set_cookie_op +{ + string name; + ClsLockType type; + string cookie; + string tag; + string new_cookie; + + cls_lock_set_cookie_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(new_cookie, 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(new_cookie, bl); + DECODE_FINISH(bl); + } + void dump(Formatter *f) const; + static void generate_test_instances(list& o); +}; +WRITE_CLASS_ENCODER(cls_lock_set_cookie_op) + #endif diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc index 72dbb8ecad0b..104081559e15 100644 --- a/src/test/cls_lock/test_cls_lock.cc +++ b/src/test/cls_lock/test_cls_lock.cc @@ -338,3 +338,54 @@ TEST(ClsLock, TestAssertLocked) { ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster)); } + +TEST(ClsLock, TestSetCookie) { + 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"; + string name = "name"; + string tag = "tag"; + string cookie = "cookie"; + string new_cookie = "new cookie"; + librados::ObjectWriteOperation op1; + set_cookie(&op1, name, LOCK_SHARED, cookie, tag, new_cookie); + ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1)); + + librados::ObjectWriteOperation op2; + lock(&op2, name, LOCK_SHARED, cookie, tag, "", utime_t{}, 0); + ASSERT_EQ(0, ioctx.operate(oid, &op2)); + + librados::ObjectWriteOperation op3; + lock(&op3, name, LOCK_SHARED, "cookie 2", tag, "", utime_t{}, 0); + ASSERT_EQ(0, ioctx.operate(oid, &op3)); + + librados::ObjectWriteOperation op4; + set_cookie(&op4, name, LOCK_SHARED, cookie, tag, cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4)); + + librados::ObjectWriteOperation op5; + set_cookie(&op5, name, LOCK_SHARED, cookie, "wrong tag", new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5)); + + librados::ObjectWriteOperation op6; + set_cookie(&op6, name, LOCK_SHARED, "wrong cookie", tag, new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op6)); + + librados::ObjectWriteOperation op7; + set_cookie(&op7, name, LOCK_EXCLUSIVE, cookie, tag, new_cookie); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op7)); + + librados::ObjectWriteOperation op8; + set_cookie(&op8, name, LOCK_SHARED, cookie, tag, "cookie 2"); + ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op8)); + + librados::ObjectWriteOperation op9; + set_cookie(&op9, name, LOCK_SHARED, cookie, tag, new_cookie); + ASSERT_EQ(0, ioctx.operate(oid, &op9)); + + 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 386e83ace2b2..dd6a5afa58ae 100644 --- a/src/test/encoding/types.h +++ b/src/test/encoding/types.h @@ -384,6 +384,7 @@ TYPE(cls_lock_get_info_op) TYPE_FEATUREFUL(cls_lock_get_info_reply) TYPE(cls_lock_list_locks_reply) TYPE(cls_lock_assert_op) +TYPE(cls_lock_set_cookie_op) #include "cls/replica_log/cls_replica_log_types.h" TYPE(cls_replica_log_item_marker)