From 26921f43cb1fc9aade0ad895e66dc79dd742465b Mon Sep 17 00:00:00 2001 From: Mykola Golub Date: Tue, 13 Jan 2015 17:05:29 +0200 Subject: [PATCH] crush: introduce a helper class for dumping crush tree. Signed-off-by: Mykola Golub --- src/crush/CrushTreeDumper.h | 178 ++++++++++++++++++++++++++++++++++++ src/crush/CrushWrapper.h | 5 + src/crush/Makefile.am | 1 + 3 files changed, 184 insertions(+) create mode 100644 src/crush/CrushTreeDumper.h diff --git a/src/crush/CrushTreeDumper.h b/src/crush/CrushTreeDumper.h new file mode 100644 index 0000000000000..812b4ed2dc8e6 --- /dev/null +++ b/src/crush/CrushTreeDumper.h @@ -0,0 +1,178 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph distributed storage system + * + * Copyright (C) 2015 Mirantis Inc + * + * Author: Mykola Golub + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + */ + +#ifndef CRUSH_TREE_DUMPER_H +#define CRUSH_TREE_DUMPER_H + +#include "CrushWrapper.h" + +/** + * CrushTreeDumper: + * A helper class and functions to dump a crush tree. + * + * Example: + * + * class SimpleDumper : public CrushTreeDumper::Dumper { + * public: + * SimpleDumper(const CrushWrapper *crush) : + * CrushTreeDumper::Dumper(crush) {} + * protected: + * virtual void dump_item(const CrushTreeDumper::Item &qi, ostream *out) { + * *out << qi.id; + * for (int k = 0; k < qi.depth; k++) + * *out << "-"; + * if (qi.is_bucket()) + * *out << crush->get_item_name(qi.id) + * else + * *out << "osd." << qi.id; + * *out << "\n"; + * } + * }; + * + * SimpleDumper(crush).dump(out); + * + */ + +namespace CrushTreeDumper { + + struct Item { + int id; + int depth; + float weight; + list children; + + Item() : id(0), depth(0), weight(0) {} + Item(int i, int d, float w) : id(i), depth(d), weight(w) {} + + bool is_bucket() const { return id < 0; } + }; + + template + class Dumper : public list { + public: + Dumper(const CrushWrapper *crush_) : crush(crush_) { + crush->find_roots(roots); + root = roots.begin(); + } + + virtual ~Dumper() {} + + virtual void reset() { + root = roots.begin(); + touched.clear(); + clear(); + } + + bool next(Item &qi) { + if (empty()) { + if (root == roots.end()) + return false; + push_back(Item(*root, 0, crush->get_bucket_weightf(*root))); + root++; + } + + qi = front(); + pop_front(); + touched.insert(qi.id); + + if (qi.is_bucket()) { + // queue bucket contents... + int s = crush->get_bucket_size(qi.id); + for (int k = s - 1; k >= 0; k--) { + int id = crush->get_bucket_item(qi.id, k); + qi.children.push_back(id); + push_front(Item(id, qi.depth + 1, + crush->get_bucket_item_weightf(qi.id, k))); + } + } + return true; + } + + void dump(F *f) { + reset(); + Item qi; + while (next(qi)) + dump_item(qi, f); + } + + bool is_touched(int id) const { return touched.count(id) > 0; } + + protected: + virtual void dump_item(const Item &qi, F *f) = 0; + + protected: + const CrushWrapper *crush; + + private: + set touched; + set roots; + set::iterator root; + }; + + inline void dump_item_fields(const CrushWrapper *crush, + const Item &qi, Formatter *f) { + f->dump_int("id", qi.id); + if (qi.is_bucket()) { + int type = crush->get_bucket_type(qi.id); + f->dump_string("name", crush->get_item_name(qi.id)); + f->dump_string("type", crush->get_type_name(type)); + f->dump_int("type_id", type); + } else { + f->dump_stream("name") << "osd." << qi.id; + f->dump_string("type", crush->get_type_name(0)); + f->dump_int("type_id", 0); + f->dump_float("crush_weight", qi.weight); + f->dump_unsigned("depth", qi.depth); + } + } + + inline void dump_bucket_children(const CrushWrapper *crush, + const Item &qi, Formatter *f) { + if (!qi.is_bucket()) + return; + + f->open_array_section("children"); + for (list::const_iterator i = qi.children.begin(); + i != qi.children.end(); + i++) { + f->dump_int("child", *i); + } + f->close_section(); + } + + class FormattingDumper : public Dumper { + public: + FormattingDumper(const CrushWrapper *crush) : Dumper(crush) {} + + protected: + virtual void dump_item(const Item &qi, Formatter *f) { + f->open_object_section("item"); + dump_item_fields(qi, f); + dump_bucket_children(qi, f); + } + + virtual void dump_item_fields(const Item &qi, Formatter *f) { + CrushTreeDumper::dump_item_fields(crush, qi, f); + } + + virtual void dump_bucket_children(const Item &qi, Formatter *f) { + CrushTreeDumper::dump_bucket_children(crush, qi, f); + } + }; + +} + +#endif diff --git a/src/crush/CrushWrapper.h b/src/crush/CrushWrapper.h index b17f9a14f14f4..5e5330591e5d8 100644 --- a/src/crush/CrushWrapper.h +++ b/src/crush/CrushWrapper.h @@ -890,6 +890,11 @@ public: if (IS_ERR(b)) return PTR_ERR(b); return crush_get_bucket_item_weight(b, pos); } + float get_bucket_item_weightf(int id, int pos) const { + const crush_bucket *b = get_bucket(id); + if (IS_ERR(b)) return 0; + return (float)crush_get_bucket_item_weight(b, pos) / (float)0x10000; + } /* modifiers */ int add_bucket(int bucketno, int alg, int hash, int type, int size, diff --git a/src/crush/Makefile.am b/src/crush/Makefile.am index 3d2f45b80e60f..bae9c886faa4a 100644 --- a/src/crush/Makefile.am +++ b/src/crush/Makefile.am @@ -11,6 +11,7 @@ noinst_LTLIBRARIES += libcrush.la noinst_HEADERS += \ crush/CrushCompiler.h \ crush/CrushTester.h \ + crush/CrushTreeDumper.h \ crush/CrushWrapper.h \ crush/CrushWrapper.i \ crush/builder.h \ -- 2.39.5