From: Sage Weil Date: Mon, 18 May 2020 15:21:12 +0000 (-0500) Subject: cls_cas: renames, cleanups X-Git-Tag: v17.0.0~2135^2~19 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=f80b23e5e8f150a801bc1c9be993cb9555c778e7;p=ceph.git cls_cas: renames, cleanups - add write_or_get method - fix PrimaryPG caller ot use write_or_get - remove old method it previously called that did weird things - cls_chunk_refcount_* -> cls_cas_chunk_* - add _ref suffix for get and put to avoid confusion (get/put could mean read/write) - some comments - move (internal) refcount representation into separate header Signed-off-by: Sage Weil --- diff --git a/qa/workunits/cls/test_cls_cas.sh b/qa/workunits/cls/test_cls_cas.sh new file mode 100644 index 0000000000000..76591348222cf --- /dev/null +++ b/qa/workunits/cls/test_cls_cas.sh @@ -0,0 +1,6 @@ +#!/bin/sh -e + +GTEST_FILTER=${CLS_CAS_GTEST_FILTER:-*} +ceph_test_cls_cas --gtest_filter=${GTEST_FILTER} + +exit 0 diff --git a/src/cls/cas/cls_cas.cc b/src/cls/cas/cls_cas.cc index d5e5f0e28b856..36bf7e8bafe58 100644 --- a/src/cls/cas/cls_cas.cc +++ b/src/cls/cas/cls_cas.cc @@ -5,6 +5,7 @@ #include "objclass/objclass.h" #include "cls_cas_ops.h" +#include "cls_cas_internal.h" #include "include/compat.h" #include "osd/osd_types.h" @@ -15,9 +16,14 @@ using ceph::decode; 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(); @@ -39,7 +45,9 @@ static int chunk_read_refcount(cls_method_context_t hctx, chunk_obj_refcount *ob 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; @@ -52,43 +60,111 @@ static int chunk_set_refcount(cls_method_context_t hctx, const struct chunk_obj_ 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; } @@ -98,31 +174,23 @@ static int cls_rc_chunk_refcount_put(cls_method_context_t hctx, bufferlist *in, 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; @@ -130,15 +198,16 @@ static int cls_rc_chunk_refcount_put(cls_method_context_t hctx, bufferlist *in, 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; } @@ -156,11 +225,12 @@ static int cls_rc_chunk_refcount_set(cls_method_context_t hctx, bufferlist *in, 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; @@ -174,48 +244,8 @@ static int cls_rc_chunk_refcount_read(cls_method_context_t hctx, bufferlist *in, 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(op.extent.offset), - static_cast(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; @@ -226,7 +256,7 @@ static int cls_rc_has_chunk(cls_method_context_t hctx, bufferlist *in, bufferlis 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) { @@ -240,28 +270,37 @@ CLS_INIT(cas) 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; } diff --git a/src/cls/cas/cls_cas_client.cc b/src/cls/cas/cls_cas_client.cc index 8ccc445aff68b..7492f62a39a8f 100644 --- a/src/cls/cas/cls_cas_client.cc +++ b/src/cls/cas/cls_cas_client.cc @@ -14,41 +14,67 @@ using ceph::bufferlist; 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& refs) +void cls_cas_chunk_set_refs( + librados::ObjectWriteOperation& op, + set& 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 *refs) +int cls_cas_chunk_read_refs( + librados::IoCtx& io_ctx, + string& oid, + set *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); @@ -61,10 +87,13 @@ int cls_chunk_refcount_read(librados::IoCtx& io_ctx, string& oid, set 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; } diff --git a/src/cls/cas/cls_cas_client.h b/src/cls/cas/cls_cas_client.h index af64f342c015d..1017edfb0d2ab 100644 --- a/src/cls/cas/cls_cas_client.h +++ b/src/cls/cas/cls_cas_client.h @@ -8,9 +8,47 @@ #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& refs); -int cls_chunk_refcount_read(librados::IoCtx& io_ctx, std::string& oid, std::set *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 *refs); + +/// force update on chunk references +void cls_cas_chunk_set_refs( + librados::ObjectWriteOperation& op, + std::set& 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 diff --git a/src/cls/cas/cls_cas_internal.h b/src/cls/cas/cls_cas_internal.h new file mode 100644 index 0000000000000..3bcb856f99aa9 --- /dev/null +++ b/src/cls/cas/cls_cas_internal.h @@ -0,0 +1,38 @@ +// -*- 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 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& ls) { + ls.push_back(new chunk_obj_refcount()); + } +}; +WRITE_CLASS_ENCODER(chunk_obj_refcount) diff --git a/src/cls/cas/cls_cas_ops.h b/src/cls/cas/cls_cas_ops.h index cfd9a77301af3..4d3ec147c744c 100644 --- a/src/cls/cas/cls_cas_ops.h +++ b/src/cls/cas/cls_cas_ops.h @@ -6,34 +6,50 @@ #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& 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& 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); @@ -46,38 +62,47 @@ struct cls_chunk_refcount_put_op { decode(source, bl); DECODE_FINISH(bl); } - - void dump(ceph::Formatter *f) const; - static void generate_test_instances(std::list& ls); + void dump(ceph::Formatter *f) const { + f->dump_object("source", source); + } + static void generate_test_instances(std::list& 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 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& ls); + void dump(ceph::Formatter *f) const { + f->dump_object("source", source); + } + static void generate_test_instances(std::list& 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 refs; - cls_chunk_refcount_read_ret() {} + cls_cas_chunk_set_refs_op() {} void encode(ceph::buffer::list& bl) const { ENCODE_START(1, 1, bl); @@ -91,15 +116,24 @@ struct cls_chunk_refcount_read_ret { DECODE_FINISH(bl); } - void dump(ceph::Formatter *f) const; - static void generate_test_instances(std::list& 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& 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 refs; - chunk_obj_refcount() {} + cls_cas_chunk_read_refs_ret() {} void encode(ceph::buffer::list& bl) const { ENCODE_START(1, 1, bl); @@ -112,7 +146,18 @@ struct chunk_obj_refcount { 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& 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 diff --git a/src/osd/PrimaryLogPG.cc b/src/osd/PrimaryLogPG.cc index 23197dd8eed88..70c97c4be561b 100644 --- a/src/osd/PrimaryLogPG.cc +++ b/src/osd/PrimaryLogPG.cc @@ -25,6 +25,7 @@ #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" @@ -2589,10 +2590,10 @@ int PrimaryLogPG::do_manifest_flush(OpRequestRef op, ObjectContextRef obc, Flush 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, @@ -2602,14 +2603,14 @@ int PrimaryLogPG::do_manifest_flush(OpRequestRef op, ObjectContextRef obc, Flush } 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); } @@ -3467,15 +3468,15 @@ void PrimaryLogPG::refcount_manifest(ObjectContextRef obc, object_locator_t oloc 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; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 25a2f2436c41d..3bef8548b4f90 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -9,6 +9,7 @@ target_include_directories(unit-main PRIVATE add_subdirectory(cls_hello) add_subdirectory(cls_lock) +add_subdirectory(cls_cas) add_subdirectory(cls_log) add_subdirectory(cls_numops) add_subdirectory(cls_sdk) diff --git a/src/test/cls_cas/CMakeLists.txt b/src/test/cls_cas/CMakeLists.txt new file mode 100644 index 0000000000000..83426bc6068c7 --- /dev/null +++ b/src/test/cls_cas/CMakeLists.txt @@ -0,0 +1,16 @@ +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}) diff --git a/src/test/cls_cas/test_cls_cas.cc b/src/test/cls_cas/test_cls_cas.cc new file mode 100644 index 0000000000000..90aa207d2abd8 --- /dev/null +++ b/src/test/cls_cas/test_cls_cas.cc @@ -0,0 +1,285 @@ +// -*- 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 +#include +#include + + +/// 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)); +} diff --git a/src/test/librados/tier_cxx.cc b/src/test/librados/tier_cxx.cc index 42c03c278e92e..e5df5b70d77ce 100644 --- a/src/test/librados/tier_cxx.cc +++ b/src/test/librados/tier_cxx.cc @@ -3214,8 +3214,8 @@ TEST_F(LibRadosTwoPoolsPP, ManifestRefRead) { // 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); @@ -3227,8 +3227,8 @@ TEST_F(LibRadosTwoPoolsPP, ManifestRefRead) { // 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); @@ -3304,8 +3304,8 @@ TEST_F(LibRadosTwoPoolsPP, ManifestUnset) { // 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); @@ -3317,8 +3317,8 @@ TEST_F(LibRadosTwoPoolsPP, ManifestUnset) { // 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); @@ -3352,7 +3352,7 @@ TEST_F(LibRadosTwoPoolsPP, ManifestUnset) { // 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(); @@ -3366,7 +3366,7 @@ TEST_F(LibRadosTwoPoolsPP, ManifestUnset) { // 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(); @@ -3497,8 +3497,8 @@ TEST_F(LibRadosTwoPoolsPP, ManifestDedupRefRead) { 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); diff --git a/src/tools/ceph-dencoder/common_types.h b/src/tools/ceph-dencoder/common_types.h index 3cf9e2d18bb3e..ed6ffc6209f22 100644 --- a/src/tools/ceph-dencoder/common_types.h +++ b/src/tools/ceph-dencoder/common_types.h @@ -61,6 +61,16 @@ TYPE_FEATUREFUL(entity_inst_t) #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) diff --git a/src/tools/ceph_dedup_tool.cc b/src/tools/ceph_dedup_tool.cc index 48d468b157ed6..bef1a5eb1bc7e 100644 --- a/src/tools/ceph_dedup_tool.cc +++ b/src/tools/ceph_dedup_tool.cc @@ -430,7 +430,7 @@ void ChunkScrub::chunk_scrub_common() auto oid = i.oid; set refs; set 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; } @@ -445,7 +445,7 @@ void ChunkScrub::chunk_scrub_common() 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); } @@ -453,7 +453,7 @@ void ChunkScrub::chunk_scrub_common() 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; @@ -778,9 +778,9 @@ int chunk_scrub_common(const std::map < std::string, std::string > &opts, } set 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) { @@ -796,7 +796,7 @@ int chunk_scrub_common(const std::map < std::string, std::string > &opts, 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; @@ -814,7 +814,7 @@ int chunk_scrub_common(const std::map < std::string, std::string > &opts, } set 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 << " "; }