summaryrefslogtreecommitdiffstats
path: root/src/cls/cas
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/cls/cas
parentInitial commit. (diff)
downloadceph-upstream/18.2.2.tar.xz
ceph-upstream/18.2.2.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cls/cas')
-rw-r--r--src/cls/cas/cls_cas.cc239
-rw-r--r--src/cls/cas/cls_cas_client.cc65
-rw-r--r--src/cls/cas/cls_cas_client.h43
-rw-r--r--src/cls/cas/cls_cas_internal.cc136
-rw-r--r--src/cls/cas/cls_cas_internal.h391
-rw-r--r--src/cls/cas/cls_cas_ops.h101
6 files changed, 975 insertions, 0 deletions
diff --git a/src/cls/cas/cls_cas.cc b/src/cls/cas/cls_cas.cc
new file mode 100644
index 000000000..26aecd894
--- /dev/null
+++ b/src/cls/cas/cls_cas.cc
@@ -0,0 +1,239 @@
+// -*- 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_cas_ops.h"
+#include "cls_cas_internal.h"
+
+#include "include/compat.h"
+#include "osd/osd_types.h"
+
+using ceph::bufferlist;
+using ceph::decode;
+
+CLS_VER(1,0)
+CLS_NAME(cas)
+
+
+//
+// helpers
+//
+
+static int chunk_read_refcount(
+ cls_method_context_t hctx,
+ chunk_refs_t *objr)
+{
+ bufferlist bl;
+ objr->clear();
+ int ret = cls_cxx_getxattr(hctx, CHUNK_REFCOUNT_ATTR, &bl);
+ if (ret == -ENODATA) {
+ return 0;
+ }
+ if (ret < 0)
+ return ret;
+
+ try {
+ auto iter = bl.cbegin();
+ decode(*objr, iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: chunk_read_refcount(): failed to decode refcount entry\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int chunk_set_refcount(
+ cls_method_context_t hctx,
+ const struct chunk_refs_t& objr)
+{
+ bufferlist bl;
+
+ encode(objr, bl);
+
+ int ret = cls_cxx_setxattr(hctx, CHUNK_REFCOUNT_ATTR, &bl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
+//
+// methods
+//
+
+static int chunk_create_or_get_ref(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_cas_chunk_create_or_get_ref_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ chunk_refs_t objr;
+ int ret = chunk_read_refcount(hctx, &objr);
+ if (ret == -ENOENT) {
+ // new chunk; init refs
+ CLS_LOG(10, "create oid=%s\n",
+ op.source.oid.name.c_str());
+ ret = cls_cxx_write_full(hctx, &op.data);
+ if (ret < 0) {
+ return ret;
+ }
+ objr.get(op.source);
+ ret = chunk_set_refcount(hctx, objr);
+ if (ret < 0) {
+ return ret;
+ }
+ } else if (ret < 0) {
+ return ret;
+ } else {
+ // existing chunk; inc ref
+ if (op.flags & cls_cas_chunk_create_or_get_ref_op::FLAG_VERIFY) {
+ bufferlist old;
+ cls_cxx_read(hctx, 0, 0, &old);
+ if (!old.contents_equal(op.data)) {
+ return -ENOMSG;
+ }
+ }
+ CLS_LOG(10, "inc ref oid=%s\n",
+ op.source.oid.name.c_str());
+
+ objr.get(op.source);
+
+ ret = chunk_set_refcount(hctx, objr);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int chunk_get_ref(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_cas_chunk_get_ref_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ chunk_refs_t objr;
+ int ret = chunk_read_refcount(hctx, &objr);
+ if (ret < 0) {
+ CLS_LOG(1, "ERROR: failed to read attr\n");
+ return ret;
+ }
+
+ // existing chunk; inc ref
+ CLS_LOG(10, "oid=%s\n", op.source.oid.name.c_str());
+
+ objr.get(op.source);
+
+ ret = chunk_set_refcount(hctx, objr);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static int chunk_put_ref(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+
+ cls_cas_chunk_put_ref_op op;
+ try {
+ decode(op, in_iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(1, "ERROR: failed to decode entry\n");
+ return -EINVAL;
+ }
+
+ chunk_refs_t objr;
+ int ret = chunk_read_refcount(hctx, &objr);
+ if (ret < 0)
+ return ret;
+
+ if (!objr.put(op.source)) {
+ CLS_LOG(10, "oid=%s (no ref)\n", op.source.oid.name.c_str());
+ return -ENOLINK;
+ }
+
+ if (objr.empty()) {
+ CLS_LOG(10, "oid=%s (last ref)\n", op.source.oid.name.c_str());
+ return cls_cxx_remove(hctx);
+ }
+
+ CLS_LOG(10, "oid=%s (dec)\n", op.source.oid.name.c_str());
+ ret = chunk_set_refcount(hctx, objr);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int references_chunk(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ auto in_iter = in->cbegin();
+ std::string fp_oid;
+ bufferlist indata, outdata;
+ try {
+ decode (fp_oid, in_iter);
+ }
+ catch (ceph::buffer::error& e) {
+ return -EINVAL;
+ }
+ CLS_LOG(10, "fp_oid: %s \n", fp_oid.c_str());
+
+ int ret = cls_get_manifest_ref_count(hctx, fp_oid);
+ if (ret) {
+ return ret;
+ }
+ return -ENOLINK;
+}
+
+CLS_INIT(cas)
+{
+ CLS_LOG(1, "Loaded cas class!");
+
+ cls_handle_t h_class;
+ cls_method_handle_t h_chunk_create_or_get_ref;
+ cls_method_handle_t h_chunk_get_ref;
+ cls_method_handle_t h_chunk_put_ref;
+ cls_method_handle_t h_references_chunk;
+
+ cls_register("cas", &h_class);
+
+ cls_register_cxx_method(h_class, "chunk_create_or_get_ref",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ chunk_create_or_get_ref,
+ &h_chunk_create_or_get_ref);
+ cls_register_cxx_method(h_class, "chunk_get_ref",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ chunk_get_ref,
+ &h_chunk_get_ref);
+ cls_register_cxx_method(h_class, "chunk_put_ref",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ chunk_put_ref,
+ &h_chunk_put_ref);
+ cls_register_cxx_method(h_class, "references_chunk", CLS_METHOD_RD,
+ references_chunk,
+ &h_references_chunk);
+
+ return;
+}
+
diff --git a/src/cls/cas/cls_cas_client.cc b/src/cls/cas/cls_cas_client.cc
new file mode 100644
index 000000000..085d9e52a
--- /dev/null
+++ b/src/cls/cas/cls_cas_client.cc
@@ -0,0 +1,65 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <errno.h>
+
+#include "cls/cas/cls_cas_client.h"
+#include "cls/cas/cls_cas_ops.h"
+#include "include/rados/librados.hpp"
+
+using std::set;
+using std::string;
+
+using ceph::bufferlist;
+using ceph::decode;
+using ceph::encode;
+
+void cls_cas_chunk_create_or_get_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid,
+ const bufferlist& data,
+ bool verify)
+{
+ bufferlist in;
+ cls_cas_chunk_create_or_get_ref_op call;
+ call.source = soid;
+ if (verify) {
+ call.flags |= cls_cas_chunk_create_or_get_ref_op::FLAG_VERIFY;
+ }
+ call.data = data;
+ encode(call, in);
+ op.exec("cas", "chunk_create_or_get_ref", in);
+}
+
+void cls_cas_chunk_get_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid)
+{
+ bufferlist in;
+ cls_cas_chunk_get_ref_op call;
+ call.source = soid;
+ encode(call, in);
+ op.exec("cas", "chunk_get_ref", in);
+}
+
+void cls_cas_chunk_put_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid)
+{
+ bufferlist in;
+ cls_cas_chunk_put_ref_op call;
+ call.source = soid;
+ encode(call, in);
+ op.exec("cas", "chunk_put_ref", in);
+}
+
+int cls_cas_references_chunk(
+ librados::IoCtx& io_ctx,
+ const string& oid,
+ const string& chunk_oid)
+{
+ bufferlist in, out;
+ encode(chunk_oid, in);
+ int r = io_ctx.exec(oid, "cas", "references_chunk", in, out);
+ return r;
+}
diff --git a/src/cls/cas/cls_cas_client.h b/src/cls/cas/cls_cas_client.h
new file mode 100644
index 000000000..0abbf045b
--- /dev/null
+++ b/src/cls/cas/cls_cas_client.h
@@ -0,0 +1,43 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_CAS_CLIENT_H
+#define CEPH_CLS_CAS_CLIENT_H
+
+#include "include/types.h"
+#include "include/rados/librados_fwd.hpp"
+#include "common/hobject.h"
+
+//
+// basic methods
+//
+
+/// create a chunk, or get additional reference if it already exists
+void cls_cas_chunk_create_or_get_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid,
+ const bufferlist& data,
+ bool verify=false);
+
+/// get ref on existing chunk
+void cls_cas_chunk_get_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid);
+
+/// drop reference on existing chunk
+void cls_cas_chunk_put_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid);
+
+
+//
+// advanced (used for scrub, repair, etc.)
+//
+
+/// check if a tiered rados object links to a chunk
+int cls_cas_references_chunk(
+ librados::IoCtx& io_ctx,
+ const std::string& oid,
+ const std::string& chunk_oid);
+
+#endif
diff --git a/src/cls/cas/cls_cas_internal.cc b/src/cls/cas/cls_cas_internal.cc
new file mode 100644
index 000000000..891b1c311
--- /dev/null
+++ b/src/cls/cas/cls_cas_internal.cc
@@ -0,0 +1,136 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "cls_cas_internal.h"
+
+
+chunk_refs_t::chunk_refs_t(const chunk_refs_t& other)
+{
+ *this = other;
+}
+
+chunk_refs_t& chunk_refs_t::operator=(const chunk_refs_t& other)
+{
+ // this is inefficient, but easy.
+ bufferlist bl;
+ other.encode(bl);
+ auto p = bl.cbegin();
+ decode(p);
+ return *this;
+}
+
+void chunk_refs_t::clear()
+{
+ // default to most precise impl
+ r.reset(new chunk_refs_by_object_t);
+}
+
+
+void chunk_refs_t::encode(ceph::buffer::list& bl) const
+{
+ bufferlist t;
+ _encode_r(t);
+ _encode_final(bl, t);
+}
+
+void chunk_refs_t::_encode_r(ceph::bufferlist& bl) const
+{
+ using ceph::encode;
+ switch (r->get_type()) {
+ case TYPE_BY_OBJECT:
+ encode(*(chunk_refs_by_object_t*)r.get(), bl);
+ break;
+ case TYPE_BY_HASH:
+ encode(*(chunk_refs_by_hash_t*)r.get(), bl);
+ break;
+ case TYPE_BY_POOL:
+ encode(*(chunk_refs_by_pool_t*)r.get(), bl);
+ break;
+ case TYPE_COUNT:
+ encode(*(chunk_refs_count_t*)r.get(), bl);
+ break;
+ default:
+ ceph_abort("unrecognized ref type");
+ }
+}
+
+void chunk_refs_t::dynamic_encode(ceph::buffer::list& bl, size_t max)
+{
+ bufferlist t;
+ while (true) {
+ _encode_r(t);
+ // account for the additional overhead in _encode_final
+ if (t.length() + 8 <= max) {
+ break;
+ }
+ // downgrade resolution
+ std::unique_ptr<refs_t> n;
+ switch (r->get_type()) {
+ case TYPE_BY_OBJECT:
+ r.reset(new chunk_refs_by_hash_t(
+ static_cast<chunk_refs_by_object_t*>(r.get())));
+ break;
+ case TYPE_BY_HASH:
+ if (!static_cast<chunk_refs_by_hash_t*>(r.get())->shrink()) {
+ r.reset(new chunk_refs_by_pool_t(
+ static_cast<chunk_refs_by_hash_t*>(r.get())));
+ }
+ break;
+ case TYPE_BY_POOL:
+ r.reset(new chunk_refs_count_t(r.get()));
+ break;
+ }
+ t.clear();
+ }
+ _encode_final(bl, t);
+}
+
+void chunk_refs_t::_encode_final(bufferlist& bl, bufferlist& t) const
+{
+ ENCODE_START(1, 1, bl);
+ encode(r->get_type(), bl);
+ bl.claim_append(t);
+ ENCODE_FINISH(bl);
+}
+
+void chunk_refs_t::decode(ceph::buffer::list::const_iterator& p)
+{
+ DECODE_START(1, p);
+ uint8_t t;
+ decode(t, p);
+ switch (t) {
+ case TYPE_BY_OBJECT:
+ {
+ auto n = new chunk_refs_by_object_t();
+ decode(*n, p);
+ r.reset(n);
+ }
+ break;
+ case TYPE_BY_HASH:
+ {
+ auto n = new chunk_refs_by_hash_t();
+ decode(*n, p);
+ r.reset(n);
+ }
+ break;
+ case TYPE_BY_POOL:
+ {
+ auto n = new chunk_refs_by_pool_t();
+ decode(*n, p);
+ r.reset(n);
+ }
+ break;
+ case TYPE_COUNT:
+ {
+ auto n = new chunk_refs_count_t();
+ decode(*n, p);
+ r.reset(n);
+ }
+ break;
+ default:
+ throw ceph::buffer::malformed_input(
+ std::string("unrecognized chunk ref encoding type ") +
+ stringify((int)t));
+ }
+ DECODE_FINISH(p);
+}
diff --git a/src/cls/cas/cls_cas_internal.h b/src/cls/cas/cls_cas_internal.h
new file mode 100644
index 000000000..09e7f9f1f
--- /dev/null
+++ b/src/cls/cas/cls_cas_internal.h
@@ -0,0 +1,391 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <string>
+
+#include "boost/variant.hpp"
+
+#include "include/stringify.h"
+#include "common/Formatter.h"
+#include "common/hobject.h"
+
+#define CHUNK_REFCOUNT_ATTR "chunk_refs"
+
+
+// public type
+
+struct chunk_refs_t {
+ enum {
+ TYPE_BY_OBJECT = 1,
+ TYPE_BY_HASH = 2,
+ TYPE_BY_PARTIAL = 3,
+ TYPE_BY_POOL = 4,
+ TYPE_COUNT = 5,
+ };
+ static const char *type_name(int t) {
+ switch (t) {
+ case TYPE_BY_OBJECT: return "by_object";
+ case TYPE_BY_HASH: return "by_hash";
+ case TYPE_BY_POOL: return "by_pool";
+ case TYPE_COUNT: return "count";
+ default: return "???";
+ }
+ }
+
+ struct refs_t {
+ virtual ~refs_t() {}
+ virtual uint8_t get_type() const = 0;
+ virtual bool empty() const = 0;
+ virtual uint64_t count() const = 0;
+ virtual void get(const hobject_t& o) = 0;
+ virtual bool put(const hobject_t& o) = 0;
+ virtual void dump(Formatter *f) const = 0;
+ virtual std::string describe_encoding() const {
+ return type_name(get_type());
+ }
+ };
+
+ std::unique_ptr<refs_t> r;
+
+ chunk_refs_t() {
+ clear();
+ }
+ chunk_refs_t(const chunk_refs_t& other);
+
+ chunk_refs_t& operator=(const chunk_refs_t&);
+
+ void clear();
+
+ int get_type() const {
+ return r->get_type();
+ }
+ std::string describe_encoding() const {
+ return r->describe_encoding();
+ }
+
+ bool empty() const {
+ return r->empty();
+ }
+ uint64_t count() const {
+ return r->count();
+ }
+
+ void get(const hobject_t& o) {
+ r->get(o);
+ }
+ bool put(const hobject_t& o) {
+ bool ret = r->put(o);
+ if (r->get_type() != TYPE_BY_OBJECT &&
+ r->count() == 0) {
+ clear(); // reset to full resolution, yay
+ }
+ return ret;
+ }
+
+ void _encode_r(bufferlist& bl) const;
+ void _encode_final(bufferlist& bl, bufferlist& t) const;
+ void dynamic_encode(ceph::buffer::list& bl, size_t max);
+ void encode(ceph::buffer::list& bl) const;
+ void decode(ceph::buffer::list::const_iterator& p);
+
+ void dump(Formatter *f) const {
+ r->dump(f);
+ }
+ static void generate_test_instances(std::list<chunk_refs_t*>& ls) {
+ ls.push_back(new chunk_refs_t());
+ }
+};
+WRITE_CLASS_ENCODER(chunk_refs_t)
+
+
+// encoding specific types
+// these are internal and should generally not be used directly
+
+struct chunk_refs_by_object_t : public chunk_refs_t::refs_t {
+ std::multiset<hobject_t> by_object;
+
+ uint8_t get_type() const {
+ return chunk_refs_t::TYPE_BY_OBJECT;
+ }
+ bool empty() const override {
+ return by_object.empty();
+ }
+ uint64_t count() const override {
+ return by_object.size();
+ }
+ void get(const hobject_t& o) override {
+ by_object.insert(o);
+ }
+ bool put(const hobject_t& o) override {
+ auto p = by_object.find(o);
+ if (p == by_object.end()) {
+ return false;
+ }
+ by_object.erase(p);
+ return true;
+ }
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(by_object, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator& p) {
+ DECODE_START(1, p);
+ decode(by_object, p);
+ DECODE_FINISH(p);
+ }
+ void dump(Formatter *f) const override {
+ f->dump_string("type", "by_object");
+ f->dump_unsigned("count", by_object.size());
+ f->open_array_section("refs");
+ for (auto& i : by_object) {
+ f->dump_object("ref", i);
+ }
+ f->close_section();
+ }
+};
+WRITE_CLASS_ENCODER(chunk_refs_by_object_t)
+
+struct chunk_refs_by_hash_t : public chunk_refs_t::refs_t {
+ uint64_t total = 0;
+ uint32_t hash_bits = 32; ///< how many bits of mask to encode
+ std::map<std::pair<int64_t,uint32_t>,uint64_t> by_hash;
+
+ chunk_refs_by_hash_t() {}
+ chunk_refs_by_hash_t(const chunk_refs_by_object_t *o) {
+ total = o->count();
+ for (auto& i : o->by_object) {
+ by_hash[std::make_pair(i.pool, i.get_hash())]++;
+ }
+ }
+
+ std::string describe_encoding() const {
+ using namespace std::literals;
+ return "by_hash("s + stringify(hash_bits) + " bits)";
+ }
+
+ uint32_t mask() {
+ // with the hobject_t reverse-bitwise sort, the least significant
+ // hash values are actually the most significant, so preserve them
+ // as we lose resolution.
+ return 0xffffffff >> (32 - hash_bits);
+ }
+
+ bool shrink() {
+ if (hash_bits <= 1) {
+ return false;
+ }
+ hash_bits--;
+ std::map<std::pair<int64_t,uint32_t>,uint64_t> old;
+ old.swap(by_hash);
+ auto m = mask();
+ for (auto& i : old) {
+ by_hash[std::make_pair(i.first.first, i.first.second & m)] = i.second;
+ }
+ return true;
+ }
+
+ uint8_t get_type() const {
+ return chunk_refs_t::TYPE_BY_HASH;
+ }
+ bool empty() const override {
+ return by_hash.empty();
+ }
+ uint64_t count() const override {
+ return total;
+ }
+ void get(const hobject_t& o) override {
+ by_hash[std::make_pair(o.pool, o.get_hash() & mask())]++;
+ ++total;
+ }
+ bool put(const hobject_t& o) override {
+ auto p = by_hash.find(std::make_pair(o.pool, o.get_hash() & mask()));
+ if (p == by_hash.end()) {
+ return false;
+ }
+ if (--p->second == 0) {
+ by_hash.erase(p);
+ }
+ --total;
+ return true;
+ }
+ DENC_HELPERS
+ void bound_encode(size_t& p) const {
+ p += 6 + sizeof(uint64_t) + by_hash.size() * (10 + 10);
+ }
+ void encode(::ceph::buffer::list::contiguous_appender& p) const {
+ DENC_START(1, 1, p);
+ denc_varint(total, p);
+ denc_varint(hash_bits, p);
+ denc_varint(by_hash.size(), p);
+ int hash_bytes = (hash_bits + 7) / 8;
+ for (auto& i : by_hash) {
+ denc_signed_varint(i.first.first, p);
+ // this may write some bytes past where we move cursor too; harmless!
+ *(ceph_le32*)p.get_pos_add(hash_bytes) = i.first.second;
+ denc_varint(i.second, p);
+ }
+ DENC_FINISH(p);
+ }
+ void decode(::ceph::buffer::ptr::const_iterator& p) {
+ DENC_START(1, 1, p);
+ denc_varint(total, p);
+ denc_varint(hash_bits, p);
+ uint64_t n;
+ denc_varint(n, p);
+ int hash_bytes = (hash_bits + 7) / 8;
+ while (n--) {
+ int64_t poolid;
+ ceph_le32 hash;
+ uint64_t count;
+ denc_signed_varint(poolid, p);
+ memcpy(&hash, p.get_pos_add(hash_bytes), hash_bytes);
+ denc_varint(count, p);
+ by_hash[std::make_pair(poolid, (uint32_t)hash)] = count;
+ }
+ DENC_FINISH(p);
+ }
+ void dump(Formatter *f) const override {
+ f->dump_string("type", "by_hash");
+ f->dump_unsigned("count", total);
+ f->dump_unsigned("hash_bits", hash_bits);
+ f->open_array_section("refs");
+ for (auto& i : by_hash) {
+ f->open_object_section("hash");
+ f->dump_int("pool", i.first.first);
+ f->dump_unsigned("hash", i.first.second);
+ f->dump_unsigned("count", i.second);
+ f->close_section();
+ }
+ f->close_section();
+ }
+};
+WRITE_CLASS_DENC(chunk_refs_by_hash_t)
+
+struct chunk_refs_by_pool_t : public chunk_refs_t::refs_t {
+ uint64_t total = 0;
+ std::map<int64_t,uint64_t> by_pool;
+
+ chunk_refs_by_pool_t() {}
+ chunk_refs_by_pool_t(const chunk_refs_by_hash_t *o) {
+ total = o->count();
+ for (auto& i : o->by_hash) {
+ by_pool[i.first.first] += i.second;
+ }
+ }
+
+ uint8_t get_type() const {
+ return chunk_refs_t::TYPE_BY_POOL;
+ }
+ bool empty() const override {
+ return by_pool.empty();
+ }
+ uint64_t count() const override {
+ return total;
+ }
+ void get(const hobject_t& o) override {
+ ++by_pool[o.pool];
+ ++total;
+ }
+ bool put(const hobject_t& o) override {
+ auto p = by_pool.find(o.pool);
+ if (p == by_pool.end()) {
+ return false;
+ }
+ --p->second;
+ if (p->second == 0) {
+ by_pool.erase(p);
+ }
+ --total;
+ return true;
+ }
+ void bound_encode(size_t& p) const {
+ p += 6 + sizeof(uint64_t) + by_pool.size() * (9 + 9);
+ }
+ DENC_HELPERS
+ void encode(::ceph::buffer::list::contiguous_appender& p) const {
+ DENC_START(1, 1, p);
+ denc_varint(total, p);
+ denc_varint(by_pool.size(), p);
+ for (auto& i : by_pool) {
+ denc_signed_varint(i.first, p);
+ denc_varint(i.second, p);
+ }
+ DENC_FINISH(p);
+ }
+ void decode(::ceph::buffer::ptr::const_iterator& p) {
+ DENC_START(1, 1, p);
+ denc_varint(total, p);
+ uint64_t n;
+ denc_varint(n, p);
+ while (n--) {
+ int64_t poolid;
+ uint64_t count;
+ denc_signed_varint(poolid, p);
+ denc_varint(count, p);
+ by_pool[poolid] = count;
+ }
+ DENC_FINISH(p);
+ }
+ void dump(Formatter *f) const override {
+ f->dump_string("type", "by_pool");
+ f->dump_unsigned("count", total);
+ f->open_array_section("pools");
+ for (auto& i : by_pool) {
+ f->open_object_section("pool");
+ f->dump_unsigned("pool_id", i.first);
+ f->dump_unsigned("count", i.second);
+ f->close_section();
+ }
+ f->close_section();
+ }
+};
+WRITE_CLASS_DENC(chunk_refs_by_pool_t)
+
+
+struct chunk_refs_count_t : public chunk_refs_t::refs_t {
+ uint64_t total = 0;
+
+ chunk_refs_count_t() {}
+ chunk_refs_count_t(const refs_t *old) {
+ total = old->count();
+ }
+
+ uint8_t get_type() const {
+ return chunk_refs_t::TYPE_COUNT;
+ }
+ bool empty() const override {
+ return total == 0;
+ }
+ uint64_t count() const override {
+ return total;
+ }
+ void get(const hobject_t& o) override {
+ ++total;
+ }
+ bool put(const hobject_t& o) override {
+ if (!total) {
+ return false;
+ }
+ --total;
+ return true;
+ }
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(total, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator& p) {
+ DECODE_START(1, p);
+ decode(total, p);
+ DECODE_FINISH(p);
+ }
+ void dump(Formatter *f) const override {
+ f->dump_string("type", "count");
+ f->dump_unsigned("count", total);
+ }
+};
+WRITE_CLASS_ENCODER(chunk_refs_count_t)
+
diff --git a/src/cls/cas/cls_cas_ops.h b/src/cls/cas/cls_cas_ops.h
new file mode 100644
index 000000000..a79013f0e
--- /dev/null
+++ b/src/cls/cas/cls_cas_ops.h
@@ -0,0 +1,101 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_CLS_CAS_OPS_H
+#define CEPH_CLS_CAS_OPS_H
+
+#include "include/types.h"
+#include "common/hobject.h"
+#include "common/Formatter.h"
+
+struct cls_cas_chunk_create_or_get_ref_op {
+ enum {
+ FLAG_VERIFY = 1, // verify content bit-for-bit if chunk already exists
+ };
+
+ hobject_t source;
+ uint64_t flags = 0;
+ bufferlist data;
+
+ cls_cas_chunk_create_or_get_ref_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(source, bl);
+ encode(flags, bl);
+ encode(data, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(source, bl);
+ decode(flags, bl);
+ decode(data, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(ceph::Formatter *f) const {
+ f->dump_object("source", source);
+ f->dump_unsigned("flags", flags);
+ f->dump_unsigned("data_len", data.length());
+ }
+ static void generate_test_instances(std::list<cls_cas_chunk_create_or_get_ref_op*>& ls) {
+ ls.push_back(new cls_cas_chunk_create_or_get_ref_op());
+ }
+};
+WRITE_CLASS_ENCODER(cls_cas_chunk_create_or_get_ref_op)
+
+
+struct cls_cas_chunk_get_ref_op {
+ hobject_t source;
+
+ cls_cas_chunk_get_ref_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(source, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(source, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(ceph::Formatter *f) const {
+ f->dump_object("source", source);
+ }
+ static void generate_test_instances(std::list<cls_cas_chunk_get_ref_op*>& ls) {
+ ls.push_back(new cls_cas_chunk_get_ref_op());
+ }
+};
+WRITE_CLASS_ENCODER(cls_cas_chunk_get_ref_op)
+
+
+struct cls_cas_chunk_put_ref_op {
+ hobject_t source;
+
+ cls_cas_chunk_put_ref_op() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(source, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(source, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter *f) const {
+ f->dump_object("source", source);
+ }
+ static void generate_test_instances(std::list<cls_cas_chunk_put_ref_op*>& ls) {
+ ls.push_back(new cls_cas_chunk_put_ref_op());
+ }
+};
+WRITE_CLASS_ENCODER(cls_cas_chunk_put_ref_op)
+
+#endif