]> 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 76c6afe60a5ce32535af0c088f30f4d2a0d86998..b85c7409945366920e523fe71d206d4a4f0b55a7 100644 (file)
@@ -13,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];
@@ -25,51 +25,17 @@ 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;
-}
-
-bool CrushWrapper::has_multirule_rulesets() const
-{
-  for (unsigned i=0; i<crush->max_rules; i++) {
-    crush_rule *r = crush->rules[i];
-    if (!r)
-      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;
-    }
-  }
-  return false;
+  return result;
 }
 
 bool CrushWrapper::has_non_straw2_buckets() const
@@ -193,10 +159,10 @@ bool CrushWrapper::has_incompat_choose_args() const
   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;
@@ -291,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];
@@ -299,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);
   }
 }
 
@@ -351,6 +357,7 @@ bool CrushWrapper::_maybe_remove_last_instance(CephContext *cct, int item, bool
     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;
@@ -393,9 +400,73 @@ int CrushWrapper::remove_root(int item)
   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
@@ -673,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;
 }
 
@@ -777,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);
@@ -1075,7 +1195,7 @@ 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(
@@ -1350,15 +1470,33 @@ 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
 }
 
 int CrushWrapper::rename_class(const string& srcname, const string& dstname)
@@ -1410,9 +1548,9 @@ int CrushWrapper::populate_classes(
   // 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 -> weights
+  map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> [bucket weight for each position]
   set<int> roots;
-  find_nonshadow_roots(roots);
+  find_nonshadow_roots(&roots);
   for (auto &r : roots) {
     if (r >= 0)
       continue;
@@ -1430,7 +1568,7 @@ int CrushWrapper::populate_classes(
 int CrushWrapper::trim_roots_with_class()
 {
   set<int> roots;
-  find_shadow_roots(roots);
+  find_shadow_roots(&roots);
   for (auto &r : roots) {
     if (r >= 0)
       continue;
@@ -1472,7 +1610,7 @@ int32_t CrushWrapper::_alloc_class_id() const {
 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;
@@ -1600,7 +1738,56 @@ int CrushWrapper::add_simple_rule(
                            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;
@@ -1623,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;
@@ -1667,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)
@@ -1678,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;
       }
@@ -1702,29 +1864,33 @@ int CrushWrapper::add_bucket(
   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 <= *idout) {
+      if ((int)cmap.size <= pos) {
        cmap.args = (crush_choose_arg*)realloc(
          cmap.args,
-         sizeof(crush_choose_arg) * (*idout + 1));
+         sizeof(crush_choose_arg) * (pos + 1));
+        assert(cmap.args);
        memset(&cmap.args[cmap.size], 0,
-              sizeof(crush_choose_arg) * (*idout + 1 - cmap.size));
-       cmap.size = *idout + 1;
+              sizeof(crush_choose_arg) * (pos + 1 - cmap.size));
+       cmap.size = pos + 1;
       }
     } else {
       cmap.args = (crush_choose_arg*)calloc(sizeof(crush_choose_arg),
-                                           *idout + 1);
-      cmap.size = *idout + 1;
+                                           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[*idout];
+      crush_choose_arg& carg = cmap.args[pos];
       carg.weight_set = (crush_weight_set*)calloc(sizeof(crush_weight_set),
                                                  size);
-      carg.weight_set_size = positions;
+      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;
@@ -1744,10 +1910,10 @@ int CrushWrapper::bucket_add_item(crush_bucket *bucket, int item, int weight)
   if (r < 0) {
     return r;
   }
-  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 = (__u32*)realloc(weight_set->weights,
                                            new_size * sizeof(__u32));
@@ -1777,10 +1943,10 @@ int CrushWrapper::bucket_remove_item(crush_bucket *bucket, int item)
   if (r < 0) {
     return r;
   }
-  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];
       assert(weight_set->size - 1 == new_size);
       for (__u32 k = position; k < new_size; k++)
@@ -1808,6 +1974,16 @@ int CrushWrapper::bucket_remove_item(crush_bucket *bucket, int item)
   return 0;
 }
 
+int CrushWrapper::bucket_set_alg(int bid, int alg)
+{
+  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,
@@ -1960,27 +2136,31 @@ int CrushWrapper::device_class_clone(
       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_size = o.weight_set_size;
+    n.weight_set_positions = o.weight_set_positions;
     n.weight_set = (crush_weight_set*)calloc(
-      n.weight_set_size, sizeof(crush_weight_set));
-    for (size_t s = 0; s < n.weight_set_size; ++s) {
+      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_size; ++s) {
-      vector<int> bucket_weights(n.weight_set_size);
+    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 {
+       } 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];
       }
@@ -1990,6 +2170,75 @@ int CrushWrapper::device_class_clone(
   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) {
@@ -2109,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;
@@ -2171,7 +2420,7 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
       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++;
@@ -2179,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++)
@@ -2299,7 +2548,7 @@ void CrushWrapper::decode(bufferlist::iterator& blp)
       __u32 choose_args_size;
       ::decode(choose_args_size, blp);
       for (__u32 i = 0; i < choose_args_size; i++) {
-       uint64_t choose_args_index;
+        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;
@@ -2312,11 +2561,11 @@ 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);
-         if (arg->weight_set_size) {
+         ::decode(arg->weight_set_positions, blp);
+         if (arg->weight_set_positions) {
            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++) {
+             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(
@@ -2336,6 +2585,7 @@ void CrushWrapper::decode(bufferlist::iterator& blp)
        choose_args[choose_args_index] = arg_map;
       }
     }
+    update_choose_args(nullptr); // in case we decode a legacy "corrupted" map
     finalize();
   }
   catch (...) {
@@ -2540,7 +2790,7 @@ namespace {
 
     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, 0, crush->get_bucket_weightf(*root)), f);
       }
@@ -2629,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;
@@ -2805,7 +3055,7 @@ protected:
        if (b &&
            bidx < (int)cmap.size &&
            cmap.args[bidx].weight_set &&
-           cmap.args[bidx].weight_set_size >= 1) {
+           cmap.args[bidx].weight_set_positions >= 1) {
          int pos;
          for (pos = 0;
               pos < (int)cmap.args[bidx].weight_set[0].size &&
@@ -2990,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
@@ -3033,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;
            }
@@ -3040,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;
          }
@@ -3154,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)
@@ -3219,16 +3476,25 @@ int CrushWrapper::_choose_args_adjust_item_weight_in_bucket(
   }
   crush_choose_arg *carg = &cmap.args[bidx];
   if (carg->weight_set == NULL) {
-    if (ss)
-      *ss << "no weight-set for bucket " << b->id;
-    ldout(cct, 10) << __func__ << "  no weight_set for bucket " << b->id
-                  << dendl;
-    return 0;
+    // 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_size != weight.size()) {
+  if (carg->weight_set_positions != weight.size()) {
     if (ss)
-      *ss << "weight_set_size != " << weight.size() << " for bucket " << b->id;
-    ldout(cct, 10) << __func__ << "  weight_set_size != " << weight.size()
+      *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;
   }