]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/crush/CrushWrapper.cc
update sources to 12.2.7
[ceph.git] / ceph / src / crush / CrushWrapper.cc
index 3a669e959f0235103c32bb2551e8d8bcdace17e5..b85c7409945366920e523fe71d206d4a4f0b55a7 100644 (file)
@@ -5,6 +5,7 @@
 #include "common/debug.h"
 #include "common/Formatter.h"
 #include "common/errno.h"
+#include "common/TextTable.h"
 #include "include/stringify.h"
 
 #include "CrushWrapper.h"
@@ -12,7 +13,7 @@
 
 #define dout_subsys ceph_subsys_crush
 
-bool CrushWrapper::has_legacy_rulesets() const
+bool CrushWrapper::has_legacy_rule_ids() const
 {
   for (unsigned i=0; i<crush->max_rules; i++) {
     crush_rule *r = crush->rules[i];
@@ -24,49 +25,27 @@ bool CrushWrapper::has_legacy_rulesets() const
   return false;
 }
 
-int CrushWrapper::renumber_rules_by_ruleset()
+std::map<int, int> CrushWrapper::renumber_rules()
 {
-  int max_ruleset = 0;
+  std::map<int, int> result;
   for (unsigned i=0; i<crush->max_rules; i++) {
     crush_rule *r = crush->rules[i];
-    if (r && r->mask.ruleset >= max_ruleset) {
-      max_ruleset = r->mask.ruleset + 1;
+    if (r && r->mask.ruleset != i) {
+      result[r->mask.ruleset] = i;
+      r->mask.ruleset = i;
     }
   }
-  struct crush_rule **newrules =
-    (crush_rule**)calloc(1, max_ruleset * sizeof(crush_rule*));
-  for (unsigned i=0; i<crush->max_rules; i++) {
-    crush_rule *r = crush->rules[i];
-    if (!r)
-      continue;
-    if (newrules[r->mask.ruleset]) {
-      // collision, we can't do it.
-      free(newrules);
-      return -EINVAL;
-    }
-    newrules[r->mask.ruleset] = r;
-  }
-
-  // success, swap!
-  free(crush->rules);
-  crush->rules = newrules;
-  crush->max_rules = max_ruleset;
-  return 0;
+  return result;
 }
 
-bool CrushWrapper::has_multirule_rulesets() const
+bool CrushWrapper::has_non_straw2_buckets() const
 {
-  for (unsigned i=0; i<crush->max_rules; i++) {
-    crush_rule *r = crush->rules[i];
-    if (!r)
+  for (int i=0; i<crush->max_buckets; ++i) {
+    crush_bucket *b = crush->buckets[i];
+    if (!b)
       continue;
-    for (unsigned j=i+1; j<crush->max_rules; j++) {
-      crush_rule *s = crush->rules[j];
-      if (!s)
-       continue;
-      if (r->mask.ruleset == s->mask.ruleset)
-       return true;
-    }
+    if (b->alg != CRUSH_BUCKET_STRAW2)
+      return true;
   }
   return false;
 }
@@ -175,13 +154,15 @@ bool CrushWrapper::has_incompat_choose_args() const
     return false;
   if (choose_args.size() > 1)
     return true;
+  if (choose_args.begin()->first != DEFAULT_CHOOSE_ARGS)
+    return true;
   crush_choose_arg_map arg_map = choose_args.begin()->second;
   for (__u32 i = 0; i < arg_map.size; i++) {
     crush_choose_arg *arg = &arg_map.args[i];
-    if (arg->weight_set_size == 0 &&
+    if (arg->weight_set_positions == 0 &&
        arg->ids_size == 0)
        continue;
-    if (arg->weight_set_size != 1)
+    if (arg->weight_set_positions != 1)
       return true;
     if (arg->ids_size != 0)
       return true;
@@ -276,7 +257,34 @@ int CrushWrapper::rename_bucket(const string& srcname,
   return set_item_name(oldid, dstname);
 }
 
-void CrushWrapper::find_takes(set<int>& roots) const
+int CrushWrapper::rename_rule(const string& srcname,
+                              const string& dstname,
+                              ostream *ss)
+{
+  if (!rule_exists(srcname)) {
+    if (ss) {
+      *ss << "source rule name '" << srcname << "' does not exist";
+    }
+    return -ENOENT;
+  }
+  if (rule_exists(dstname)) {
+    if (ss) {
+      *ss << "destination rule name '" << dstname << "' already exists";
+    }
+    return -EEXIST;
+  }
+  int rule_id = get_rule_id(srcname);
+  auto it = rule_name_map.find(rule_id);
+  assert(it != rule_name_map.end());
+  it->second = dstname;
+  if (have_rmaps) {
+    rule_name_rmap.erase(srcname);
+    rule_name_rmap[dstname] = rule_id;
+  }
+  return 0;
+}
+
+void CrushWrapper::find_takes(set<int> *roots) const
 {
   for (unsigned i=0; i<crush->max_rules; i++) {
     crush_rule *r = crush->rules[i];
@@ -284,19 +292,32 @@ void CrushWrapper::find_takes(set<int>& roots) const
       continue;
     for (unsigned j=0; j<r->len; j++) {
       if (r->steps[j].op == CRUSH_RULE_TAKE)
-       roots.insert(r->steps[j].arg1);
+       roots->insert(r->steps[j].arg1);
     }
   }
 }
 
-void CrushWrapper::find_roots(set<int>& roots) const
+void CrushWrapper::find_takes_by_rule(int rule, set<int> *roots) const
+{
+  if (rule < 0 || rule >= (int)crush->max_rules)
+    return;
+  crush_rule *r = crush->rules[rule];
+  if (!r)
+    return;
+  for (unsigned i = 0; i < r->len; i++) {
+    if (r->steps[i].op == CRUSH_RULE_TAKE)
+      roots->insert(r->steps[i].arg1);
+  }
+}
+
+void CrushWrapper::find_roots(set<int> *roots) const
 {
   for (int i = 0; i < crush->max_buckets; i++) {
     if (!crush->buckets[i])
       continue;
     crush_bucket *b = crush->buckets[i];
     if (!_search_item_exists(b->id))
-      roots.insert(b->id);
+      roots->insert(b->id);
   }
 }
 
@@ -335,28 +356,38 @@ bool CrushWrapper::_maybe_remove_last_instance(CephContext *cct, int item, bool
     crush_remove_bucket(crush, t);
     if (class_bucket.count(item) != 0)
       class_bucket.erase(item);
+    class_remove_item(item);
+    update_choose_args(cct);
   }
   if ((item >= 0 || !unlink_only) && name_map.count(item)) {
     ldout(cct, 5) << "_maybe_remove_last_instance removing name for item " << item << dendl;
     name_map.erase(item);
     have_rmaps = false;
+    if (item >= 0 && !unlink_only) {
+      class_remove_item(item);
+    }
   }
+  rebuild_roots_with_classes();
   return true;
 }
 
-int CrushWrapper::remove_root(int item, bool unused)
+int CrushWrapper::remove_root(int item)
 {
-  if (unused && _bucket_is_in_use(item))
-    return 0;
-
   crush_bucket *b = get_bucket(item);
-  if (IS_ERR(b))
-    return -ENOENT;
+  if (IS_ERR(b)) {
+    // should be idempotent
+    // e.g.: we use 'crush link' to link same host into
+    // different roots, which as a result can cause different
+    // shadow trees reference same hosts too. This means
+    // we may need to destory the same buckets(hosts, racks, etc.)
+    // multiple times during rebuilding all shadow trees.
+    return 0;
+  }
 
   for (unsigned n = 0; n < b->size; n++) {
     if (b->items[n] >= 0)
       continue;
-    int r = remove_root(b->items[n], unused);
+    int r = remove_root(b->items[n]);
     if (r < 0)
       return r;
   }
@@ -368,19 +399,86 @@ int CrushWrapper::remove_root(int item, bool unused)
   }
   if (class_bucket.count(item) != 0)
     class_bucket.erase(item);
+  class_remove_item(item);
+  update_choose_args(nullptr);
   return 0;
 }
 
+void CrushWrapper::update_choose_args(CephContext *cct)
+{
+  for (auto& i : choose_args) {
+    crush_choose_arg_map &arg_map = i.second;
+    unsigned positions = get_choose_args_positions(arg_map);
+    for (int j = 0; j < crush->max_buckets; ++j) {
+      crush_bucket *b = crush->buckets[j];
+      auto& carg = arg_map.args[j];
+      // strip out choose_args for any buckets that no longer exist
+      if (!b || b->alg != CRUSH_BUCKET_STRAW2) {
+       if (carg.ids) {
+         if (cct)
+           ldout(cct,0) << __func__ << " removing " << i.first << " bucket "
+                        << (-1-j) << " ids" << dendl;
+         free(carg.ids);
+         carg.ids = 0;
+         carg.ids_size = 0;
+       }
+       if (carg.weight_set) {
+         if (cct)
+           ldout(cct,0) << __func__ << " removing " << i.first << " bucket "
+                        << (-1-j) << " weight_sets" << dendl;
+         for (unsigned p = 0; p < carg.weight_set_positions; ++p) {
+           free(carg.weight_set[p].weights);
+         }
+         free(carg.weight_set);
+         carg.weight_set = 0;
+         carg.weight_set_positions = 0;
+       }
+       continue;
+      }
+      if (carg.weight_set_positions == 0) {
+       continue;       // skip it
+      }
+      if (carg.weight_set_positions != positions) {
+       if (cct)
+         lderr(cct) << __func__ << " " << i.first << " bucket "
+                    << (-1-j) << " positions " << carg.weight_set_positions
+                    << " -> " << positions << dendl;
+       continue;       // wth... skip!
+      }
+      // mis-sized weight_sets?  this shouldn't ever happen.
+      for (unsigned p = 0; p < positions; ++p) {
+       if (carg.weight_set[p].size != b->size) {
+         if (cct)
+           lderr(cct) << __func__ << " fixing " << i.first << " bucket "
+                      << (-1-j) << " position " << p
+                      << " size " << carg.weight_set[p].size << " -> "
+                      << b->size << dendl;
+         auto old_ws = carg.weight_set[p];
+         carg.weight_set[p].size = b->size;
+         carg.weight_set[p].weights = (__u32*)calloc(b->size, sizeof(__u32));
+         auto max = std::min<unsigned>(old_ws.size, b->size);
+         for (unsigned k = 0; k < max; ++k) {
+           carg.weight_set[p].weights[k] = old_ws.weights[k];
+         }
+         free(old_ws.weights);
+       }
+      }
+    }
+  }
+}
+
 int CrushWrapper::remove_item(CephContext *cct, int item, bool unlink_only)
 {
-  ldout(cct, 5) << "remove_item " << item << (unlink_only ? " unlink_only":"") << dendl;
+  ldout(cct, 5) << "remove_item " << item
+               << (unlink_only ? " unlink_only":"") << dendl;
 
   int ret = -ENOENT;
 
   if (item < 0 && !unlink_only) {
     crush_bucket *t = get_bucket(item);
     if (IS_ERR(t)) {
-      ldout(cct, 1) << "remove_item bucket " << item << " does not exist" << dendl;
+      ldout(cct, 1) << "remove_item bucket " << item << " does not exist"
+                   << dendl;
       return -ENOENT;
     }
 
@@ -404,6 +502,11 @@ int CrushWrapper::remove_item(CephContext *cct, int item, bool unlink_only)
       if (id == item) {
        ldout(cct, 5) << "remove_item removing item " << item
                      << " from bucket " << b->id << dendl;
+       for (auto& p : choose_args) {
+         // weight down each weight-set to 0 before we remove the item
+         vector<int> weightv(get_choose_args_positions(p.second), 0);
+         choose_args_adjust_item_weight(cct, p.second, item, weightv, nullptr);
+       }
        bucket_remove_item(b, item);
        adjust_item_weight(cct, b->id, b->weight);
        ret = 0;
@@ -457,7 +560,8 @@ bool CrushWrapper::_bucket_is_in_use(int item)
   return false;
 }
 
-int CrushWrapper::_remove_item_under(CephContext *cct, int item, int ancestor, bool unlink_only)
+int CrushWrapper::_remove_item_under(
+  CephContext *cct, int item, int ancestor, bool unlink_only)
 {
   ldout(cct, 5) << "_remove_item_under " << item << " under " << ancestor
                << (unlink_only ? " unlink_only":"") << dendl;
@@ -475,7 +579,14 @@ int CrushWrapper::_remove_item_under(CephContext *cct, int item, int ancestor, b
   for (unsigned i=0; i<b->size; ++i) {
     int id = b->items[i];
     if (id == item) {
-      ldout(cct, 5) << "_remove_item_under removing item " << item << " from bucket " << b->id << dendl;
+      ldout(cct, 5) << "_remove_item_under removing item " << item
+                   << " from bucket " << b->id << dendl;
+      for (auto& p : choose_args) {
+       // weight down each weight-set to 0 before we remove the item
+       vector<int> weightv(get_choose_args_positions(p.second), 0);
+       _choose_args_adjust_item_weight_in_bucket(
+         cct, p.second, b->id, item, weightv, nullptr);
+      }
       bucket_remove_item(b, item);
       adjust_item_weight(cct, b->id, b->weight);
       ret = 0;
@@ -488,7 +599,8 @@ int CrushWrapper::_remove_item_under(CephContext *cct, int item, int ancestor, b
   return ret;
 }
 
-int CrushWrapper::remove_item_under(CephContext *cct, int item, int ancestor, bool unlink_only)
+int CrushWrapper::remove_item_under(
+  CephContext *cct, int item, int ancestor, bool unlink_only)
 {
   ldout(cct, 5) << "remove_item_under " << item << " under " << ancestor
                << (unlink_only ? " unlink_only":"") << dendl;
@@ -632,7 +744,7 @@ bool CrushWrapper::check_item_loc(CephContext *cct, int item, const map<string,s
     return false;
   }
   
-  ldout(cct, 1) << "check_item_loc item " << item << " loc " << loc << dendl;
+  ldout(cct, 2) << __func__ << " item " << item << " loc " << loc << dendl;
   return false;
 }
 
@@ -736,6 +848,55 @@ int CrushWrapper::get_children(int id, list<int> *children)
   return b->size;
 }
 
+void CrushWrapper::get_children_of_type(int id,
+                                        int type,
+                                       set<int> *children,
+                                       bool exclude_shadow) const
+{
+  if (id >= 0) {
+    if (type == 0) {
+      // want leaf?
+      children->insert(id);
+    }
+    return;
+  }
+  auto b = get_bucket(id);
+  if (IS_ERR(b)) {
+    return;
+  }
+  if (b->type < type) {
+    // give up
+    return;
+  } else if (b->type == type) {
+    if (!is_shadow_item(b->id) || !exclude_shadow) {
+      children->insert(b->id);
+    }
+    return;
+  }
+  for (unsigned n = 0; n < b->size; n++) {
+    get_children_of_type(b->items[n], type, children, exclude_shadow);
+  }
+}
+
+int CrushWrapper::get_rule_failure_domain(int rule_id)
+{
+  crush_rule *rule = get_rule(rule_id);
+  if (IS_ERR(rule)) {
+    return -ENOENT;
+  }
+  int type = 0; // default to osd-level
+  for (unsigned s = 0; s < rule->len; ++s) {
+    if ((rule->steps[s].op == CRUSH_RULE_CHOOSE_FIRSTN ||
+         rule->steps[s].op == CRUSH_RULE_CHOOSE_INDEP ||
+         rule->steps[s].op == CRUSH_RULE_CHOOSELEAF_FIRSTN ||
+         rule->steps[s].op == CRUSH_RULE_CHOOSELEAF_INDEP) &&
+         rule->steps[s].arg2 > type) {
+      type = rule->steps[s].arg2;
+    }
+  }
+  return type;
+}
+
 int CrushWrapper::_get_leaves(int id, list<int> *leaves)
 {
   assert(leaves);
@@ -795,8 +956,9 @@ int CrushWrapper::get_leaves(const string &name, set<int> *leaves)
   return 0;
 }
 
-int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string name,
-                             const map<string,string>& loc)  // typename -> bucketname
+int CrushWrapper::insert_item(
+  CephContext *cct, int item, float weight, string name,
+  const map<string,string>& loc)  // typename -> bucketname
 {
   ldout(cct, 5) << "insert_item item " << item << " weight " << weight
                << " name " << name << " loc " << loc << dendl;
@@ -807,6 +969,11 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
   if (!is_valid_crush_loc(cct, loc))
     return -EINVAL;
 
+  int r = validate_weightf(weight);
+  if (r < 0) {
+    return r;
+  }
+
   if (name_exists(name)) {
     if (get_item_id(name) != item) {
       ldout(cct, 10) << "device name '" << name << "' already exists as id "
@@ -819,9 +986,10 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
 
   int cur = item;
 
-  // create locations if locations don't exist and add child in location with 0 weight
-  // the more detail in the insert_item method declaration in CrushWrapper.h
-  for (map<int,string>::iterator p = type_map.begin(); p != type_map.end(); ++p) {
+  // create locations if locations don't exist and add child in
+  // location with 0 weight the more detail in the insert_item method
+  // declaration in CrushWrapper.h
+  for (auto p = type_map.begin(); p != type_map.end(); ++p) {
     // ignore device type
     if (p->first == 0)
       continue;
@@ -829,7 +997,8 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
     // skip types that are unspecified
     map<string,string>::const_iterator q = loc.find(p->second);
     if (q == loc.end()) {
-      ldout(cct, 2) << "warning: did not specify location for '" << p->second << "' level (levels are "
+      ldout(cct, 2) << "warning: did not specify location for '"
+                   << p->second << "' level (levels are "
                    << type_map << ")" << dendl;
       continue;
     }
@@ -840,7 +1009,8 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
       int r = add_bucket(0, 0,
                         CRUSH_HASH_DEFAULT, p->first, 1, &cur, &empty, &newid);
       if (r < 0) {
-        ldout(cct, 1) << "add_bucket failure error: " << cpp_strerror(r) << dendl;
+        ldout(cct, 1) << "add_bucket failure error: " << cpp_strerror(r)
+                     << dendl;
         return r;
       }
       set_item_name(newid, q->second);
@@ -858,7 +1028,8 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
 
     // check that we aren't creating a cycle.
     if (subtree_contains(id, cur)) {
-      ldout(cct, 1) << "insert_item item " << cur << " already exists beneath " << id << dendl;
+      ldout(cct, 1) << "insert_item item " << cur << " already exists beneath "
+                   << id << dendl;
       return -EINVAL;
     }
 
@@ -887,19 +1058,29 @@ int CrushWrapper::insert_item(CephContext *cct, int item, float weight, string n
   }
 
   // adjust the item's weight in location
-  if(adjust_item_weightf_in_loc(cct, item, weight, loc) > 0) {
+  if (adjust_item_weightf_in_loc(cct, item, weight, loc) > 0) {
     if (item >= crush->max_devices) {
       crush->max_devices = item + 1;
-      ldout(cct, 5) << "insert_item max_devices now " << crush->max_devices << dendl;
+      ldout(cct, 5) << "insert_item max_devices now " << crush->max_devices
+                   << dendl;
+    }
+    r = rebuild_roots_with_classes();
+    if (r < 0) {
+      ldout(cct, 0) << __func__ << " unable to rebuild roots with classes: "
+                    << cpp_strerror(r) << dendl;
+      return r;
     }
     return 0;
   }
 
-  ldout(cct, 1) << "error: didn't find anywhere to add item " << item << " in " << loc << dendl;
+  ldout(cct, 1) << "error: didn't find anywhere to add item " << item
+               << " in " << loc << dendl;
   return -EINVAL;
 }
 
-int CrushWrapper::move_bucket(CephContext *cct, int id, const map<string,string>& loc)
+
+int CrushWrapper::move_bucket(
+  CephContext *cct, int id, const map<string,string>& loc)
 {
   // sorry this only works for buckets
   if (id >= 0)
@@ -918,6 +1099,59 @@ int CrushWrapper::move_bucket(CephContext *cct, int id, const map<string,string>
   return insert_item(cct, id, bucket_weight / (float)0x10000, id_name, loc);
 }
 
+int CrushWrapper::detach_bucket(CephContext *cct, int item)
+{
+  if (!crush)
+    return (-EINVAL);
+
+  if (item >= 0)
+    return (-EINVAL);
+
+  // check that the bucket that we want to detach exists
+  assert(bucket_exists(item));
+
+  // get the bucket's weight
+  crush_bucket *b = get_bucket(item);
+  unsigned bucket_weight = b->weight;
+
+  // get where the bucket is located
+  pair<string, string> bucket_location = get_immediate_parent(item);
+
+  // get the id of the parent bucket
+  int parent_id = get_item_id(bucket_location.second);
+
+  // get the parent bucket
+  crush_bucket *parent_bucket = get_bucket(parent_id);
+
+  if (!IS_ERR(parent_bucket)) {
+    // zero out the bucket weight
+    bucket_adjust_item_weight(cct, parent_bucket, item, 0);
+    adjust_item_weight(cct, parent_bucket->id, parent_bucket->weight);
+    for (auto& p : choose_args) {
+      // weight down each weight-set to 0 before we remove the item
+      vector<int> weightv(get_choose_args_positions(p.second), 0);
+      choose_args_adjust_item_weight(cct, p.second, item, weightv, nullptr);
+    }
+
+    // remove the bucket from the parent
+    bucket_remove_item(parent_bucket, item);
+  } else if (PTR_ERR(parent_bucket) != -ENOENT) {
+    return PTR_ERR(parent_bucket);
+  }
+
+  // check that we're happy
+  int test_weight = 0;
+  map<string,string> test_location;
+  test_location[ bucket_location.first ] = (bucket_location.second);
+
+  bool successful_detach = !(check_item_loc(cct, item, test_location,
+                                           &test_weight));
+  assert(successful_detach);
+  assert(test_weight == 0);
+
+  return bucket_weight;
+}
+
 int CrushWrapper::swap_bucket(CephContext *cct, int src, int dst)
 {
   if (src >= 0 || dst >= 0)
@@ -961,10 +1195,11 @@ int CrushWrapper::swap_bucket(CephContext *cct, int src, int dst)
 
   // swap names
   swap_names(src, dst);
-  return 0;
+  return rebuild_roots_with_classes();
 }
 
-int CrushWrapper::link_bucket(CephContext *cct, int id, const map<string,string>& loc)
+int CrushWrapper::link_bucket(
+  CephContext *cct, int id, const map<string,string>& loc)
 {
   // sorry this only works for buckets
   if (id >= 0)
@@ -982,8 +1217,9 @@ int CrushWrapper::link_bucket(CephContext *cct, int id, const map<string,string>
   return insert_item(cct, id, bucket_weight / (float)0x10000, id_name, loc);
 }
 
-int CrushWrapper::create_or_move_item(CephContext *cct, int item, float weight, string name,
-                                     const map<string,string>& loc)  // typename -> bucketname
+int CrushWrapper::create_or_move_item(
+  CephContext *cct, int item, float weight, string name,
+  const map<string,string>& loc)  // typename -> bucketname
 {
   int ret = 0;
   int old_iweight;
@@ -992,14 +1228,17 @@ int CrushWrapper::create_or_move_item(CephContext *cct, int item, float weight,
     return -EINVAL;
 
   if (check_item_loc(cct, item, loc, &old_iweight)) {
-    ldout(cct, 5) << "create_or_move_item " << item << " already at " << loc << dendl;
+    ldout(cct, 5) << "create_or_move_item " << item << " already at " << loc
+                 << dendl;
   } else {
     if (_search_item_exists(item)) {
       weight = get_item_weightf(item);
-      ldout(cct, 10) << "create_or_move_item " << item << " exists with weight " << weight << dendl;
+      ldout(cct, 10) << "create_or_move_item " << item
+                    << " exists with weight " << weight << dendl;
       remove_item(cct, item, true);
     }
-    ldout(cct, 5) << "create_or_move_item adding " << item << " weight " << weight
+    ldout(cct, 5) << "create_or_move_item adding " << item
+                 << " weight " << weight
                  << " at " << loc << dendl;
     ret = insert_item(cct, item, weight, name, loc);
     if (ret == 0)
@@ -1008,8 +1247,9 @@ int CrushWrapper::create_or_move_item(CephContext *cct, int item, float weight,
   return ret;
 }
 
-int CrushWrapper::update_item(CephContext *cct, int item, float weight, string name,
-                             const map<string,string>& loc)  // typename -> bucketname
+int CrushWrapper::update_item(
+  CephContext *cct, int item, float weight, string name,
+  const map<string,string>& loc)  // typename -> bucketname
 {
   ldout(cct, 5) << "update_item item " << item << " weight " << weight
                << " name " << name << " loc " << loc << dendl;
@@ -1021,6 +1261,11 @@ int CrushWrapper::update_item(CephContext *cct, int item, float weight, string n
   if (!is_valid_crush_loc(cct, loc))
     return -EINVAL;
 
+  ret = validate_weightf(weight);
+  if (ret < 0) {
+    return ret;
+  }
+
   // compare quantized (fixed-point integer) weights!  
   int iweight = (int)(weight * (float)0x10000);
   int old_iweight;
@@ -1028,12 +1273,14 @@ int CrushWrapper::update_item(CephContext *cct, int item, float weight, string n
     ldout(cct, 5) << "update_item " << item << " already at " << loc << dendl;
     if (old_iweight != iweight) {
       ldout(cct, 5) << "update_item " << item << " adjusting weight "
-                   << ((float)old_iweight/(float)0x10000) << " -> " << weight << dendl;
+                   << ((float)old_iweight/(float)0x10000) << " -> " << weight
+                   << dendl;
       adjust_item_weight_in_loc(cct, item, iweight, loc);
       ret = 1;
     }
     if (get_item_name(item) != name) {
-      ldout(cct, 5) << "update_item setting " << item << " name to " << name << dendl;
+      ldout(cct, 5) << "update_item setting " << item << " name to " << name
+                   << dendl;
       set_item_name(item, name);
       ret = 1;
     }
@@ -1093,7 +1340,8 @@ int CrushWrapper::adjust_item_weight(CephContext *cct, int id, int weight)
     for (unsigned i = 0; i < b->size; i++) {
       if (b->items[i] == id) {
        int diff = bucket_adjust_item_weight(cct, b, id, weight);
-       ldout(cct, 5) << "adjust_item_weight " << id << " diff " << diff << " in bucket " << bidx << dendl;
+       ldout(cct, 5) << "adjust_item_weight " << id << " diff " << diff
+                     << " in bucket " << bidx << dendl;
        adjust_item_weight(cct, -1 - bidx, b->weight);
        changed++;
       }
@@ -1106,10 +1354,11 @@ int CrushWrapper::adjust_item_weight(CephContext *cct, int id, int weight)
 
 int CrushWrapper::adjust_item_weight_in_loc(CephContext *cct, int id, int weight, const map<string,string>& loc)
 {
-  ldout(cct, 5) << "adjust_item_weight_in_loc " << id << " weight " << weight << " in " << loc << dendl;
+  ldout(cct, 5) << "adjust_item_weight_in_loc " << id << " weight " << weight
+               << " in " << loc << dendl;
   int changed = 0;
 
-  for (map<string,string>::const_iterator l = loc.begin(); l != loc.end(); ++l) {
+  for (auto l = loc.begin(); l != loc.end(); ++l) {
     int bid = get_item_id(l->second);
     if (!bucket_exists(bid))
       continue;
@@ -1117,7 +1366,8 @@ int CrushWrapper::adjust_item_weight_in_loc(CephContext *cct, int id, int weight
     for (unsigned int i = 0; i < b->size; i++) {
       if (b->items[i] == id) {
        int diff = bucket_adjust_item_weight(cct, b, id, weight);
-       ldout(cct, 5) << "adjust_item_weight_in_loc " << id << " diff " << diff << " in bucket " << bid << dendl;
+       ldout(cct, 5) << "adjust_item_weight_in_loc " << id << " diff " << diff
+                     << " in bucket " << bid << dendl;
        adjust_item_weight(cct, bid, b->weight);
        changed++;
       }
@@ -1184,6 +1434,8 @@ pair<string,string> CrushWrapper::get_immediate_parent(int id, int *_ret)
     crush_bucket *b = crush->buckets[bidx];
     if (b == 0)
       continue;
+   if (is_shadow_item(b->id))
+      continue;
     for (unsigned i = 0; i < b->size; i++)
       if (b->items[i] == id) {
         string parent_id = name_map[b->id];
@@ -1206,6 +1458,8 @@ int CrushWrapper::get_immediate_parent_id(int id, int *parent) const
     crush_bucket *b = crush->buckets[bidx];
     if (b == 0)
       continue;
+    if (is_shadow_item(b->id))
+      continue;
     for (unsigned i = 0; i < b->size; i++) {
       if (b->items[i] == id) {
        *parent = b->id;
@@ -1216,43 +1470,94 @@ int CrushWrapper::get_immediate_parent_id(int id, int *parent) const
   return -ENOENT;
 }
 
-int CrushWrapper::get_parent_of_type(int item, int type) const
+int CrushWrapper::get_parent_of_type(int item, int type, int rule) const
 {
-  do {
-    int r = get_immediate_parent_id(item, &item);
-    if (r < 0) {
-      return 0;
+  if (rule < 0) {
+    // no rule specified
+    do {
+      int r = get_immediate_parent_id(item, &item);
+      if (r < 0) {
+        return 0;
+      }
+    } while (get_bucket_type(item) != type);
+    return item;
+  }
+  set<int> roots;
+  find_takes_by_rule(rule, &roots);
+  for (auto root : roots) {
+    set<int> candidates;
+    get_children_of_type(root, type, &candidates, false);
+    for (auto candidate : candidates) {
+      if (subtree_contains(candidate, item)) {
+       // note that here we assure that no two different buckets
+       // from a single crush rule will share a same device,
+       // which should generally be true.
+        return candidate;
+      }
     }
-  } while (get_bucket_type(item) != type);
-  return item;
+  }
+  return 0; // not found
 }
 
-bool CrushWrapper::class_is_in_use(int class_id)
+int CrushWrapper::rename_class(const string& srcname, const string& dstname)
 {
-  for (auto &i : class_bucket)
-    for (auto &j : i.second)
-      if (j.first == class_id)
-       return true;
-
-  for (auto &i : class_map)
-    if (i.second == class_id)
-      return true;
+  auto i = class_rname.find(srcname);
+  if (i == class_rname.end())
+    return -ENOENT;
+  auto j = class_rname.find(dstname);
+  if (j != class_rname.end())
+    return -EEXIST;
 
-  return false;
+  int class_id = i->second;
+  assert(class_name.count(class_id));
+  // rename any shadow buckets of old class name
+  for (auto &it: class_map) {
+    if (it.first < 0 && it.second == class_id) {
+        string old_name = get_item_name(it.first);
+        size_t pos = old_name.find("~");
+        assert(pos != string::npos);
+        string name_no_class = old_name.substr(0, pos);
+        string old_class_name = old_name.substr(pos + 1);
+        assert(old_class_name == srcname);
+        string new_name = name_no_class + "~" + dstname;
+        // we do not use set_item_name
+        // because the name is intentionally invalid
+        name_map[it.first] = new_name;
+        have_rmaps = false;
+    }
+  }
+
+  // rename class
+  class_rname.erase(srcname);
+  class_name.erase(class_id);
+  class_rname[dstname] = class_id;
+  class_name[class_id] = dstname;
+  return 0;
 }
 
-int CrushWrapper::populate_classes()
+int CrushWrapper::populate_classes(
+  const std::map<int32_t, map<int32_t, int32_t>>& old_class_bucket)
 {
+  // build set of previous used shadow ids
+  set<int32_t> used_ids;
+  for (auto& p : old_class_bucket) {
+    for (auto& q : p.second) {
+      used_ids.insert(q.second);
+    }
+  }
+  // accumulate weight values for each carg and bucket as we go. because it is
+  // depth first, we will have the nested bucket weights we need when we
+  // finish constructing the containing buckets.
+  map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> [bucket weight for each position]
   set<int> roots;
-  find_roots(roots);
+  find_nonshadow_roots(&roots);
   for (auto &r : roots) {
     if (r >= 0)
       continue;
-    if (id_has_class(r))
-      continue;
     for (auto &c : class_name) {
       int clone;
-      int res = device_class_clone(r, c.first, &clone);
+      int res = device_class_clone(r, c.first, old_class_bucket, used_ids,
+                                  &clone, &cmap_item_weight);
       if (res < 0)
        return res;
     }
@@ -1260,21 +1565,14 @@ int CrushWrapper::populate_classes()
   return 0;
 }
 
-int CrushWrapper::cleanup_classes()
-{
-  return trim_roots_with_class(true);
-}
-
-int CrushWrapper::trim_roots_with_class(bool unused)
+int CrushWrapper::trim_roots_with_class()
 {
   set<int> roots;
-  find_roots(roots);
+  find_shadow_roots(&roots);
   for (auto &r : roots) {
     if (r >= 0)
       continue;
-    if (!id_has_class(r))
-      continue;
-    int res = remove_root(r, unused);
+    int res = remove_root(r);
     if (res)
       return res;
   }
@@ -1283,10 +1581,36 @@ int CrushWrapper::trim_roots_with_class(bool unused)
   return 0;
 }
 
+int32_t CrushWrapper::_alloc_class_id() const {
+  if (class_name.empty()) {
+    return 0;
+  }
+  int32_t class_id = class_name.rbegin()->first + 1;
+  if (class_id >= 0) {
+    return class_id;
+  }
+  // wrapped, pick a random start and do exhaustive search
+  uint32_t upperlimit = numeric_limits<int32_t>::max();
+  upperlimit++;
+  class_id = rand() % upperlimit;
+  const auto start = class_id;
+  do {
+    if (!class_name.count(class_id)) {
+      return class_id;
+    } else {
+      class_id++;
+      if (class_id < 0) {
+        class_id = 0;
+      }
+    }
+  } while (class_id != start);
+  assert(0 == "no available class id");
+}
+
 void CrushWrapper::reweight(CephContext *cct)
 {
   set<int> roots;
-  find_roots(roots);
+  find_roots(&roots);
   for (set<int>::iterator p = roots.begin(); p != roots.end(); ++p) {
     if (*p >= 0)
       continue;
@@ -1300,8 +1624,10 @@ void CrushWrapper::reweight(CephContext *cct)
 int CrushWrapper::add_simple_rule_at(
   string name, string root_name,
   string failure_domain_name,
+  string device_class,
   string mode, int rule_type,
-  int rno, ostream *err)
+  int rno,
+  ostream *err)
 {
   if (rule_exists(name)) {
     if (err)
@@ -1340,6 +1666,22 @@ int CrushWrapper::add_simple_rule_at(
       return -EINVAL;
     }
   }
+  if (device_class.size()) {
+    if (!class_exists(device_class)) {
+      if (err)
+       *err << "device class " << device_class << " does not exist";
+      return -EINVAL;
+    }
+    int c = get_class_id(device_class);
+    if (class_bucket.count(root) == 0 ||
+       class_bucket[root].count(c) == 0) {
+      if (err)
+       *err << "root " << root_name << " has no devices with class "
+            << device_class;
+      return -EINVAL;
+    }
+    root = class_bucket[root][c];
+  }
   if (mode != "firstn" && mode != "indep") {
     if (err)
       *err << "unknown mode " << mode;
@@ -1387,14 +1729,65 @@ int CrushWrapper::add_simple_rule_at(
 int CrushWrapper::add_simple_rule(
   string name, string root_name,
   string failure_domain_name,
+  string device_class,
   string mode, int rule_type,
   ostream *err)
 {
-  return add_simple_rule_at(name, root_name, failure_domain_name, mode,
+  return add_simple_rule_at(name, root_name, failure_domain_name, device_class,
+                           mode,
                            rule_type, -1, err);
 }
 
-int CrushWrapper::get_rule_weight_osd_map(unsigned ruleno, map<int,float> *pmap)
+float CrushWrapper::_get_take_weight_osd_map(int root,
+                                            map<int,float> *pmap) const
+{
+  float sum = 0.0;
+  list<int> q;
+  q.push_back(root);
+  //breadth first iterate the OSD tree
+  while (!q.empty()) {
+    int bno = q.front();
+    q.pop_front();
+    crush_bucket *b = crush->buckets[-1-bno];
+    assert(b);
+    for (unsigned j=0; j<b->size; ++j) {
+      int item_id = b->items[j];
+      if (item_id >= 0) { //it's an OSD
+       float w = crush_get_bucket_item_weight(b, j);
+       (*pmap)[item_id] = w;
+       sum += w;
+      } else { //not an OSD, expand the child later
+       q.push_back(item_id);
+      }
+    }
+  }
+  return sum;
+}
+
+void CrushWrapper::_normalize_weight_map(float sum,
+                                        const map<int,float>& m,
+                                        map<int,float> *pmap) const
+{
+  for (auto& p : m) {
+    map<int,float>::iterator q = pmap->find(p.first);
+    if (q == pmap->end()) {
+      (*pmap)[p.first] = p.second / sum;
+    } else {
+      q->second += p.second / sum;
+    }
+  }
+}
+
+int CrushWrapper::get_take_weight_osd_map(int root, map<int,float> *pmap) const
+{
+  map<int,float> m;
+  float sum = _get_take_weight_osd_map(root, &m);
+  _normalize_weight_map(sum, m, pmap);
+  return 0;
+}
+
+int CrushWrapper::get_rule_weight_osd_map(unsigned ruleno,
+                                         map<int,float> *pmap) const
 {
   if (ruleno >= crush->max_rules)
     return -ENOENT;
@@ -1417,35 +1810,10 @@ int CrushWrapper::get_rule_weight_osd_map(unsigned ruleno, map<int,float> *pmap)
        m[n] = 1.0;
        sum = 1.0;
       } else {
-       list<int> q;
-       q.push_back(n);
-       //breadth first iterate the OSD tree
-       while (!q.empty()) {
-         int bno = q.front();
-         q.pop_front();
-         crush_bucket *b = crush->buckets[-1-bno];
-         assert(b);
-         for (unsigned j=0; j<b->size; ++j) {
-           int item_id = b->items[j];
-           if (item_id >= 0) { //it's an OSD
-             float w = crush_get_bucket_item_weight(b, j);
-             m[item_id] = w;
-             sum += w;
-           } else { //not an OSD, expand the child later
-             q.push_back(item_id);
-           }
-         }
-       }
-      }
-    }
-    for (map<int,float>::iterator p = m.begin(); p != m.end(); ++p) {
-      map<int,float>::iterator q = pmap->find(p->first);
-      if (q == pmap->end()) {
-       (*pmap)[p->first] = p->second / sum;
-      } else {
-       q->second += p->second / sum;
+       sum += _get_take_weight_osd_map(n, &m);
       }
     }
+    _normalize_weight_map(sum, m, pmap);
   }
 
   return 0;
@@ -1461,7 +1829,7 @@ int CrushWrapper::remove_rule(int ruleno)
   crush->rules[ruleno] = NULL;
   rule_name_map.erase(ruleno);
   have_rmaps = false;
-  return 0;
+  return rebuild_roots_with_classes();
 }
 
 int CrushWrapper::bucket_adjust_item_weight(CephContext *cct, crush_bucket *bucket, int item, int weight)
@@ -1472,10 +1840,10 @@ int CrushWrapper::bucket_adjust_item_weight(CephContext *cct, crush_bucket *buck
       if (bucket->items[position] == item)
        break;
     assert(position != bucket->size);
-    for (auto w : choose_args) {
-      crush_choose_arg_map arg_map = w.second;
+    for (auto &w : choose_args) {
+      crush_choose_arg_map &arg_map = w.second;
       crush_choose_arg *arg = &arg_map.args[-1-bucket->id];
-      for (__u32 j = 0; j < arg->weight_set_size; j++) {
+      for (__u32 j = 0; j < arg->weight_set_positions; j++) {
        crush_weight_set *weight_set = &arg->weight_set[j];
        weight_set->weights[position] = weight;
       }
@@ -1484,27 +1852,83 @@ int CrushWrapper::bucket_adjust_item_weight(CephContext *cct, crush_bucket *buck
   return crush_bucket_adjust_item_weight(crush, bucket, item, weight);
 }
 
+int CrushWrapper::add_bucket(
+  int bucketno, int alg, int hash, int type, int size,
+  int *items, int *weights, int *idout)
+{
+  if (alg == 0) {
+    alg = get_default_bucket_alg();
+    if (alg == 0)
+      return -EINVAL;
+  }
+  crush_bucket *b = crush_make_bucket(crush, alg, hash, type, size, items,
+                                     weights);
+  assert(b);
+  assert(idout);
+  int r = crush_add_bucket(crush, bucketno, b, idout);
+  int pos = -1 - *idout;
+  for (auto& p : choose_args) {
+    crush_choose_arg_map& cmap = p.second;
+    if (cmap.args) {
+      if ((int)cmap.size <= pos) {
+       cmap.args = (crush_choose_arg*)realloc(
+         cmap.args,
+         sizeof(crush_choose_arg) * (pos + 1));
+        assert(cmap.args);
+       memset(&cmap.args[cmap.size], 0,
+              sizeof(crush_choose_arg) * (pos + 1 - cmap.size));
+       cmap.size = pos + 1;
+      }
+    } else {
+      cmap.args = (crush_choose_arg*)calloc(sizeof(crush_choose_arg),
+                                           pos + 1);
+      assert(cmap.args);
+      cmap.size = pos + 1;
+    }
+    if (size > 0) {
+      int positions = get_choose_args_positions(cmap);
+      crush_choose_arg& carg = cmap.args[pos];
+      carg.weight_set = (crush_weight_set*)calloc(sizeof(crush_weight_set),
+                                                 size);
+      carg.weight_set_positions = positions;
+      for (int ppos = 0; ppos < positions; ++ppos) {
+       carg.weight_set[ppos].weights = (__u32*)calloc(sizeof(__u32), size);
+       carg.weight_set[ppos].size = size;
+       for (int bpos = 0; bpos < size; ++bpos) {
+         carg.weight_set[ppos].weights[bpos] = weights[bpos];
+       }
+      }
+    }
+  }
+  return r;
+}
+
 int CrushWrapper::bucket_add_item(crush_bucket *bucket, int item, int weight)
 {
   __u32 new_size = bucket->size + 1;
-  for (auto w : choose_args) {
-    crush_choose_arg_map arg_map = w.second;
+  int r = crush_bucket_add_item(crush, bucket, item, weight);
+  if (r < 0) {
+    return r;
+  }
+  for (auto &w : choose_args) {
+    crush_choose_arg_map &arg_map = w.second;
     crush_choose_arg *arg = &arg_map.args[-1-bucket->id];
-    for (__u32 j = 0; j < arg->weight_set_size; j++) {
+    for (__u32 j = 0; j < arg->weight_set_positions; j++) {
       crush_weight_set *weight_set = &arg->weight_set[j];
-      weight_set->weights = (__u32*)realloc(weight_set->weights, new_size * sizeof(__u32));
+      weight_set->weights = (__u32*)realloc(weight_set->weights,
+                                           new_size * sizeof(__u32));
       assert(weight_set->size + 1 == new_size);
       weight_set->weights[weight_set->size] = weight;
       weight_set->size = new_size;
     }
     if (arg->ids_size) {
-      arg->ids = (int*)realloc(arg->ids, new_size * sizeof(int));
+      arg->ids = (__s32 *)realloc(arg->ids, new_size * sizeof(__s32));
       assert(arg->ids_size + 1 == new_size);
       arg->ids[arg->ids_size] = item;
       arg->ids_size = new_size;
     }
   }
-  return crush_bucket_add_item(crush, bucket, item, weight);
+  return 0;
 }
 
 int CrushWrapper::bucket_remove_item(crush_bucket *bucket, int item)
@@ -1515,43 +1939,74 @@ int CrushWrapper::bucket_remove_item(crush_bucket *bucket, int item)
     if (bucket->items[position] == item)
       break;
   assert(position != bucket->size);
-  for (auto w : choose_args) {
-    crush_choose_arg_map arg_map = w.second;
+  int r = crush_bucket_remove_item(crush, bucket, item);
+  if (r < 0) {
+    return r;
+  }
+  for (auto &w : choose_args) {
+    crush_choose_arg_map &arg_map = w.second;
     crush_choose_arg *arg = &arg_map.args[-1-bucket->id];
-    for (__u32 j = 0; j < arg->weight_set_size; j++) {
+    for (__u32 j = 0; j < arg->weight_set_positions; j++) {
       crush_weight_set *weight_set = &arg->weight_set[j];
       assert(weight_set->size - 1 == new_size);
       for (__u32 k = position; k < new_size; k++)
        weight_set->weights[k] = weight_set->weights[k+1];
-      weight_set->weights = (__u32*)realloc(weight_set->weights, new_size * sizeof(__u32));
+      if (new_size) {
+       weight_set->weights = (__u32*)realloc(weight_set->weights,
+                                             new_size * sizeof(__u32));
+      } else {
+       weight_set->weights = NULL;
+      }
       weight_set->size = new_size;
     }
     if (arg->ids_size) {
       assert(arg->ids_size - 1 == new_size);
       for (__u32 k = position; k < new_size; k++)
        arg->ids[k] = arg->ids[k+1];
-      arg->ids = (int*)realloc(arg->ids, new_size * sizeof(int));
+      if (new_size) {
+       arg->ids = (__s32 *)realloc(arg->ids, new_size * sizeof(__s32));
+      } else {
+       arg->ids = NULL;
+      }
       arg->ids_size = new_size;
     }
   }
-  return crush_bucket_remove_item(crush, bucket, item);
+  return 0;
 }
 
-int CrushWrapper::update_device_class(CephContext *cct, int id, const string& class_name, const string& name)
+int CrushWrapper::bucket_set_alg(int bid, int alg)
 {
-  int class_id = get_class_id(class_name);
-  if (class_id < 0) {
-    ldout(cct, 0) << "update_device_class class " << class_name << " does not exist " << dendl;
+  crush_bucket *b = get_bucket(bid);
+  if (!b) {
     return -ENOENT;
   }
+  b->alg = alg;
+  return 0;
+}
+
+int CrushWrapper::update_device_class(int id,
+                                      const string& class_name,
+                                      const string& name,
+                                      ostream *ss)
+{
+  assert(item_exists(id));
+  auto old_class_name = get_item_class(id);
+  if (old_class_name && old_class_name != class_name) {
+    *ss << "osd." << id << " has already bound to class '" << old_class_name
+        << "', can not reset class to '" << class_name  << "'; "
+        << "use 'ceph osd crush rm-device-class <osd>' to "
+        << "remove old class first";
+    return -EBUSY;
+  }
+
+  int class_id = get_or_create_class_id(class_name);
   if (id < 0) {
-    ldout(cct, 0) << "update_device_class " << name << " id " << id << " is negative " << dendl;
+    *ss << name << " id " << id << " is negative";
     return -EINVAL;
   }
-  assert(item_exists(id));
 
   if (class_map.count(id) != 0 && class_map[id] == class_id) {
-    ldout(cct, 5) << "update_device_class " << name << " already set to class " << class_name << dendl;
+    *ss << name << " already set to class " << class_name;
     return 0;
   }
 
@@ -1563,7 +2018,37 @@ int CrushWrapper::update_device_class(CephContext *cct, int id, const string& cl
   return 1;
 }
 
-int CrushWrapper::device_class_clone(int original_id, int device_class, int *clone)
+int CrushWrapper::remove_device_class(CephContext *cct, int id, ostream *ss)
+{
+  assert(ss);
+  const char *name = get_item_name(id);
+  if (!name) {
+    *ss << "osd." << id << " does not have a name";
+    return -ENOENT;
+  }
+
+  const char *class_name = get_item_class(id);
+  if (!class_name) {
+    *ss << "osd." << id << " has not been bound to a specific class yet";
+    return 0;
+  }
+  class_remove_item(id);
+
+  int r = rebuild_roots_with_classes();
+  if (r < 0) {
+    *ss << "unable to rebuild roots with class '" << class_name << "' "
+        << "of osd." << id << ": " << cpp_strerror(r);
+    return r;
+  }
+  return 0;
+}
+
+int CrushWrapper::device_class_clone(
+  int original_id, int device_class,
+  const std::map<int32_t, map<int32_t, int32_t>>& old_class_bucket,
+  const std::set<int32_t>& used_ids,
+  int *clone,
+  map<int,map<int,vector<int>>> *cmap_item_weight)
 {
   const char *item_name = get_item_name(original_id);
   if (item_name == NULL)
@@ -1576,61 +2061,234 @@ int CrushWrapper::device_class_clone(int original_id, int device_class, int *clo
     *clone = get_item_id(copy_name);
     return 0;
   }
+
   crush_bucket *original = get_bucket(original_id);
-  if (original == NULL)
-    return -ENOENT;
+  assert(!IS_ERR(original));
   crush_bucket *copy = crush_make_bucket(crush,
                                         original->alg,
                                         original->hash,
                                         original->type,
                                         0, NULL, NULL);
-  if(copy == NULL)
-    return -ENOMEM;
+  assert(copy);
+
+  vector<unsigned> item_orig_pos;  // new item pos -> orig item pos
   for (unsigned i = 0; i < original->size; i++) {
     int item = original->items[i];
     int weight = crush_get_bucket_item_weight(original, i);
     if (item >= 0) {
       if (class_map.count(item) != 0 && class_map[item] == device_class) {
-       int res = bucket_add_item(copy, item, weight);
+       int res = crush_bucket_add_item(crush, copy, item, weight);
        if (res)
          return res;
+      } else {
+       continue;
       }
     } else {
       int child_copy_id;
-      int res = device_class_clone(item, device_class, &child_copy_id);
+      int res = device_class_clone(item, device_class, old_class_bucket,
+                                  used_ids, &child_copy_id,
+                                  cmap_item_weight);
       if (res < 0)
        return res;
       crush_bucket *child_copy = get_bucket(child_copy_id);
-      if (IS_ERR(child_copy))
-       return -ENOENT;
-      res = bucket_add_item(copy, child_copy_id, child_copy->weight);
+      assert(!IS_ERR(child_copy));
+      res = crush_bucket_add_item(crush, copy, child_copy_id,
+                                 child_copy->weight);
       if (res)
        return res;
     }
+    item_orig_pos.push_back(i);
+  }
+  assert(item_orig_pos.size() == copy->size);
+
+  int bno = 0;
+  if (old_class_bucket.count(original_id) &&
+      old_class_bucket.at(original_id).count(device_class)) {
+    bno = old_class_bucket.at(original_id).at(device_class);
+  } else {
+    // pick a new shadow bucket id that is not used by the current map
+    // *or* any previous shadow buckets.
+    bno = -1;
+    while (((-1-bno) < crush->max_buckets && crush->buckets[-1-bno]) ||
+          used_ids.count(bno)) {
+      --bno;
+    }
   }
-  int res = crush_add_bucket(crush, 0, copy, clone);
+  int res = crush_add_bucket(crush, bno, copy, clone);
   if (res)
     return res;
+  assert(!bno || bno == *clone);
+
   res = set_item_class(*clone, device_class);
   if (res < 0)
     return res;
+
   // we do not use set_item_name because the name is intentionally invalid
   name_map[*clone] = copy_name;
   if (have_rmaps)
     name_rmap[copy_name] = *clone;
   class_bucket[original_id][device_class] = *clone;
+
+  // set up choose_args for the new bucket.
+  for (auto& w : choose_args) {
+    crush_choose_arg_map& cmap = w.second;
+    if (-1-bno >= (int)cmap.size) {
+      unsigned new_size = -1-bno + 1;
+      cmap.args = (crush_choose_arg*)realloc(cmap.args,
+                                            new_size * sizeof(cmap.args[0]));
+      assert(cmap.args);
+      memset(cmap.args + cmap.size, 0,
+            (new_size - cmap.size) * sizeof(cmap.args[0]));
+      cmap.size = new_size;
+    }
+    auto& o = cmap.args[-1-original_id];
+    auto& n = cmap.args[-1-bno];
+    n.ids_size = 0; // FIXME: implement me someday
+    n.weight_set_positions = o.weight_set_positions;
+    n.weight_set = (crush_weight_set*)calloc(
+      n.weight_set_positions, sizeof(crush_weight_set));
+    for (size_t s = 0; s < n.weight_set_positions; ++s) {
+      n.weight_set[s].size = copy->size;
+      n.weight_set[s].weights = (__u32*)calloc(copy->size, sizeof(__u32));
+    }
+    for (size_t s = 0; s < n.weight_set_positions; ++s) {
+      vector<int> bucket_weights(n.weight_set_positions);
+      for (size_t i = 0; i < copy->size; ++i) {
+       int item = copy->items[i];
+       if (item >= 0) {
+         n.weight_set[s].weights[i] = o.weight_set[s].weights[item_orig_pos[i]];
+       } else if ((*cmap_item_weight)[w.first].count(item)) {
+         n.weight_set[s].weights[i] = (*cmap_item_weight)[w.first][item][s];
+       } else {
+         n.weight_set[s].weights[i] = 0;
+       }
+       bucket_weights[s] += n.weight_set[s].weights[i];
+      }
+      (*cmap_item_weight)[w.first][bno] = bucket_weights;
+    }
+  }
+  return 0;
+}
+
+int CrushWrapper::get_rules_by_class(const string &class_name, set<int> *rules)
+{
+  assert(rules);
+  rules->clear();
+  if (!class_exists(class_name)) {
+    return -ENOENT;
+  }
+  int class_id = get_class_id(class_name);
+  for (unsigned i = 0; i < crush->max_rules; ++i) {
+    crush_rule *r = crush->rules[i];
+    if (!r)
+      continue;
+    for (unsigned j = 0; j < r->len; ++j) {
+      if (r->steps[j].op == CRUSH_RULE_TAKE) {
+        int step_item = r->steps[j].arg1;
+        int original_item;
+        int c;
+        int res = split_id_class(step_item, &original_item, &c);
+        if (res < 0) {
+          return res;
+        }
+        if (c != -1 && c == class_id) {
+          rules->insert(i);
+          break;
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+// return rules that might reference the given osd
+int CrushWrapper::get_rules_by_osd(int osd, set<int> *rules)
+{
+  assert(rules);
+  rules->clear();
+  if (osd < 0) {
+    return -EINVAL;
+  }
+  for (unsigned i = 0; i < crush->max_rules; ++i) {
+    crush_rule *r = crush->rules[i];
+    if (!r)
+      continue;
+    for (unsigned j = 0; j < r->len; ++j) {
+      if (r->steps[j].op == CRUSH_RULE_TAKE) {
+        int step_item = r->steps[j].arg1;
+        list<int> unordered;
+        int rc = _get_leaves(step_item, &unordered);
+        if (rc < 0) {
+          return rc; // propagate fatal errors!
+        }
+        bool match = false;
+        for (auto &o: unordered) {
+          assert(o >= 0);
+          if (o == osd) {
+            match = true;
+            break;
+          }
+        }
+        if (match) {
+          rules->insert(i);
+          break;
+        }
+      }
+    }
+  }
   return 0;
 }
 
+bool CrushWrapper::_class_is_dead(int class_id)
+{
+  for (auto &p: class_map) {
+    if (p.first >= 0 && p.second == class_id) {
+      return false;
+    }
+  }
+  for (unsigned i = 0; i < crush->max_rules; ++i) {
+    crush_rule *r = crush->rules[i];
+    if (!r)
+      continue;
+    for (unsigned j = 0; j < r->len; ++j) {
+      if (r->steps[j].op == CRUSH_RULE_TAKE) {
+        int root = r->steps[j].arg1;
+        for (auto &p : class_bucket) {
+          auto& q = p.second;
+          if (q.count(class_id) && q[class_id] == root) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  // no more referenced by any devices or crush rules
+  return true;
+}
+
+void CrushWrapper::cleanup_dead_classes()
+{
+  auto p = class_name.begin();
+  while (p != class_name.end()) {
+    if (_class_is_dead(p->first)) {
+      string n = p->second;
+      ++p;
+      remove_class_name(n);
+    } else {
+      ++p;
+    }
+  }
+}
+
 int CrushWrapper::rebuild_roots_with_classes()
 {
-  int r = trim_roots_with_class(false);
+  std::map<int32_t, map<int32_t, int32_t> > old_class_bucket = class_bucket;
+  cleanup_dead_classes();
+  int r = trim_roots_with_class();
   if (r < 0)
     return r;
-  r = populate_classes();
-  if (r < 0)
-    return r;
-  return trim_roots_with_class(true);
+  class_bucket.clear();
+  return populate_classes(old_class_bucket);
 }
 
 void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
@@ -1700,7 +2358,7 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
       {
        __u32 *weights;
        if (encode_compat_choose_args &&
-           arg_map.args[i].weight_set_size > 0) {
+           arg_map.args[i].weight_set_positions > 0) {
          weights = arg_map.args[i].weight_set[0].weights;
        } else {
          weights = (reinterpret_cast<crush_bucket_straw2*>(crush->buckets[i]))->item_weights;
@@ -1753,14 +2411,16 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
     ::encode(class_name, bl);
     ::encode(class_bucket, bl);
 
-    ::encode(choose_args.size(), bl);
+    // choose args
+    __u32 size = (__u32)choose_args.size();
+    ::encode(size, bl);
     for (auto c : choose_args) {
       ::encode(c.first, bl);
       crush_choose_arg_map arg_map = c.second;
-      __u32 size = 0;
+      size = 0;
       for (__u32 i = 0; i < arg_map.size; i++) {
        crush_choose_arg *arg = &arg_map.args[i];
-       if (arg->weight_set_size == 0 &&
+       if (arg->weight_set_positions == 0 &&
            arg->ids_size == 0)
          continue;
        size++;
@@ -1768,12 +2428,12 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
       ::encode(size, bl);
       for (__u32 i = 0; i < arg_map.size; i++) {
        crush_choose_arg *arg = &arg_map.args[i];
-       if (arg->weight_set_size == 0 &&
+       if (arg->weight_set_positions == 0 &&
            arg->ids_size == 0)
          continue;
        ::encode(i, bl);
-       ::encode(arg->weight_set_size, bl);
-       for (__u32 j = 0; j < arg->weight_set_size; j++) {
+       ::encode(arg->weight_set_positions, bl);
+       for (__u32 j = 0; j < arg->weight_set_positions; j++) {
          crush_weight_set *weight_set = &arg->weight_set[j];
          ::encode(weight_set->size, bl);
          for (__u32 k = 0; k < weight_set->size; k++)
@@ -1883,17 +2543,17 @@ void CrushWrapper::decode(bufferlist::iterator& blp)
       for (auto &c : class_name)
        class_rname[c.second] = c.first;
       ::decode(class_bucket, blp);
-      cleanup_classes();
     }
     if (!blp.end()) {
-      size_t choose_args_size;
+      __u32 choose_args_size;
       ::decode(choose_args_size, blp);
-      for (size_t i = 0; i < choose_args_size; i++) {
-       uint64_t choose_args_index;
+      for (__u32 i = 0; i < choose_args_size; i++) {
+        typename decltype(choose_args)::key_type choose_args_index;
        ::decode(choose_args_index, blp);
        crush_choose_arg_map arg_map;
        arg_map.size = crush->max_buckets;
-       arg_map.args = (crush_choose_arg*)calloc(arg_map.size, sizeof(crush_choose_arg));
+       arg_map.args = (crush_choose_arg*)calloc(
+         arg_map.size, sizeof(crush_choose_arg));
        __u32 size;
        ::decode(size, blp);
        for (__u32 j = 0; j < size; j++) {
@@ -1901,23 +2561,31 @@ void CrushWrapper::decode(bufferlist::iterator& blp)
          ::decode(bucket_index, blp);
          assert(bucket_index < arg_map.size);
          crush_choose_arg *arg = &arg_map.args[bucket_index];
-         ::decode(arg->weight_set_size, blp);
-         arg->weight_set = (crush_weight_set*)calloc(arg->weight_set_size, sizeof(crush_weight_set));
-         for (__u32 k = 0; k < arg->weight_set_size; k++) {
-           crush_weight_set *weight_set = &arg->weight_set[k];
-           ::decode(weight_set->size, blp);
-           weight_set->weights = (__u32*)calloc(weight_set->size, sizeof(__u32));
-           for (__u32 l = 0; l < weight_set->size; l++)
-             ::decode(weight_set->weights[l], blp);
+         ::decode(arg->weight_set_positions, blp);
+         if (arg->weight_set_positions) {
+           arg->weight_set = (crush_weight_set*)calloc(
+             arg->weight_set_positions, sizeof(crush_weight_set));
+           for (__u32 k = 0; k < arg->weight_set_positions; k++) {
+             crush_weight_set *weight_set = &arg->weight_set[k];
+             ::decode(weight_set->size, blp);
+             weight_set->weights = (__u32*)calloc(
+               weight_set->size, sizeof(__u32));
+             for (__u32 l = 0; l < weight_set->size; l++)
+               ::decode(weight_set->weights[l], blp);
+           }
          }
          ::decode(arg->ids_size, blp);
-         arg->ids = (int*)calloc(arg->ids_size, sizeof(int));
-         for (__u32 k = 0; k < arg->ids_size; k++)
-           ::decode(arg->ids[k], blp);
+         if (arg->ids_size) {
+           assert(arg->ids_size == crush->buckets[bucket_index]->size);
+           arg->ids = (__s32 *)calloc(arg->ids_size, sizeof(__s32));
+           for (__u32 k = 0; k < arg->ids_size; k++)
+             ::decode(arg->ids[k], blp);
+         }
        }
        choose_args[choose_args_index] = arg_map;
       }
     }
+    update_choose_args(nullptr); // in case we decode a legacy "corrupted" map
     finalize();
   }
   catch (...) {
@@ -2114,15 +2782,17 @@ namespace {
   class TreeDumper {
     typedef CrushTreeDumper::Item Item;
     const CrushWrapper *crush;
+    const CrushTreeDumper::name_map_t& weight_set_names;
   public:
-    explicit TreeDumper(const CrushWrapper *crush)
-      : crush(crush) {}
+    explicit TreeDumper(const CrushWrapper *crush,
+                       const CrushTreeDumper::name_map_t& wsnames)
+      : crush(crush), weight_set_names(wsnames) {}
 
     void dump(Formatter *f) {
       set<int> roots;
-      crush->find_roots(roots);
+      crush->find_roots(&roots);
       for (set<int>::iterator root = roots.begin(); root != roots.end(); ++root) {
-       dump_item(Item(*root, 0, crush->get_bucket_weightf(*root)), f);
+       dump_item(Item(*root, 0, 0, crush->get_bucket_weightf(*root)), f);
       }
     }
 
@@ -2130,12 +2800,12 @@ namespace {
     void dump_item(const Item& qi, Formatter* f) {
       if (qi.is_bucket()) {
        f->open_object_section("bucket");
-       CrushTreeDumper::dump_item_fields(crush, qi, f);
+       CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f);
        dump_bucket_children(qi, f);
        f->close_section();
       } else {
        f->open_object_section("device");
-       CrushTreeDumper::dump_item_fields(crush, qi, f);
+       CrushTreeDumper::dump_item_fields(crush, weight_set_names, qi, f);
        f->close_section();
       }
     }
@@ -2146,17 +2816,19 @@ namespace {
       for (int pos = 0; pos < max_pos; pos++) {
        int id = crush->get_bucket_item(parent.id, pos);
        float weight = crush->get_bucket_item_weightf(parent.id, pos);
-       dump_item(Item(id, parent.depth + 1, weight), f);
+       dump_item(Item(id, parent.id, parent.depth + 1, weight), f);
       }
       f->close_section();
     }
   };
 }
 
-void CrushWrapper::dump_tree(Formatter *f) const
+void CrushWrapper::dump_tree(
+  Formatter *f,
+  const CrushTreeDumper::name_map_t& weight_set_names) const
 {
   assert(f);
-  TreeDumper(this).dump(f);
+  TreeDumper(this, weight_set_names).dump(f);
 }
 
 void CrushWrapper::dump_tunables(Formatter *f) const
@@ -2207,15 +2879,15 @@ void CrushWrapper::dump_choose_args(Formatter *f) const
     f->open_array_section(stringify(c.first).c_str());
     for (__u32 i = 0; i < arg_map.size; i++) {
       crush_choose_arg *arg = &arg_map.args[i];
-      if (arg->weight_set_size == 0 &&
+      if (arg->weight_set_positions == 0 &&
          arg->ids_size == 0)
        continue;
       f->open_object_section("choose_args");
       int bucket_index = i;
       f->dump_int("bucket_id", -1-bucket_index);
-      if (arg->weight_set_size > 0) {
+      if (arg->weight_set_positions > 0) {
        f->open_array_section("weight_set");
-       for (__u32 j = 0; j < arg->weight_set_size; j++) {
+       for (__u32 j = 0; j < arg->weight_set_positions; j++) {
          f->open_array_section("weights");
          __u32 *weights = arg->weight_set[j].weights;
          __u32 size = arg->weight_set[j].size;
@@ -2326,36 +2998,88 @@ void CrushWrapper::list_rules(Formatter *f) const
   }
 }
 
-class CrushTreePlainDumper : public CrushTreeDumper::Dumper<ostream> {
-public:
-  typedef CrushTreeDumper::Dumper<ostream> Parent;
-
-  explicit CrushTreePlainDumper(const CrushWrapper *crush)
-    : Parent(crush) {}
+void CrushWrapper::list_rules(ostream *ss) const
+{
+  for (int rule = 0; rule < get_max_rules(); rule++) {
+    if (!rule_exists(rule))
+      continue;
+    *ss << get_rule_name(rule) << "\n";
+  }
+}
 
-  void dump(ostream *out) {
-    *out << "ID\tWEIGHT\tTYPE NAME\n";
-    Parent::dump(out);
+class CrushTreePlainDumper : public CrushTreeDumper::Dumper<TextTable> {
+public:
+  typedef CrushTreeDumper::Dumper<TextTable> Parent;
+
+  explicit CrushTreePlainDumper(const CrushWrapper *crush,
+                               const CrushTreeDumper::name_map_t& wsnames)
+    : Parent(crush, wsnames) {}
+  explicit CrushTreePlainDumper(const CrushWrapper *crush,
+                                const CrushTreeDumper::name_map_t& wsnames,
+                                bool show_shadow)
+    : Parent(crush, wsnames, show_shadow) {}
+
+
+  void dump(TextTable *tbl) {
+    tbl->define_column("ID", TextTable::LEFT, TextTable::RIGHT);
+    tbl->define_column("CLASS", TextTable::LEFT, TextTable::RIGHT);
+    tbl->define_column("WEIGHT", TextTable::LEFT, TextTable::RIGHT);
+    for (auto& p : crush->choose_args) {
+      if (p.first == CrushWrapper::DEFAULT_CHOOSE_ARGS) {
+       tbl->define_column("(compat)", TextTable::LEFT, TextTable::RIGHT);
+      } else {
+       string name;
+       auto q = weight_set_names.find(p.first);
+       name = q != weight_set_names.end() ? q->second :
+         stringify(p.first);
+       tbl->define_column(name.c_str(), TextTable::LEFT, TextTable::RIGHT);
+      }
+    }
+    tbl->define_column("TYPE NAME", TextTable::LEFT, TextTable::LEFT);
+    Parent::dump(tbl);
   }
 
 protected:
-  void dump_item(const CrushTreeDumper::Item &qi, ostream *out) override {
-    *out << qi.id << "\t"
-        << weightf_t(qi.weight) << "\t";
-
-    for (int k=0; k < qi.depth; k++)
-      *out << "\t";
-
-    if (qi.is_bucket())
-    {
-      *out << crush->get_type_name(crush->get_bucket_type(qi.id)) << " "
-          << crush->get_item_name(qi.id);
+  void dump_item(const CrushTreeDumper::Item &qi, TextTable *tbl) override {
+    const char *c = crush->get_item_class(qi.id);
+    if (!c)
+      c = "";
+    *tbl << qi.id
+        << c
+        << weightf_t(qi.weight);
+    for (auto& p : crush->choose_args) {
+      if (qi.parent < 0) {
+       const crush_choose_arg_map cmap = crush->choose_args_get(p.first);
+       int bidx = -1 - qi.parent;
+       const crush_bucket *b = crush->get_bucket(qi.parent);
+       if (b &&
+           bidx < (int)cmap.size &&
+           cmap.args[bidx].weight_set &&
+           cmap.args[bidx].weight_set_positions >= 1) {
+         int pos;
+         for (pos = 0;
+              pos < (int)cmap.args[bidx].weight_set[0].size &&
+                b->items[pos] != qi.id;
+              ++pos) ;
+         *tbl << weightf_t((float)cmap.args[bidx].weight_set[0].weights[pos] /
+                           (float)0x10000);
+         continue;
+       }
+      }
+      *tbl << "";
     }
-    else
-    {
-      *out << "osd." << qi.id;
+    ostringstream ss;
+    for (int k=0; k < qi.depth; k++) {
+      ss << "    ";
+    }
+    if (qi.is_bucket()) {
+      ss << crush->get_type_name(crush->get_bucket_type(qi.id)) << " "
+        << crush->get_item_name(qi.id);
+    } else {
+      ss << "osd." << qi.id;
     }
-    *out << "\n";
+    *tbl << ss.str();
+    *tbl << TextTable::endrow;
   }
 };
 
@@ -2364,8 +3088,16 @@ class CrushTreeFormattingDumper : public CrushTreeDumper::FormattingDumper {
 public:
   typedef CrushTreeDumper::FormattingDumper Parent;
 
-  explicit CrushTreeFormattingDumper(const CrushWrapper *crush)
-    : Parent(crush) {}
+  explicit CrushTreeFormattingDumper(
+    const CrushWrapper *crush,
+    const CrushTreeDumper::name_map_t& wsnames)
+    : Parent(crush, wsnames) {}
+
+  explicit CrushTreeFormattingDumper(
+    const CrushWrapper *crush,
+    const CrushTreeDumper::name_map_t& wsnames,
+    bool show_shadow)
+    : Parent(crush, wsnames, show_shadow) {}
 
   void dump(Formatter *f) {
     f->open_array_section("nodes");
@@ -2377,12 +3109,20 @@ public:
 };
 
 
-void CrushWrapper::dump_tree(ostream *out, Formatter *f) const
+void CrushWrapper::dump_tree(
+  ostream *out,
+  Formatter *f,
+  const CrushTreeDumper::name_map_t& weight_set_names,
+  bool show_shadow) const
 {
-  if (out)
-    CrushTreePlainDumper(this).dump(out);
-  if (f)
-    CrushTreeFormattingDumper(this).dump(f);
+  if (out) {
+    TextTable tbl;
+    CrushTreePlainDumper(this, weight_set_names, show_shadow).dump(&tbl);
+    *out << tbl;
+  }
+  if (f) {
+    CrushTreeFormattingDumper(this, weight_set_names, show_shadow).dump(f);
+  }
 }
 
 void CrushWrapper::generate_test_instances(list<CrushWrapper*>& o)
@@ -2500,6 +3240,10 @@ int CrushWrapper::_choose_type_stack(
                   << " w " << w << dendl;
     vector<int> o;
     auto tmpi = i;
+    if (i == orig.end()) {
+      ldout(cct, 10) << __func__ << " end of orig, break 0" << dendl;
+      break;
+    }
     for (auto from : w) {
       ldout(cct, 10) << " from " << from << dendl;
       // identify leaves under each choice.  we use this to check whether any of these
@@ -2543,6 +3287,7 @@ int CrushWrapper::_choose_type_stack(
              ldout(cct, 10) << __func__ << " pos " << pos << " replace "
                             << *i << " -> " << item << dendl;
              replaced = true;
+              assert(i != orig.end());
              ++i;
              break;
            }
@@ -2550,6 +3295,7 @@ int CrushWrapper::_choose_type_stack(
          if (!replaced) {
            ldout(cct, 10) << __func__ << " pos " << pos << " keep " << *i
                           << dendl;
+            assert(i != orig.end());
            o.push_back(*i);
            ++i;
          }
@@ -2664,7 +3410,8 @@ int CrushWrapper::try_remap_rule(
        if (numrep <= 0)
          numrep += maxout;
        type_stack.push_back(make_pair(type, numrep));
-       type_stack.push_back(make_pair(0, 1));
+        if (type > 0)
+         type_stack.push_back(make_pair(0, 1));
        int r = _choose_type_stack(cct, type_stack, overfull, underfull, orig,
                                   i, used, &w);
        if (r < 0)
@@ -2707,3 +3454,93 @@ int CrushWrapper::try_remap_rule(
 
   return 0;
 }
+
+
+int CrushWrapper::_choose_args_adjust_item_weight_in_bucket(
+  CephContext *cct,
+  crush_choose_arg_map cmap,
+  int bucketid,
+  int id,
+  const vector<int>& weight,
+  ostream *ss)
+{
+  int changed = 0;
+  int bidx = -1 - bucketid;
+  crush_bucket *b = crush->buckets[bidx];
+  if (bidx >= (int)cmap.size) {
+    if (ss)
+      *ss << "no weight-set for bucket " << b->id;
+    ldout(cct, 10) << __func__ << "  no crush_choose_arg for bucket " << b->id
+                  << dendl;
+    return 0;
+  }
+  crush_choose_arg *carg = &cmap.args[bidx];
+  if (carg->weight_set == NULL) {
+    // create a weight-set for this bucket and populate it with the
+    // bucket weights
+    unsigned positions = get_choose_args_positions(cmap);
+    carg->weight_set_positions = positions;
+    carg->weight_set = static_cast<crush_weight_set*>(
+      calloc(sizeof(crush_weight_set), positions));
+    for (unsigned p = 0; p < positions; ++p) {
+      carg->weight_set[p].size = b->size;
+      carg->weight_set[p].weights = (__u32*)calloc(b->size, sizeof(__u32));
+      for (unsigned i = 0; i < b->size; ++i) {
+       carg->weight_set[p].weights[i] = crush_get_bucket_item_weight(b, i);
+      }
+    }
+    changed++;
+  }
+  if (carg->weight_set_positions != weight.size()) {
+    if (ss)
+      *ss << "weight_set_positions != " << weight.size() << " for bucket " << b->id;
+    ldout(cct, 10) << __func__ << "  weight_set_positions != " << weight.size()
+                  << " for bucket " << b->id << dendl;
+    return 0;
+  }
+  for (unsigned i = 0; i < b->size; i++) {
+    if (b->items[i] == id) {
+      for (unsigned j = 0; j < weight.size(); ++j) {
+       carg->weight_set[j].weights[i] = weight[j];
+      }
+      ldout(cct, 5) << __func__ << "  set " << id << " to " << weight
+                   << " in bucket " << b->id << dendl;
+      changed++;
+    }
+  }
+  if (changed) {
+    vector<int> bucket_weight(weight.size(), 0);
+    for (unsigned i = 0; i < b->size; i++) {
+      for (unsigned j = 0; j < weight.size(); ++j) {
+       bucket_weight[j] += carg->weight_set[j].weights[i];
+      }
+    }
+    choose_args_adjust_item_weight(cct, cmap, b->id, bucket_weight, nullptr);
+  }
+  return changed;
+}
+
+int CrushWrapper::choose_args_adjust_item_weight(
+  CephContext *cct,
+  crush_choose_arg_map cmap,
+  int id,
+  const vector<int>& weight,
+  ostream *ss)
+{
+  ldout(cct, 5) << __func__ << " " << id << " weight " << weight << dendl;
+  int changed = 0;
+  for (int bidx = 0; bidx < crush->max_buckets; bidx++) {
+    crush_bucket *b = crush->buckets[bidx];
+    if (b == nullptr) {
+      continue;
+    }
+    changed += _choose_args_adjust_item_weight_in_bucket(
+      cct, cmap, b->id, id, weight, ss);
+  }
+  if (!changed) {
+    if (ss)
+      *ss << "item " << id << " not found in crush map";
+    return -ENOENT;
+  }
+  return changed;
+}