]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cls_lock: New assert_locked operation
authorJason Dillaman <dillaman@redhat.com>
Tue, 18 Nov 2014 08:56:41 +0000 (03:56 -0500)
committerJosh Durgin <jdurgin@redhat.com>
Sat, 24 Jan 2015 23:05:49 +0000 (15:05 -0800)
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 <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 b4772e0d08f81bca3707587304a31f2037dd239b..cefb870d173b4ee8b2c3da11f5074cf2cfc363bd 100644 (file)
@@ -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<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!");
@@ -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;
 }
index 54af41cd049265b060de61a7fb0c0de9edceb621..30466fbd7e5bae9da297397cfaa5ecd42450a5e0 100644 (file)
@@ -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,
index 4e2144c79b4cab0bdebedb0ed35cd6471ba69473..b60d25e098a9f6508f69f18b5d123e7c281aa0af 100644 (file)
@@ -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<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;
@@ -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);
index 557e7d4d5d069e5e3ca6b408862de2b76414a36e..7de832623d268dda88a5bd19d70cbf1de4baffe9 100644 (file)
@@ -169,4 +169,22 @@ void cls_lock_list_locks_reply::generate_test_instances(list<cls_lock_list_locks
   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);
+}
 
index 95f745e5c4bf2a0761ade7f57d839e31cd3facc7..d2076f1169a701bddb422975b4c1069484709540 100644 (file)
@@ -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<cls_lock_assert_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_assert_op)
+
 #endif
index ead03bb218354dbb7e2bd9091784fba62357b8af..72dbb8ecad0b76a7f8c18fc5a4f1a2058b6fc99b 100644 (file)
@@ -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));
+}
index 4a70574f8e5e94154f21144ebd2da33ecbd108ca..684364e1841a33c32972242f2ed2fe7e05a83b1e 100644 (file)
@@ -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)