From d958bcb73c64a11cd0c6e81d63945ec20bdf30b5 Mon Sep 17 00:00:00 2001 From: Patrick Donnelly Date: Mon, 26 Aug 2024 12:21:10 -0400 Subject: [PATCH] mds,include: add inode_t optional metadata Signed-off-by: Patrick Donnelly Fixes: https://tracker.ceph.com/issues/66373 --- src/include/cephfs/types.h | 235 ++++++++++++++++++++++++++++++++++++- src/include/encoding.h | 18 +++ src/mds/CInode.cc | 12 +- 3 files changed, 262 insertions(+), 3 deletions(-) diff --git a/src/include/cephfs/types.h b/src/include/cephfs/types.h index 435bc104d83f3..0527c7b010db6 100644 --- a/src/include/cephfs/types.h +++ b/src/include/cephfs/types.h @@ -383,6 +383,228 @@ enum { DAMAGE_FRAGTREE // fragtree -- repair by searching }; + +template class Allocator> +class unknown_md_t { +public: + void encode(ceph::buffer::list& bl, uint64_t features) const { + encode_nohead(payload, bl); + } + void decode(ceph::buffer::list::const_iterator& p) { + bufferlist bl; + DECODE_UNKNOWN(bl, p); + auto blp = bl.cbegin(); + blp.copy(blp.get_remaining(), payload); + } + + void print(std::ostream& os) const { + os << "unknown_md_t(len=" << payload.size() << ")"; + } + void dump(ceph::Formatter* f) const { + f->dump_bool("length", payload.length()); + } + +private: + std::vector> payload; +}; + +template class Allocator> +struct optmetadata_server_t { + using opts = std::variant< + unknown_md_t, + >; + enum kind_t : uint64_t { + UNKNOWN, + _MAX + }; +}; + +template class Allocator> +struct optmetadata_client_t { + using opts = std::variant< + unknown_md_t, + >; + enum kind_t : uint64_t { + UNKNOWN, + _MAX + }; +}; + +template +void defconstruct_type(std::variant& v, std::size_t i) +{ + constexpr auto N = sizeof...(Ts); + static const std::array, N> lookup = {Ts{}...}; + v = lookup[i]; +} + +template class Allocator> +struct optmetadata_singleton { + using optmetadata_t = typename M::opts; + using kind_t = typename M::kind_t; + + optmetadata_singleton(kind_t kind = kind_t::UNKNOWN) + { + u64kind = (uint64_t)kind; + defconstruct_type(optmetadata, get_kind()); + } + + auto get_kind() const { + constexpr auto optsmax = std::variant_size_v; + static_assert(kind_t::_MAX == optsmax); + static_assert(kind_t::UNKNOWN == 0); + if (u64kind > optsmax) { + return kind_t::UNKNOWN; + } else { + return (kind_t)u64kind; + } + } + template class > class T> + auto& get_meta() { + return std::get< T >(optmetadata); + } + template class > class T> + auto& get_meta() const { + return std::get< T >(optmetadata); + } + + void print(std::ostream& os) const { + os << "(k=" << u64kind << " m="; + std::visit([&os](auto& o) { o.print(os); }, optmetadata); + os << ")"; + } + void dump(ceph::Formatter* f) const { + f->dump_int("kind", u64kind); + f->dump_object("metadata", optmetadata); + } + + void encode(ceph::buffer::list& bl, uint64_t features) const { + // no versioning, use optmetadata + ceph::encode(u64kind, bl); + std::visit([&bl, features](auto& o) { o.encode(bl, features); }, optmetadata); + } + + void decode(ceph::buffer::list::const_iterator& p) { + ceph::decode(u64kind, p); + *this = optmetadata_singleton((kind_t)u64kind); + std::visit([&p](auto& o) { o.decode(p); }, optmetadata); + } + + bool operator<(const optmetadata_singleton& other) const { + return u64kind < other.u64kind; + } + +private: + uint64_t u64kind = 0; + optmetadata_t optmetadata; +}; + +template class Allocator> +struct optmetadata_multiton { + static constexpr int STRUCT_V = 1; + static constexpr int COMPAT_V = 1; + + using optkind_t = typename Singleton::kind_t; + using optvec_t = std::vector>; + + void encode(ceph::buffer::list& bl, uint64_t features) const { + // no versioning, use payload + ENCODE_START(STRUCT_V, COMPAT_V, bl); + ceph::encode(opts, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator& p) { + DECODE_START(STRUCT_V, p); + ceph::decode(opts, p); + DECODE_FINISH(p); + } + + void print(std::ostream& os) const { + os << "optm(len=" << opts.size() << " " << opts << ")"; + } + void dump(ceph::Formatter* f) const { + f->dump_bool("length", opts.size()); + f->open_array_section("opts"); + for (auto& opt : opts) { + f->dump_object("opt", opt); + } + f->dump_object("opts", opts); + } + + bool has_opt(optkind_t kind) const { + auto f = [kind](auto& o) { + return o.get_kind() == kind; + }; + auto it = std::find_if(opts.begin(), opts.end(), std::move(f)); + return it != opts.end(); + } + auto& get_opt(optkind_t kind) const { + auto f = [kind](auto& o) { + return o.get_kind() == kind; + }; + auto it = std::find_if(opts.begin(), opts.end(), std::move(f)); + return *it; + } + auto& get_opt(optkind_t kind) { + auto f = [kind](auto& o) { + return o.get_kind() == kind; + }; + auto it = std::find_if(opts.begin(), opts.end(), std::move(f)); + return *it; + } + auto& get_or_create_opt(optkind_t kind) { + auto f = [kind](auto& o) { + return o.get_kind() == kind; + }; + if (auto it = std::find_if(opts.begin(), opts.end(), std::move(f)); it != opts.end()) { + return *it; + } + auto it = std::lower_bound(opts.begin(), opts.end(), kind); + it = opts.emplace(it, kind); + return *it; + } + void del_opt(optkind_t kind) { + auto f = [kind](auto& o) { + return o.get_kind() == kind; + }; + auto it = std::remove_if(opts.begin(), opts.end(), std::move(f)); + opts.erase(it, opts.end()); + } + + auto size() const { + return opts.size(); + } + +private: + optvec_t opts; +}; + +template class Allocator> +static inline void encode(optmetadata_singleton const& o, ::ceph::buffer::list& bl, uint64_t features=0) +{ + ENCODE_DUMP_PRE(); + o.encode(bl, features); + ENCODE_DUMP_POST(cl); +} +template class Allocator> +static inline void decode(optmetadata_singleton& o, ::ceph::buffer::list::const_iterator& p) +{ + o.decode(p); +} + +template class Allocator> +static inline void encode(optmetadata_multiton const& o, ::ceph::buffer::list& bl, uint64_t features=0) +{ + ENCODE_DUMP_PRE(); + o.encode(bl, features); + ENCODE_DUMP_POST(cl); +} +template class Allocator> +static inline void decode(optmetadata_multiton& o, ::ceph::buffer::list::const_iterator& p) +{ + o.decode(p); +} + template class Allocator = std::allocator> struct inode_t { /** @@ -390,6 +612,7 @@ struct inode_t { * Do not forget to add any new fields to the compare() function. * *************** */ + using optmetadata_singleton_server_t = optmetadata_singleton,Allocator>; using client_range_map = std::map,Allocator>>; static const uint8_t F_EPHEMERAL_DISTRIBUTED_PIN = (1<<0); @@ -608,6 +831,8 @@ struct inode_t { std::vector> fscrypt_file; std::vector> fscrypt_last_block; + optmetadata_multiton optmetadata; + private: bool older_is_consistent(const inode_t &other) const; }; @@ -616,7 +841,7 @@ private: template class Allocator> void inode_t::encode(ceph::buffer::list &bl, uint64_t features) const { - ENCODE_START(19, 6, bl); + ENCODE_START(20, 6, bl); encode(ino, bl); encode(rdev, bl); @@ -676,6 +901,8 @@ void inode_t::encode(ceph::buffer::list &bl, uint64_t features) const encode(fscrypt_file, bl); encode(fscrypt_last_block, bl); + encode(optmetadata, bl, features); + ENCODE_FINISH(bl); } @@ -793,6 +1020,11 @@ void inode_t::decode(ceph::buffer::list::const_iterator &p) if (struct_v >= 19) { decode(fscrypt_last_block, p); } + + if (struct_v >= 20) { + decode(optmetadata, p); + } + DECODE_FINISH(p); } @@ -944,6 +1176,7 @@ void inode_t::generate_test_instances(std::list& ls) template class Allocator> int inode_t::compare(const inode_t &other, bool *divergent) const { + // TODO: fscrypt / optmetadata: https://tracker.ceph.com/issues/70188 ceph_assert(ino == other.ino); *divergent = false; if (version == other.version) { diff --git a/src/include/encoding.h b/src/include/encoding.h index d5fcf77d66030..f53a04837eceb 100644 --- a/src/include/encoding.h +++ b/src/include/encoding.h @@ -1507,6 +1507,24 @@ decode(std::array& v, bufferlist::const_iterator& p) unsigned struct_end = bl.get_off() + struct_len; \ do { +#define DECODE_UNKNOWN(payload, bl) \ + do { \ + __u8 struct_v, struct_compat; \ + using ::ceph::decode; \ + decode(struct_v, bl); \ + decode(struct_compat, bl); \ + __u32 struct_len; \ + decode(struct_len, bl); \ + if (struct_len > bl.get_remaining()) \ + throw ::ceph::buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); \ + payload.clear(); \ + using ::ceph::encode; \ + encode(struct_v, payload); \ + encode(struct_compat, payload); \ + encode(struct_len, payload); \ + bl.copy(struct_len, payload); \ + } while (0) + /* BEWARE: any change to this macro MUST be also reflected in the duplicative * DECODE_START_LEGACY_COMPAT_LEN! */ #define __DECODE_START_LEGACY_COMPAT_LEN(v, compatv, lenv, skip_v, bl) \ diff --git a/src/mds/CInode.cc b/src/mds/CInode.cc index ffaa1315fc226..977f4b0876fe0 100644 --- a/src/mds/CInode.cc +++ b/src/mds/CInode.cc @@ -2161,7 +2161,7 @@ void CInode::decode_lock_iflock(bufferlist::const_iterator& p) void CInode::encode_lock_ipolicy(bufferlist& bl) { - ENCODE_START(3, 1, bl); + ENCODE_START(4, 1, bl); if (is_dir()) { encode(get_inode()->version, bl); encode(get_inode()->ctime, bl); @@ -2170,8 +2170,10 @@ void CInode::encode_lock_ipolicy(bufferlist& bl) encode(get_inode()->export_pin, bl); encode(get_inode()->flags, bl); encode(get_inode()->export_ephemeral_random_pin, bl); + encode(get_inode()->optmetadata, bl); } else { encode(get_inode()->flags, bl); + encode(get_inode()->optmetadata, bl); } ENCODE_FINISH(bl); } @@ -2180,7 +2182,7 @@ void CInode::decode_lock_ipolicy(bufferlist::const_iterator& p) { ceph_assert(!is_auth()); auto _inode = allocate_inode(*get_inode()); - DECODE_START(3, p); + DECODE_START(4, p); if (is_dir()) { decode(_inode->version, p); utime_t tm; @@ -2194,10 +2196,16 @@ void CInode::decode_lock_ipolicy(bufferlist::const_iterator& p) decode(_inode->flags, p); decode(_inode->export_ephemeral_random_pin, p); } + if (struct_v >= 4) { + decode(_inode->optmetadata, p); + } } else { if (struct_v >= 3) { decode(_inode->flags, p); } + if (struct_v >= 4) { + decode(_inode->optmetadata, p); + } } DECODE_FINISH(p); -- 2.39.5