diff options
Diffstat (limited to 'src/cls/refcount')
-rw-r--r-- | src/cls/refcount/cls_refcount.cc | 216 | ||||
-rw-r--r-- | src/cls/refcount/cls_refcount_client.cc | 61 | ||||
-rw-r--r-- | src/cls/refcount/cls_refcount_client.h | 34 | ||||
-rw-r--r-- | src/cls/refcount/cls_refcount_ops.cc | 104 | ||||
-rw-r--r-- | src/cls/refcount/cls_refcount_ops.h | 154 |
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 |