]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cls_lock: support updating the lock cookie without releasing the lock
authorJason Dillaman <dillaman@redhat.com>
Mon, 15 Aug 2016 20:50:43 +0000 (16:50 -0400)
committerJason Dillaman <dillaman@redhat.com>
Tue, 23 Aug 2016 16:23:07 +0000 (12:23 -0400)
Fixes: http://tracker.ceph.com/issues/17015
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/cls/lock/cls_lock.cc
src/cls/lock/cls_lock_client.cc
src/cls/lock/cls_lock_client.h
src/cls/lock/cls_lock_ops.cc
src/cls/lock/cls_lock_ops.h
src/test/cls_lock/test_cls_lock.cc
src/test/encoding/types.h

index dc61a62663b319acad45cdf313d098e417d2cd51..c8f3359cee55a1c59c090341d62c61fb9a12a3f4 100644 (file)
@@ -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<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;
+}
+
 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;
 }
index 30466fbd7e5bae9da297397cfaa5ecd42450a5e0..fc2790b92d756a743ebe2f0d43c8578edb5096c0 100644 (file)
@@ -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);
index b60d25e098a9f6508f69f18b5d123e7c281aa0af..13fb3f154b0d3488325a30694ccc3900670494c1 100644 (file)
@@ -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;
index 7de832623d268dda88a5bd19d70cbf1de4baffe9..4b9a8a3d6ae8572a5e6a0fe5c673aa9854ad281e 100644 (file)
@@ -188,3 +188,24 @@ void cls_lock_assert_op::generate_test_instances(list<cls_lock_assert_op*>& 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<cls_lock_set_cookie_op*>& 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);
+}
+
index 9cfe41c4b9d6abffd82dcd45734906631258b8e5..dbdddfe21407d5182673e4599c689f630fc76dec 100644 (file)
@@ -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<cls_lock_set_cookie_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_set_cookie_op)
+
 #endif
index 72dbb8ecad0b76a7f8c18fc5a4f1a2058b6fc99b..104081559e152d3dc850e4f1a1bc14e78e196d79 100644 (file)
@@ -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));
+}
index 386e83ace2b2aff289db5aaa3d4522b1c0ffb0bf..dd6a5afa58ae49f752e9c2a30e031306c6899116 100644 (file)
@@ -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)