--- /dev/null
+#!/bin/sh -e
+
+GTEST_FILTER=${CLS_CAS_GTEST_FILTER:-*}
+ceph_test_cls_cas --gtest_filter=${GTEST_FILTER}
+
+exit 0
#include "objclass/objclass.h"
#include "cls_cas_ops.h"
+#include "cls_cas_internal.h"
#include "include/compat.h"
#include "osd/osd_types.h"
CLS_VER(1,0)
CLS_NAME(cas)
-struct chunk_obj_refcount;
-static int chunk_read_refcount(cls_method_context_t hctx, chunk_obj_refcount *objr)
+//
+// helpers
+//
+
+static int chunk_read_refcount(
+ cls_method_context_t hctx,
+ chunk_obj_refcount *objr)
{
bufferlist bl;
objr->refs.clear();
return 0;
}
-static int chunk_set_refcount(cls_method_context_t hctx, const struct chunk_obj_refcount& objr)
+static int chunk_set_refcount(
+ cls_method_context_t hctx,
+ const struct chunk_obj_refcount& objr)
{
bufferlist bl;
return 0;
}
-static int cls_rc_chunk_refcount_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+
+//
+// methods
+//
+
+static int chunk_create_or_get_ref(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
{
auto in_iter = in->cbegin();
- cls_chunk_refcount_get_op op;
+ cls_cas_chunk_create_or_get_ref_op op;
try {
decode(op, in_iter);
} catch (ceph::buffer::error& err) {
- CLS_LOG(1, "ERROR: cls_rc_refcount_get(): failed to decode entry\n");
+ CLS_LOG(1, "ERROR: failed to decode entry\n");
return -EINVAL;
}
chunk_obj_refcount objr;
int ret = chunk_read_refcount(hctx, &objr);
- if (ret < 0)
+ 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.refs.insert(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());
- CLS_LOG(10, "cls_rc_chunk_refcount_get() oid=%s\n", op.source.oid.name.c_str());
+ if (objr.refs.count(op.source)) {
+ return -EEXIST;
+ }
+ objr.refs.insert(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_obj_refcount 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());
+
+ if (objr.refs.count(op.source)) {
+ return -EEXIST;
+ }
objr.refs.insert(op.source);
ret = chunk_set_refcount(hctx, objr);
- if (ret < 0)
+ if (ret < 0) {
return ret;
-
+ }
return 0;
}
-static int cls_rc_chunk_refcount_put(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+static int chunk_put_ref(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
{
auto in_iter = in->cbegin();
- cls_chunk_refcount_put_op op;
+ cls_cas_chunk_put_ref_op op;
try {
decode(op, in_iter);
} catch (ceph::buffer::error& err) {
- CLS_LOG(1, "ERROR: cls_rc_chunk_refcount_put(): failed to decode entry\n");
+ CLS_LOG(1, "ERROR: failed to decode entry\n");
return -EINVAL;
}
return ret;
if (objr.refs.empty()) {// shouldn't happen!
- CLS_LOG(0, "ERROR: cls_rc_chunk_refcount_put() was called without any references!\n");
- return -EINVAL;
- }
-
- CLS_LOG(10, "cls_rc_chunk_refcount_put() oid=%s\n", op.source.oid.name.c_str());
-
- bool found = false;
- for (auto &p : objr.refs) {
- if (p == op.source) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- return 0;
+ CLS_LOG(0, "ERROR was called without any references!\n");
+ return -ENOLINK;
}
auto p = objr.refs.find(op.source);
+ if (p == objr.refs.end()) {
+ CLS_LOG(10, "oid=%s (no ref)\n", op.source.oid.name.c_str());
+ return -ENOLINK;
+ }
objr.refs.erase(p);
if (objr.refs.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 cls_rc_chunk_refcount_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+static int chunk_set_refs(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
{
auto in_iter = in->cbegin();
- cls_chunk_refcount_set_op op;
+ cls_cas_chunk_set_refs_op op;
try {
decode(op, in_iter);
} catch (ceph::buffer::error& err) {
- CLS_LOG(1, "ERROR: cls_chunk_refcount_set(): failed to decode entry\n");
+ CLS_LOG(1, "ERROR: cls_cas_chunk_set(): failed to decode entry\n");
return -EINVAL;
}
return 0;
}
-static int cls_rc_chunk_refcount_read(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+static int chunk_read_refs(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
{
chunk_obj_refcount objr;
- cls_chunk_refcount_read_ret read_ret;
+ cls_cas_chunk_read_refs_ret read_ret;
int ret = chunk_read_refcount(hctx, &objr);
if (ret < 0)
return ret;
return 0;
}
-static int cls_rc_write_or_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
-{
- auto in_iter = in->cbegin();
- hobject_t src_obj;
- bufferlist indata, outdata;
- ceph_osd_op op;
- try {
- decode (op, in_iter);
- decode(src_obj, in_iter);
- in_iter.copy(op.extent.length, indata);
- }
- catch (ceph::buffer::error& e) {
- return -EINVAL;
- }
-
- CLS_LOG(10, " offset: %llu length: %llu \n",
- static_cast<long long unsigned>(op.extent.offset),
- static_cast<long long unsigned>(op.extent.length));
- chunk_obj_refcount objr;
- int ret = chunk_read_refcount(hctx, &objr);
- if (ret == -ENOENT) {
- objr.refs.insert(src_obj);
- bufferlist set_bl;
- encode(objr, set_bl);
- ret = cls_cxx_chunk_write_and_set(hctx, op.extent.offset, op.extent.length, &indata, op.flags,
- &set_bl, set_bl.length());
- if (ret < 0)
- return ret;
-
- return 0;
- }
-
- objr.refs.insert(src_obj);
- ret = chunk_set_refcount(hctx, objr);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-
-static int cls_rc_has_chunk(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+static int references_chunk(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
{
auto in_iter = in->cbegin();
std::string fp_oid;
catch (ceph::buffer::error& e) {
return -EINVAL;
}
- CLS_LOG(10, " fp_oid: %s \n", fp_oid.c_str());
+ CLS_LOG(10, "fp_oid: %s \n", fp_oid.c_str());
bool ret = cls_has_chunk(hctx, fp_oid);
if (ret) {
CLS_LOG(1, "Loaded cas class!");
cls_handle_t h_class;
- cls_method_handle_t h_cas_write_or_get;
- cls_method_handle_t h_chunk_refcount_get;
- cls_method_handle_t h_chunk_refcount_put;
- cls_method_handle_t h_chunk_refcount_set;
- cls_method_handle_t h_chunk_refcount_read;
- cls_method_handle_t h_chunk_has_chunk;
+ 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_chunk_set_refs;
+ cls_method_handle_t h_chunk_read_refs;
+ cls_method_handle_t h_references_chunk;
cls_register("cas", &h_class);
- /* chunk refcount */
- cls_register_cxx_method(h_class, "chunk_get", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_chunk_refcount_get,
- &h_chunk_refcount_get);
- cls_register_cxx_method(h_class, "chunk_put", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_chunk_refcount_put,
- &h_chunk_refcount_put);
- cls_register_cxx_method(h_class, "chunk_set", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_chunk_refcount_set,
- &h_chunk_refcount_set);
- cls_register_cxx_method(h_class, "chunk_read", CLS_METHOD_RD, cls_rc_chunk_refcount_read,
- &h_chunk_refcount_read);
- cls_register_cxx_method(h_class, "cas_write_or_get", CLS_METHOD_RD | CLS_METHOD_WR, cls_rc_write_or_get,
- &h_cas_write_or_get);
- cls_register_cxx_method(h_class, "has_chunk", CLS_METHOD_RD, cls_rc_has_chunk,
- &h_chunk_has_chunk);
+ 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, "chunk_set_refs",
+ CLS_METHOD_RD | CLS_METHOD_WR,
+ chunk_set_refs,
+ &h_chunk_set_refs);
+ cls_register_cxx_method(h_class, "chunk_read_refs", CLS_METHOD_RD,
+ chunk_read_refs,
+ &h_chunk_read_refs);
+ cls_register_cxx_method(h_class, "references_chunk", CLS_METHOD_RD,
+ references_chunk,
+ &h_references_chunk);
return;
}
using ceph::decode;
using ceph::encode;
-void cls_chunk_refcount_get(librados::ObjectWriteOperation& op, const hobject_t& soid)
+void cls_cas_chunk_create_or_get_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid,
+ const bufferlist& data,
+ bool verify)
{
bufferlist in;
- cls_chunk_refcount_get_op call;
+ 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", in);
+ op.exec("cas", "chunk_get_ref", in);
}
-void cls_chunk_refcount_put(librados::ObjectWriteOperation& op, const hobject_t& soid)
+void cls_cas_chunk_put_ref(
+ librados::ObjectWriteOperation& op,
+ const hobject_t& soid)
{
bufferlist in;
- cls_chunk_refcount_put_op call;
+ cls_cas_chunk_put_ref_op call;
call.source = soid;
encode(call, in);
- op.exec("cas", "chunk_put", in);
+ op.exec("cas", "chunk_put_ref", in);
}
-void cls_chunk_refcount_set(librados::ObjectWriteOperation& op, set<hobject_t>& refs)
+void cls_cas_chunk_set_refs(
+ librados::ObjectWriteOperation& op,
+ set<hobject_t>& refs)
{
bufferlist in;
- cls_chunk_refcount_set_op call;
+ cls_cas_chunk_set_refs_op call;
call.refs = refs;
encode(call, in);
- op.exec("cas", "chunk_set", in);
+ op.exec("cas", "chunk_set_refs", in);
}
-int cls_chunk_refcount_read(librados::IoCtx& io_ctx, string& oid, set<hobject_t> *refs)
+int cls_cas_chunk_read_refs(
+ librados::IoCtx& io_ctx,
+ string& oid,
+ set<hobject_t> *refs)
{
bufferlist in, out;
- int r = io_ctx.exec(oid, "cas", "chunk_read", in, out);
+ int r = io_ctx.exec(oid, "cas", "chunk_read_refs", in, out);
if (r < 0)
return r;
- cls_chunk_refcount_read_ret ret;
+ cls_cas_chunk_read_refs_ret ret;
try {
auto iter = out.cbegin();
decode(ret, iter);
return r;
}
-int cls_chunk_has_chunk(librados::IoCtx& io_ctx, string& oid, string& fp_oid)
+int cls_cas_references_chunk(
+ librados::IoCtx& io_ctx,
+ const string& oid,
+ const string& chunk_oid)
{
bufferlist in, out;
- encode(fp_oid, in);
- int r = io_ctx.exec(oid, "cas", "has_chunk", in, out);
+ encode(chunk_oid, in);
+ int r = io_ctx.exec(oid, "cas", "references_chunk", in, out);
return r;
}
#include "include/rados/librados_fwd.hpp"
#include "common/hobject.h"
-void cls_chunk_refcount_get(librados::ObjectWriteOperation& op,const hobject_t& soid);
-void cls_chunk_refcount_put(librados::ObjectWriteOperation& op, const hobject_t& soid);
-void cls_chunk_refcount_set(librados::ObjectWriteOperation& op, std::set<hobject_t>& refs);
-int cls_chunk_refcount_read(librados::IoCtx& io_ctx, std::string& oid, std::set<hobject_t> *refs);
-int cls_chunk_has_chunk(librados::IoCtx& io_ctx, std::string& oid, std::string& fp_oid);
+//
+// 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.)
+//
+
+/// read list of all chunk references
+int cls_cas_chunk_read_refs(
+ librados::IoCtx& io_ctx,
+ std::string& oid,
+ std::set<hobject_t> *refs);
+
+/// force update on chunk references
+void cls_cas_chunk_set_refs(
+ librados::ObjectWriteOperation& op,
+ std::set<hobject_t>& refs);
+
+/// 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
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "common/Formatter.h"
+
+#define CHUNK_REFCOUNT_ATTR "chunk_refcount"
+
+struct chunk_obj_refcount {
+ std::set<hobject_t> refs;
+
+ chunk_obj_refcount() {}
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(refs, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(refs, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const {
+ f->open_array_section("refs");
+ for (auto& i : refs) {
+ f->dump_object("ref", i);
+ }
+ f->close_section();
+ }
+ static void generate_test_instances(std::list<chunk_obj_refcount*>& ls) {
+ ls.push_back(new chunk_obj_refcount());
+ }
+};
+WRITE_CLASS_ENCODER(chunk_obj_refcount)
#include "include/types.h"
#include "common/hobject.h"
+#include "common/Formatter.h"
-#define CHUNK_REFCOUNT_ATTR "chunk_refcount"
+struct cls_cas_chunk_create_or_get_ref_op {
+ enum {
+ FLAG_VERIFY = 1, // verify content bit-for-bit if chunk already exists
+ };
-struct cls_chunk_refcount_get_op {
hobject_t source;
+ uint64_t flags = 0;
+ bufferlist data;
- cls_chunk_refcount_get_op() {}
+ 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;
- static void generate_test_instances(std::list<cls_chunk_refcount_get_op*>& ls);
+ 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_chunk_refcount_get_op)
+WRITE_CLASS_ENCODER(cls_cas_chunk_create_or_get_ref_op)
+
-struct cls_chunk_refcount_put_op {
+struct cls_cas_chunk_get_ref_op {
hobject_t source;
- cls_chunk_refcount_put_op() {}
+ cls_cas_chunk_get_ref_op() {}
void encode(ceph::buffer::list& bl) const {
ENCODE_START(1, 1, bl);
decode(source, bl);
DECODE_FINISH(bl);
}
-
- void dump(ceph::Formatter *f) const;
- static void generate_test_instances(std::list<cls_chunk_refcount_put_op*>& ls);
+ 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_chunk_refcount_put_op)
+WRITE_CLASS_ENCODER(cls_cas_chunk_get_ref_op)
-struct cls_chunk_refcount_set_op {
- std::set<hobject_t> refs;
- cls_chunk_refcount_set_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(refs, bl);
+ encode(source, bl);
ENCODE_FINISH(bl);
}
void decode(ceph::buffer::list::const_iterator& bl) {
DECODE_START(1, bl);
- decode(refs, bl);
+ decode(source, bl);
DECODE_FINISH(bl);
}
- void dump(ceph::Formatter *f) const;
- static void generate_test_instances(std::list<cls_chunk_refcount_set_op*>& ls);
+ 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_chunk_refcount_set_op)
+WRITE_CLASS_ENCODER(cls_cas_chunk_put_ref_op)
+
-struct cls_chunk_refcount_read_ret {
+struct cls_cas_chunk_set_refs_op {
std::set<hobject_t> refs;
- cls_chunk_refcount_read_ret() {}
+ cls_cas_chunk_set_refs_op() {}
void encode(ceph::buffer::list& bl) const {
ENCODE_START(1, 1, bl);
DECODE_FINISH(bl);
}
- void dump(ceph::Formatter *f) const;
- static void generate_test_instances(std::list<cls_chunk_refcount_read_ret*>& ls);
+ void dump(ceph::Formatter *f) const {
+ f->open_array_section("refs");
+ for (auto& i : refs) {
+ f->dump_object("ref", i);
+ }
+ f->close_section();
+ }
+ static void generate_test_instances(std::list<cls_cas_chunk_set_refs_op*>& ls) {
+ ls.push_back(new cls_cas_chunk_set_refs_op());
+ }
};
-WRITE_CLASS_ENCODER(cls_chunk_refcount_read_ret)
+WRITE_CLASS_ENCODER(cls_cas_chunk_set_refs_op)
+
-struct chunk_obj_refcount {
+struct cls_cas_chunk_read_refs_ret {
std::set<hobject_t> refs;
- chunk_obj_refcount() {}
+ cls_cas_chunk_read_refs_ret() {}
void encode(ceph::buffer::list& bl) const {
ENCODE_START(1, 1, bl);
decode(refs, bl);
DECODE_FINISH(bl);
}
+
+ void dump(ceph::Formatter *f) const {
+ f->open_array_section("refs");
+ for (auto& i : refs) {
+ f->dump_object("ref", i);
+ }
+ f->close_section();
+ }
+ static void generate_test_instances(std::list<cls_cas_chunk_read_refs_ret*>& ls) {
+ ls.push_back(new cls_cas_chunk_read_refs_ret());
+ }
};
-WRITE_CLASS_ENCODER(chunk_obj_refcount)
+WRITE_CLASS_ENCODER(cls_cas_chunk_read_refs_ret)
#endif
#include "Session.h"
#include "objclass/objclass.h"
+#include "cls/cas/cls_cas_ops.h"
#include "common/ceph_crypto.h"
#include "common/errno.h"
#include "common/scrub_types.h"
if (fp_oid != tgt_soid.oid) {
// decrement old chunk's reference count
ObjectOperation dec_op;
- cls_chunk_refcount_put_op put_call;
+ cls_cas_chunk_put_ref_op put_call;
put_call.source = soid;
::encode(put_call, in);
- dec_op.call("cas", "chunk_put", in);
+ dec_op.call("cas", "chunk_put_ref", in);
// we don't care dec_op's completion. scrub for dedup will fix this.
tid = osd->objecter->mutate(
tgt_soid.oid, oloc, dec_op, snapc,
}
tgt_soid.oid = fp_oid;
iter->second.oid = tgt_soid;
- // add data op
- ceph_osd_op osd_op;
- osd_op.extent.offset = 0;
- osd_op.extent.length = chunk_data.length();
- encode(osd_op, in);
- encode(soid, in);
- in.append(chunk_data);
- obj_op.call("cas", "cas_write_or_get", in);
+ {
+ bufferlist t;
+ cls_cas_chunk_create_or_get_ref_op get_call;
+ get_call.source = soid;
+ get_call.data = chunk_data;
+ ::encode(get_call, t);
+ obj_op.call("cas", "chunk_create_or_get_ref", t);
+ }
} else {
obj_op.add_data(CEPH_OSD_OP_WRITE, tgt_offset, tgt_length, chunk_data);
}
ObjectOperation obj_op;
bufferlist in;
if (get) {
- cls_chunk_refcount_get_op call;
+ cls_cas_chunk_get_ref_op call;
call.source = obc->obs.oi.soid;
::encode(call, in);
- obj_op.call("cas", "chunk_get", in);
+ obj_op.call("cas", "chunk_get_ref", in);
} else {
- cls_chunk_refcount_put_op call;
+ cls_cas_chunk_put_ref_op call;
call.source = obc->obs.oi.soid;
::encode(call, in);
- obj_op.call("cas", "chunk_put", in);
+ obj_op.call("cas", "chunk_put_ref", in);
}
Context *c = nullptr;
add_subdirectory(cls_hello)
add_subdirectory(cls_lock)
+add_subdirectory(cls_cas)
add_subdirectory(cls_log)
add_subdirectory(cls_numops)
add_subdirectory(cls_sdk)
--- /dev/null
+add_executable(ceph_test_cls_cas
+ test_cls_cas.cc)
+target_link_libraries(ceph_test_cls_cas
+ librados
+ cls_cas_client
+ global
+ radostest-cxx
+ ${UNITTEST_LIBS}
+ ${BLKID_LIBRARIES}
+ ${CMAKE_DL_LIBS}
+ ${CRYPTO_LIBS}
+ ${EXTRALIBS}
+ )
+install(TARGETS
+ ceph_test_cls_cas
+ DESTINATION ${CMAKE_INSTALL_BINDIR})
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/types.h"
+#include "cls/cas/cls_cas_client.h"
+#include "cls/cas/cls_cas_internal.h"
+
+#include "include/utime.h"
+#include "common/Clock.h"
+#include "global/global_context.h"
+
+#include "gtest/gtest.h"
+#include "test/librados/test_cxx.h"
+
+#include <errno.h>
+#include <string>
+#include <vector>
+
+
+/// creates a temporary pool and initializes an IoCtx for each test
+class cls_cas : public ::testing::Test {
+ librados::Rados rados;
+ std::string pool_name;
+ protected:
+ librados::IoCtx ioctx;
+
+ void SetUp() {
+ pool_name = get_temp_pool_name();
+ /* create pool */
+ ASSERT_EQ("", create_one_pool_pp(pool_name, rados));
+ ASSERT_EQ(0, rados.ioctx_create(pool_name.c_str(), ioctx));
+ }
+ void TearDown() {
+ /* remove pool */
+ ioctx.close();
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, rados));
+ }
+};
+
+static librados::ObjectWriteOperation *new_op() {
+ return new librados::ObjectWriteOperation();
+}
+
+static librados::ObjectReadOperation *new_rop() {
+ return new librados::ObjectReadOperation();
+}
+
+TEST_F(cls_cas, get_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2, ref3;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+ ref3.oid.name = "foo3";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get x3
+ {
+ auto op = new_op();
+ cls_cas_chunk_get_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get again
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref3, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // 3x puts to remove
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref3);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+
+ // get
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+TEST_F(cls_cas, wrong_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put wrong thing
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(-ENOLINK, ioctx.operate(oid, op));
+ }
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+TEST_F(cls_cas, dup_get)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1;
+ ref1.oid.name = "foo1";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // dup create_or_get_ref, get_ref
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(-EEXIST, ioctx.operate(oid, op));
+ }
+ {
+ auto op = new_op();
+ cls_cas_chunk_get_ref(*op, ref1);
+ ASSERT_EQ(-EEXIST, ioctx.operate(oid, op));
+ }
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
+
+TEST_F(cls_cas, dup_put)
+{
+ bufferlist bl;
+ bl.append("my data");
+ string oid = "mychunk";
+ hobject_t ref1;
+ ref1.oid.name = "foo1";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // write
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, op));
+ }
+}
+
+
+TEST_F(cls_cas, get_wrong_data)
+{
+ bufferlist bl, bl2;
+ bl.append("my data");
+ bl2.append("my different data");
+ string oid = "mychunk";
+ hobject_t ref1, ref2;
+ ref1.oid.name = "foo1";
+ ref2.oid.name = "foo2";
+
+ // initially the object does not exist
+ bufferlist t;
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+
+ // get
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref1, bl);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // get w/ different data
+ // with verify
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref2, bl2, true);
+ ASSERT_EQ(-ENOMSG, ioctx.operate(oid, op));
+ }
+ // without verify
+ {
+ auto op = new_op();
+ cls_cas_chunk_create_or_get_ref(*op, ref2, bl2, false);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+
+ // put
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref1);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(bl.length(), ioctx.read(oid, t, 0, 0));
+ {
+ auto op = new_op();
+ cls_cas_chunk_put_ref(*op, ref2);
+ ASSERT_EQ(0, ioctx.operate(oid, op));
+ }
+ ASSERT_EQ(-ENOENT, ioctx.read(oid, t, 0, 0));
+}
// redirect's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
- cls_chunk_refcount_read_ret read_ret;
+ cache_ioctx.exec("bar", "cas", "chunk_read_refs", in, out);
+ cls_cas_chunk_read_refs_ret read_ret;
try {
auto iter = out.cbegin();
decode(read_ret, iter);
// chunk's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
- cls_chunk_refcount_read_ret read_ret;
+ cache_ioctx.exec("bar-chunk", "cas", "chunk_read_refs", in, out);
+ cls_cas_chunk_read_refs_ret read_ret;
try {
auto iter = out.cbegin();
decode(read_ret, iter);
// redirect's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
- cls_chunk_refcount_read_ret read_ret;
+ cache_ioctx.exec("bar", "cas", "chunk_read_refs", in, out);
+ cls_cas_chunk_read_refs_ret read_ret;
try {
auto iter = out.cbegin();
decode(read_ret, iter);
// chunk's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
- cls_chunk_refcount_read_ret read_ret;
+ cache_ioctx.exec("bar-chunk", "cas", "chunk_read_refs", in, out);
+ cls_cas_chunk_read_refs_ret read_ret;
try {
auto iter = out.cbegin();
decode(read_ret, iter);
// redirect's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar", "cas", "chunk_read", in, out);
+ cache_ioctx.exec("bar", "cas", "chunk_read_refs", in, out);
if (out.length() != 0U) {
ObjectWriteOperation op;
op.unset_manifest();
// chunk's refcount
{
bufferlist in, out;
- cache_ioctx.exec("bar-chunk", "cas", "chunk_read", in, out);
+ cache_ioctx.exec("bar-chunk", "cas", "chunk_read_refs", in, out);
if (out.length() != 0U) {
ObjectWriteOperation op;
op.unset_manifest();
sha1_gen.Update((const unsigned char *)"There hi", size);
sha1_gen.Final(fingerprint);
buf_to_hex(fingerprint, CEPH_CRYPTO_SHA1_DIGESTSIZE, p_str);
- cache_ioctx.exec(p_str, "cas", "chunk_read", in, out);
- cls_chunk_refcount_read_ret read_ret;
+ cache_ioctx.exec(p_str, "cas", "chunk_read_refs", in, out);
+ cls_cas_chunk_read_refs_ret read_ret;
try {
auto iter = out.cbegin();
decode(read_ret, iter);
#include "crush/CrushWrapper.h"
TYPE_FEATUREFUL_NOCOPY(CrushWrapper)
+#include "cls/cas/cls_cas_ops.h"
+TYPE(cls_cas_chunk_create_or_get_ref_op)
+TYPE(cls_cas_chunk_get_ref_op)
+TYPE(cls_cas_chunk_put_ref_op)
+TYPE(cls_cas_chunk_set_refs_op)
+TYPE(cls_cas_chunk_read_refs_ret)
+
+#include "cls/cas/cls_cas_internal.h"
+TYPE(chunk_obj_refcount)
+
#include "cls/lock/cls_lock_types.h"
TYPE(rados::cls::lock::locker_id_t)
TYPE_FEATUREFUL(rados::cls::lock::locker_info_t)
auto oid = i.oid;
set<hobject_t> refs;
set<hobject_t> real_refs;
- ret = cls_chunk_refcount_read(chunk_io_ctx, oid, &refs);
+ ret = cls_cas_chunk_read_refs(chunk_io_ctx, oid, &refs);
if (ret < 0) {
continue;
}
continue;
}
- ret = cls_chunk_has_chunk(target_io_ctx, pp.oid.name, oid);
+ ret = cls_cas_references_chunk(target_io_ctx, pp.oid.name, oid);
if (ret != -ENOENT) {
real_refs.insert(pp);
}
if (refs.size() != real_refs.size()) {
ObjectWriteOperation op;
- cls_chunk_refcount_set(op, real_refs);
+ cls_cas_chunk_set_refs(op, real_refs);
ret = chunk_io_ctx.operate(oid, &op);
if (ret < 0) {
continue;
}
set<hobject_t> refs;
- ret = cls_chunk_refcount_read(chunk_io_ctx, object_name, &refs);
+ ret = cls_cas_chunk_read_refs(chunk_io_ctx, object_name, &refs);
if (ret < 0) {
- cerr << " cls_chunk_refcount_read fail : " << cpp_strerror(ret) << std::endl;
+ cerr << " cls_cas_chunk_read fail : " << cpp_strerror(ret) << std::endl;
return ret;
}
for (auto p : refs) {
refs.insert(oid);
ObjectWriteOperation op;
- cls_chunk_refcount_set(op, refs);
+ cls_cas_chunk_set_refs(op, refs);
ret = chunk_io_ctx.operate(object_name, &op);
if (ret < 0) {
cerr << " operate fail : " << cpp_strerror(ret) << std::endl;
}
set<hobject_t> refs;
cout << " refs: " << std::endl;
- ret = cls_chunk_refcount_read(chunk_io_ctx, object_name, &refs);
+ ret = cls_cas_chunk_read_refs(chunk_io_ctx, object_name, &refs);
for (auto p : refs) {
cout << " " << p.oid.name << " ";
}