summaryrefslogtreecommitdiffstats
path: root/src/cls/refcount
diff options
context:
space:
mode:
Diffstat (limited to 'src/cls/refcount')
-rw-r--r--src/cls/refcount/cls_refcount.cc216
-rw-r--r--src/cls/refcount/cls_refcount_client.cc61
-rw-r--r--src/cls/refcount/cls_refcount_client.h34
-rw-r--r--src/cls/refcount/cls_refcount_ops.cc104
-rw-r--r--src/cls/refcount/cls_refcount_ops.h154
5 files changed, 569 insertions, 0 deletions
diff --git a/src/cls/refcount/cls_refcount.cc b/src/cls/refcount/cls_refcount.cc
new file mode 100644
index 00000000..73cc3b85
--- /dev/null
+++ b/src/cls/refcount/cls_refcount.cc
@@ -0,0 +1,216 @@
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+
+#include "objclass/objclass.h"
+#include "cls/refcount/cls_refcount_ops.h"
+
+#include "include/compat.h"
+
+CLS_VER(1,0)
+CLS_NAME(refcount)
+
+
+#define REFCOUNT_ATTR "refcount"
+
+static string wildcard_tag;
+
+static int read_refcount(cls_method_context_t hctx, bool implicit_ref, obj_refcount *objr)
+{
+ bufferlist bl;
+ objr->refs.clear();
+ int ret = cls_cxx_getxattr(hctx, REFCOUNT_ATTR, &bl);
+ if (ret == -ENODATA) {
+ if (implicit_ref) {
+ objr->refs[wildcard_tag] = true;
+ }
+ return 0;
+ }
+ if (ret < 0)
+ return ret;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(*objr, iter);
+ } catch (buffer::error& err) {
+ CLS_LOG(0, "ERROR: read_refcount(): failed to decode refcount entry\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int set_refcount(cls_method_context_t hctx, const struct obj_refcount& objr)
+{
+ bufferlist bl;
+
+ encode(objr, bl);
+
+ int ret = cls_cxx_setxattr(hctx, REFCOUNT_ATTR, &bl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cls_rc_refcount_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_refcount_get_op op;
+ try {
+ decode(op, in_iter);
+ } catch (buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_rc_refcount_get(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ obj_refcount objr;
+ int ret = read_refcount(hctx, op.implicit_ref, &objr);
+ if (ret < 0)
+ return ret;
+
+ CLS_LOG(10, "cls_rc_refcount_get() tag=%s\n", op.tag.c_str());
+
+ objr.refs[op.tag] = true;
+
+ ret = set_refcount(hctx, objr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cls_rc_refcount_put(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_refcount_put_op op;
+ try {
+ decode(op, in_iter);
+ } catch (buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_rc_refcount_put(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ obj_refcount objr;
+ int ret = read_refcount(hctx, op.implicit_ref, &objr);
+ if (ret < 0)
+ return ret;
+
+ if (objr.refs.empty()) {// shouldn't happen!
+ CLS_LOG(0, "ERROR: cls_rc_refcount_put() was called without any references!\n");
+ return -EINVAL;
+ }
+
+ CLS_LOG(10, "cls_rc_refcount_put() tag=%s\n", op.tag.c_str());
+
+ bool found = false;
+ map<string, bool>::iterator iter = objr.refs.find(op.tag);
+ if (iter != objr.refs.end()) {
+ found = true;
+ } else if (op.implicit_ref) {
+ iter = objr.refs.find(wildcard_tag);
+ if (iter != objr.refs.end()) {
+ found = true;
+ }
+ }
+
+ if (!found ||
+ objr.retired_refs.find(op.tag) != objr.retired_refs.end())
+ return 0;
+
+ objr.retired_refs.insert(op.tag);
+ objr.refs.erase(iter);
+
+ if (objr.refs.empty()) {
+ return cls_cxx_remove(hctx);
+ }
+
+ ret = set_refcount(hctx, objr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cls_rc_refcount_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_refcount_set_op op;
+ try {
+ decode(op, in_iter);
+ } catch (buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_refcount_set(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ if (!op.refs.size()) {
+ return cls_cxx_remove(hctx);
+ }
+
+ obj_refcount objr;
+ list<string>::iterator iter;
+ for (iter = op.refs.begin(); iter != op.refs.end(); ++iter) {
+ objr.refs[*iter] = true;
+ }
+
+ int ret = set_refcount(hctx, objr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cls_rc_refcount_read(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_refcount_read_op op;
+ try {
+ decode(op, in_iter);
+ } catch (buffer::error& err) {
+ CLS_LOG(1, "ERROR: cls_rc_refcount_read(): failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ obj_refcount objr;
+
+ cls_refcount_read_ret read_ret;
+ int ret = read_refcount(hctx, op.implicit_ref, &objr);
+ if (ret < 0)
+ return ret;
+
+ map<string, bool>::iterator iter;
+ for (iter = objr.refs.begin(); iter != objr.refs.end(); ++iter) {
+ read_ret.refs.push_back(iter->first);
+ }
+
+ encode(read_ret, *out);
+
+ return 0;
+}
+
+CLS_INIT(refcount)
+{
+ CLS_LOG(1, "Loaded refcount class!");
+
+ cls_handle_t h_class;
+ cls_method_handle_t h_refcount_get;
+ cls_method_handle_t h_refcount_put;
+ cls_method_handle_t h_refcount_set;
+ cls_method_handle_t h_refcount_read;
+
+ cls_register("refcount", &h_class);
+
+ /* refcount */
+ cls_register_cxx_method(h_class, "get", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_refcount_get, &h_refcount_get);
+ cls_register_cxx_method(h_class, "put", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_refcount_put, &h_refcount_put);
+ cls_register_cxx_method(h_class, "set", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_refcount_set, &h_refcount_set);
+ cls_register_cxx_method(h_class, "read", CLS_METHOD_RD, cls_rc_refcount_read, &h_refcount_read);
+
+ return;
+}
+
diff --git a/src/cls/refcount/cls_refcount_client.cc b/src/cls/refcount/cls_refcount_client.cc
new file mode 100644
index 00000000..9d5210c8
--- /dev/null
+++ b/src/cls/refcount/cls_refcount_client.cc
@@ -0,0 +1,61 @@
+#include <errno.h>
+
+#include "cls/refcount/cls_refcount_client.h"
+#include "cls/refcount/cls_refcount_ops.h"
+#include "include/rados/librados.hpp"
+
+using namespace librados;
+
+
+void cls_refcount_get(librados::ObjectWriteOperation& op, const string& tag, bool implicit_ref)
+{
+ bufferlist in;
+ cls_refcount_get_op call;
+ call.tag = tag;
+ call.implicit_ref = implicit_ref;
+ encode(call, in);
+ op.exec("refcount", "get", in);
+}
+
+void cls_refcount_put(librados::ObjectWriteOperation& op, const string& tag, bool implicit_ref)
+{
+ bufferlist in;
+ cls_refcount_put_op call;
+ call.tag = tag;
+ call.implicit_ref = implicit_ref;
+ encode(call, in);
+ op.exec("refcount", "put", in);
+}
+
+void cls_refcount_set(librados::ObjectWriteOperation& op, list<string>& refs)
+{
+ bufferlist in;
+ cls_refcount_set_op call;
+ call.refs = refs;
+ encode(call, in);
+ op.exec("refcount", "set", in);
+}
+
+int cls_refcount_read(librados::IoCtx& io_ctx, string& oid, list<string> *refs, bool implicit_ref)
+{
+ bufferlist in, out;
+ cls_refcount_read_op call;
+ call.implicit_ref = implicit_ref;
+ encode(call, in);
+ int r = io_ctx.exec(oid, "refcount", "read", in, out);
+ if (r < 0)
+ return r;
+
+ cls_refcount_read_ret ret;
+ try {
+ auto iter = out.cbegin();
+ decode(ret, iter);
+ } catch (buffer::error& err) {
+ return -EIO;
+ }
+
+ *refs = ret.refs;
+
+ return r;
+}
+
diff --git a/src/cls/refcount/cls_refcount_client.h b/src/cls/refcount/cls_refcount_client.h
new file mode 100644
index 00000000..c41a4f6c
--- /dev/null
+++ b/src/cls/refcount/cls_refcount_client.h
@@ -0,0 +1,34 @@
+#ifndef CEPH_CLS_REFCOUNT_CLIENT_H
+#define CEPH_CLS_REFCOUNT_CLIENT_H
+
+#include "include/rados/librados_fwd.hpp"
+#include "include/types.h"
+
+/*
+ * refcount objclass
+ *
+ * The refcount objclass implements a refcounting scheme that allows having multiple references
+ * to a single rados object. The canonical way to use it is to add a reference and to remove a
+ * reference using a specific tag. This way we ensure that refcounting operations are idempotent,
+ * that is, a single client can only increase/decrease the refcount once using a single tag, so
+ * any replay of operations (implicit or explicit) is possible.
+ *
+ * So, the regular usage would be to create an object, to increase the refcount. Then, when
+ * wanting to have another reference to it, increase the refcount using a different tag. When
+ * removing a reference it is required to drop the refcount (using the same tag that was used
+ * for that reference). When the refcount drops to zero, the object is removed automaticfally.
+ *
+ * In order to maintain backwards compatibility with objects that were created without having
+ * their refcount increased, the implicit_ref was added. Any object that was created without
+ * having it's refcount increased (explicitly) is having an implicit refcount of 1. Since
+ * we don't have a tag for this refcount, we consider this tag as a wildcard. So if the refcount
+ * is being decreased by an unknown tag and we still have one wildcard tag, we'll accept it
+ * as the relevant tag, and the refcount will be decreased.
+ */
+
+void cls_refcount_get(librados::ObjectWriteOperation& op, const string& tag, bool implicit_ref = false);
+void cls_refcount_put(librados::ObjectWriteOperation& op, const string& tag, bool implicit_ref = false);
+void cls_refcount_set(librados::ObjectWriteOperation& op, list<string>& refs);
+int cls_refcount_read(librados::IoCtx& io_ctx, string& oid, list<string> *refs, bool implicit_ref = false);
+
+#endif
diff --git a/src/cls/refcount/cls_refcount_ops.cc b/src/cls/refcount/cls_refcount_ops.cc
new file mode 100644
index 00000000..ca7f93ed
--- /dev/null
+++ b/src/cls/refcount/cls_refcount_ops.cc
@@ -0,0 +1,104 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls_refcount_ops.h"
+#include "common/Formatter.h"
+#include "common/ceph_json.h"
+
+void cls_refcount_get_op::dump(ceph::Formatter *f) const
+{
+ f->dump_string("tag", tag);
+ f->dump_int("implicit_ref", (int)implicit_ref);
+}
+
+void cls_refcount_get_op::generate_test_instances(list<cls_refcount_get_op*>& ls)
+{
+ ls.push_back(new cls_refcount_get_op);
+ ls.push_back(new cls_refcount_get_op);
+ ls.back()->tag = "foo";
+ ls.back()->implicit_ref = true;
+}
+
+
+void cls_refcount_put_op::dump(ceph::Formatter *f) const
+{
+ f->dump_string("tag", tag);
+ f->dump_int("implicit_ref", (int)implicit_ref);
+}
+
+void cls_refcount_put_op::generate_test_instances(list<cls_refcount_put_op*>& ls)
+{
+ ls.push_back(new cls_refcount_put_op);
+ ls.push_back(new cls_refcount_put_op);
+ ls.back()->tag = "foo";
+ ls.back()->implicit_ref = true;
+}
+
+
+
+void cls_refcount_set_op::dump(ceph::Formatter *f) const
+{
+ encode_json("refs", refs, f);
+}
+
+void cls_refcount_set_op::generate_test_instances(list<cls_refcount_set_op*>& ls)
+{
+ ls.push_back(new cls_refcount_set_op);
+ ls.push_back(new cls_refcount_set_op);
+ ls.back()->refs.push_back("foo");
+ ls.back()->refs.push_back("bar");
+}
+
+
+void cls_refcount_read_op::dump(ceph::Formatter *f) const
+{
+ f->dump_int("implicit_ref", (int)implicit_ref);
+}
+
+void cls_refcount_read_op::generate_test_instances(list<cls_refcount_read_op*>& ls)
+{
+ ls.push_back(new cls_refcount_read_op);
+ ls.push_back(new cls_refcount_read_op);
+ ls.back()->implicit_ref = true;
+}
+
+
+void cls_refcount_read_ret::dump(ceph::Formatter *f) const
+{
+ f->open_array_section("refs");
+ for (list<string>::const_iterator p = refs.begin(); p != refs.end(); ++p)
+ f->dump_string("ref", *p);
+ f->close_section();
+}
+
+void cls_refcount_read_ret::generate_test_instances(list<cls_refcount_read_ret*>& ls)
+{
+ ls.push_back(new cls_refcount_read_ret);
+ ls.push_back(new cls_refcount_read_ret);
+ ls.back()->refs.push_back("foo");
+ ls.back()->refs.push_back("bar");
+}
+
+void obj_refcount::dump(ceph::Formatter *f) const
+{
+ f->open_array_section("refs");
+ for (const auto &kv: refs) {
+ f->open_object_section("ref");
+ f->dump_string("oid", kv.first.c_str());
+ f->dump_bool("active",kv.second);
+ f->close_section();
+ }
+ f->close_section();
+
+ f->open_array_section("retired_refs");
+ for (const auto& it: retired_refs)
+ f->dump_string("ref", it.c_str());
+ f->close_section();
+}
+
+void obj_refcount::generate_test_instances(list<obj_refcount*>& ls)
+{
+ ls.push_back(new obj_refcount);
+ ls.back()->refs.emplace("foo",true);
+ ls.back()->retired_refs.emplace("bar");
+}
diff --git a/src/cls/refcount/cls_refcount_ops.h b/src/cls/refcount/cls_refcount_ops.h
new file mode 100644
index 00000000..946c11aa
--- /dev/null
+++ b/src/cls/refcount/cls_refcount_ops.h
@@ -0,0 +1,154 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_REFCOUNT_OPS_H
+#define CEPH_CLS_REFCOUNT_OPS_H
+
+#include "include/types.h"
+#include "common/hobject.h"
+
+struct cls_refcount_get_op {
+ string tag;
+ bool implicit_ref;
+
+ cls_refcount_get_op() : implicit_ref(false) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(tag, bl);
+ encode(implicit_ref, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(tag, bl);
+ decode(implicit_ref, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<cls_refcount_get_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_refcount_get_op)
+
+struct cls_refcount_put_op {
+ string tag;
+ bool implicit_ref; // assume wildcard reference for
+ // objects without a set ref
+
+ cls_refcount_put_op() : implicit_ref(false) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(tag, bl);
+ encode(implicit_ref, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(tag, bl);
+ decode(implicit_ref, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<cls_refcount_put_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_refcount_put_op)
+
+struct cls_refcount_set_op {
+ list<string> refs;
+
+ cls_refcount_set_op() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(refs, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(refs, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<cls_refcount_set_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_refcount_set_op)
+
+struct cls_refcount_read_op {
+ bool implicit_ref; // assume wildcard reference for
+ // objects without a set ref
+
+ cls_refcount_read_op() : implicit_ref(false) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(implicit_ref, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(implicit_ref, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<cls_refcount_read_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_refcount_read_op)
+
+struct cls_refcount_read_ret {
+ list<string> refs;
+
+ cls_refcount_read_ret() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(refs, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(refs, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<cls_refcount_read_ret*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_refcount_read_ret)
+
+struct obj_refcount {
+ map<string, bool> refs;
+ set<string> retired_refs;
+
+ obj_refcount() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(2, 1, bl);
+ encode(refs, bl);
+ encode(retired_refs, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(2, bl);
+ decode(refs, bl);
+ if (struct_v >= 2) {
+ decode(retired_refs, bl);
+ }
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<obj_refcount*>& ls);
+};
+WRITE_CLASS_ENCODER(obj_refcount)
+
+#endif