diff options
Diffstat (limited to 'src/include/CompatSet.h')
-rw-r--r-- | src/include/CompatSet.h | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/include/CompatSet.h b/src/include/CompatSet.h new file mode 100644 index 000000000..35c7a7738 --- /dev/null +++ b/src/include/CompatSet.h @@ -0,0 +1,285 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2009 Sage Weil <sage@newdream.net> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef CEPH_COMPATSET_H +#define CEPH_COMPATSET_H + +#include <iostream> +#include <map> +#include <string> + +#include "include/buffer.h" +#include "include/encoding.h" +#include "include/types.h" +#include "common/Formatter.h" + +struct CompatSet { + + struct Feature { + uint64_t id; + std::string name; + + Feature(uint64_t _id, const std::string& _name) : id(_id), name(_name) {} + }; + + class FeatureSet { + uint64_t mask; + std::map<uint64_t, std::string> names; + + public: + friend struct CompatSet; + friend class CephCompatSet_AllSet_Test; + friend class CephCompatSet_other_Test; + friend class CephCompatSet_merge_Test; + friend std::ostream& operator<<(std::ostream& out, const CompatSet::FeatureSet& fs); + friend std::ostream& operator<<(std::ostream& out, const CompatSet& compat); + FeatureSet() : mask(1), names() {} + void insert(const Feature& f) { + ceph_assert(f.id > 0); + ceph_assert(f.id < 64); + mask |= ((uint64_t)1<<f.id); + names[f.id] = f.name; + } + + bool contains(const Feature& f) const { + return names.count(f.id); + } + bool contains(uint64_t f) const { + return names.count(f); + } + /** + * Getter instead of using name[] to be const safe + */ + std::string get_name(uint64_t const f) const { + std::map<uint64_t, std::string>::const_iterator i = names.find(f); + ceph_assert(i != names.end()); + return i->second; + } + + void remove(uint64_t f) { + if (names.count(f)) { + names.erase(f); + mask &= ~((uint64_t)1<<f); + } + } + void remove(const Feature& f) { + remove(f.id); + } + + void encode(ceph::buffer::list& bl) const { + using ceph::encode; + /* See below, mask always has the lowest bit set in memory, but + * unset in the encoding */ + encode(mask & (~(uint64_t)1), bl); + encode(names, bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + using ceph::decode; + decode(mask, bl); + decode(names, bl); + /** + * Previously, there was a bug where insert did + * mask |= f.id rather than mask |= (1 << f.id). + * In FeatureSets from those version, mask always + * has the lowest bit set. Since then, masks always + * have the lowest bit unset. + * + * When we encounter such a FeatureSet, we have to + * reconstruct the mask from the names map. + */ + if (mask & 1) { + mask = 1; + std::map<uint64_t, std::string> temp_names; + temp_names.swap(names); + for (auto i = temp_names.begin(); i != temp_names.end(); ++i) { + insert(Feature(i->first, i->second)); + } + } else { + mask |= 1; + } + } + + void dump(ceph::Formatter *f) const { + for (auto p = names.cbegin(); p != names.cend(); ++p) { + char s[18]; + snprintf(s, sizeof(s), "feature_%llu", (unsigned long long)p->first); + f->dump_string(s, p->second); + } + } + }; + + // These features have no impact on the read / write status + FeatureSet compat; + // If any of these features are missing, read is possible ( as long + // as no incompat feature is missing ) but it is not possible to write + FeatureSet ro_compat; + // If any of these features are missing, read or write is not possible + FeatureSet incompat; + + CompatSet(FeatureSet& _compat, FeatureSet& _ro_compat, FeatureSet& _incompat) : + compat(_compat), ro_compat(_ro_compat), incompat(_incompat) {} + + CompatSet() : compat(), ro_compat(), incompat() { } + + + /* does this filesystem implementation have the + features required to read the other? */ + bool readable(CompatSet const& other) const { + return !((other.incompat.mask ^ incompat.mask) & other.incompat.mask); + } + + /* does this filesystem implementation have the + features required to write the other? */ + bool writeable(CompatSet const& other) const { + return readable(other) && + !((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask); + } + + /* Compare this CompatSet to another. + * CAREFULLY NOTE: This operation is NOT commutative. + * a > b DOES NOT imply that b < a. + * If returns: + * 0: The CompatSets have the same feature set. + * 1: This CompatSet's features are a strict superset of the other's. + * -1: This CompatSet is missing at least one feature + * described in the other. It may still have more features, though. + */ + int compare(const CompatSet& other) const { + if ((other.compat.mask == compat.mask) && + (other.ro_compat.mask == ro_compat.mask) && + (other.incompat.mask == incompat.mask)) return 0; + //okay, they're not the same + + //if we're writeable we have a superset of theirs on incompat and ro_compat + if (writeable(other) && !((other.compat.mask ^ compat.mask) + & other.compat.mask)) return 1; + //if we make it here, we weren't writeable or had a difference compat set + return -1; + } + + /* Get the features supported by other CompatSet but not this one, + * as a CompatSet. + */ + CompatSet unsupported(const CompatSet& other) const { + CompatSet diff; + uint64_t other_compat = + ((other.compat.mask ^ compat.mask) & other.compat.mask); + uint64_t other_ro_compat = + ((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask); + uint64_t other_incompat = + ((other.incompat.mask ^ incompat.mask) & other.incompat.mask); + for (int id = 1; id < 64; ++id) { + uint64_t mask = (uint64_t)1 << id; + if (mask & other_compat) { + diff.compat.insert( Feature(id, other.compat.names.at(id))); + } + if (mask & other_ro_compat) { + diff.ro_compat.insert(Feature(id, other.ro_compat.names.at(id))); + } + if (mask & other_incompat) { + diff.incompat.insert( Feature(id, other.incompat.names.at(id))); + } + } + return diff; + } + + /* Merge features supported by other CompatSet into this one. + * Return: true if some features were merged + */ + bool merge(CompatSet const & other) { + uint64_t other_compat = + ((other.compat.mask ^ compat.mask) & other.compat.mask); + uint64_t other_ro_compat = + ((other.ro_compat.mask ^ ro_compat.mask) & other.ro_compat.mask); + uint64_t other_incompat = + ((other.incompat.mask ^ incompat.mask) & other.incompat.mask); + if (!other_compat && !other_ro_compat && !other_incompat) + return false; + for (int id = 1; id < 64; ++id) { + uint64_t mask = (uint64_t)1 << id; + if (mask & other_compat) { + compat.insert( Feature(id, other.compat.get_name(id))); + } + if (mask & other_ro_compat) { + ro_compat.insert(Feature(id, other.ro_compat.get_name(id))); + } + if (mask & other_incompat) { + incompat.insert( Feature(id, other.incompat.get_name(id))); + } + } + return true; + } + + std::ostream& printlite(std::ostream& o) const { + o << "{c=[" << std::hex << compat.mask << "]"; + o << ",r=[" << std::hex << ro_compat.mask << "]"; + o << ",i=[" << std::hex << incompat.mask << "]}"; + o << std::dec; + return o; + } + + void encode(ceph::buffer::list& bl) const { + compat.encode(bl); + ro_compat.encode(bl); + incompat.encode(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + compat.decode(bl); + ro_compat.decode(bl); + incompat.decode(bl); + } + + void dump(ceph::Formatter *f) const { + f->open_object_section("compat"); + compat.dump(f); + f->close_section(); + f->open_object_section("ro_compat"); + ro_compat.dump(f); + f->close_section(); + f->open_object_section("incompat"); + incompat.dump(f); + f->close_section(); + } + + static void generate_test_instances(std::list<CompatSet*>& o) { + o.push_back(new CompatSet); + o.push_back(new CompatSet); + o.back()->compat.insert(Feature(1, "one")); + o.back()->compat.insert(Feature(2, "two")); + o.back()->ro_compat.insert(Feature(4, "four")); + o.back()->incompat.insert(Feature(3, "three")); + } +}; +WRITE_CLASS_ENCODER(CompatSet) + +inline std::ostream& operator<<(std::ostream& out, const CompatSet::Feature& f) +{ + return out << "F(" << f.id << ", \"" << f.name << "\")"; +} + +inline std::ostream& operator<<(std::ostream& out, const CompatSet::FeatureSet& fs) +{ + return out << fs.names; +} + +inline std::ostream& operator<<(std::ostream& out, const CompatSet& compat) +{ + return out << "compat=" << compat.compat + << ",rocompat=" << compat.ro_compat + << ",incompat=" << compat.incompat; +} + +#endif |