]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crush: encode can override weights with weight set 15002/head
authorLoic Dachary <ldachary@redhat.com>
Mon, 8 May 2017 16:57:23 +0000 (18:57 +0200)
committerLoic Dachary <ldachary@redhat.com>
Fri, 12 May 2017 15:17:35 +0000 (17:17 +0200)
Encode a "legacy" crushmap if (1) we're using weight sets, (2) the
client is lacking features for the weight set, but (3) the weight set
has a single position and no id remapping. Since these maps are only
used by clients (not humans), then there is no need to preserve the
original crush weights. We can just swap them for the real weights and
the legacy clients will behave as expected.

Fixes: http://tracker.ceph.com/issues/19836
Signed-off-by: Loic Dachary <loic@dachary.org>
src/crush/CrushWrapper.cc
src/test/crush/CrushWrapper.cc

index 04246a8422567e62214fa4ba5bad4b612520d052..a0a54066907da73a23c0028d990c6e9444d4f653 100644 (file)
@@ -112,10 +112,20 @@ bool CrushWrapper::has_choose_args() const
 
 bool CrushWrapper::has_incompat_choose_args() const
 {
-  // FIXME: if the chooseargs all have 1 position *and* do not remap IDs then
-  // we can fabricate a compatible crush map for legacy clients by swapping the
-  // choose_args weights in for the real weights.  until then,
-  return has_chooseargs();
+  if (choose_args.size() != 1)
+    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 &&
+       arg->ids_size == 0)
+       continue;
+    if (arg->weight_set_size != 1)
+      return true;
+    if (arg->ids_size != 0)
+      return true;
+  }
+  return false;
 }
 
 int CrushWrapper::split_id_class(int i, int *idout, int *classout) const
@@ -1394,6 +1404,16 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
   ::encode(crush->max_rules, bl);
   ::encode(crush->max_devices, bl);
 
+  bool encode_compat_choose_args = false;
+  crush_choose_arg_map arg_map;
+  memset(&arg_map, '\0', sizeof(arg_map));
+  if (has_choose_args() &&
+      !HAVE_FEATURE(features, CRUSH_CHOOSE_ARGS)) {
+    assert(!has_incompat_choose_args());
+    encode_compat_choose_args = true;
+    arg_map = choose_args.begin()->second;
+  }
+
   // buckets
   for (int i=0; i<crush->max_buckets; i++) {
     __u32 alg = 0;
@@ -1437,8 +1457,17 @@ void CrushWrapper::encode(bufferlist& bl, uint64_t features) const
       break;
 
     case CRUSH_BUCKET_STRAW2:
-      for (unsigned j=0; j<crush->buckets[i]->size; j++) {
-       ::encode((reinterpret_cast<crush_bucket_straw2*>(crush->buckets[i]))->item_weights[j], bl);
+      {
+       __u32 *weights;
+       if (encode_compat_choose_args &&
+           arg_map.args[i].weight_set_size > 0) {
+         weights = arg_map.args[i].weight_set[0].weights;
+       } else {
+         weights = (reinterpret_cast<crush_bucket_straw2*>(crush->buckets[i]))->item_weights;
+       }
+       for (unsigned j=0; j<crush->buckets[i]->size; j++) {
+         ::encode(weights[j], bl);
+       }
       }
       break;
 
index 6e6fb05e5e7c8ac38bd49934cba4ca982239bc12..42e67f006dd611df7c040304d9c00e92db5b97d8 100644 (file)
@@ -961,6 +961,75 @@ TEST(CrushWrapper, distance) {
   ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
 }
 
+TEST(CrushWrapper, choose_args_compat) {
+  CrushWrapper c;
+  c.create();
+  c.set_type_name(1, "host");
+  c.set_type_name(2, "rack");
+  c.set_type_name(3, "root");
+
+  int weight = 12;
+
+  map<string,string> loc;
+  loc["host"] = "b1";
+  loc["rack"] = "r11";
+  loc["root"] = "default";
+  int item = 1;
+  c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
+
+  loc["host"] = "b2";
+  loc["rack"] = "r12";
+  loc["root"] = "default";
+  item = 2;
+  c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
+
+  assert(c.add_simple_ruleset("rule1", "r11", "host", "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
+
+  int id = c.get_item_id("b1");
+
+  __u32 weights = 666 * 0x10000;
+  crush_weight_set weight_set;
+  weight_set.size = 1;
+  weight_set.weights = &weights;
+  crush_choose_arg choose_args[c.get_max_buckets()];
+  memset(choose_args, '\0', sizeof(crush_choose_arg) * c.get_max_buckets());
+  choose_args[-1-id].ids_size = 0;
+  choose_args[-1-id].weight_set_size = 1;
+  choose_args[-1-id].weight_set = &weight_set;
+  crush_choose_arg_map arg_map;
+  arg_map.size = c.get_max_buckets();
+  arg_map.args = choose_args;
+
+  uint64_t features = CEPH_FEATURE_CRUSH_TUNABLES5|CEPH_FEATURE_INCARNATION_2;
+
+  // if the client is capable, encode choose_args
+  {
+    c.choose_args[0] = arg_map;
+    bufferlist bl;
+    c.encode(bl, features|CEPH_FEATURE_CRUSH_CHOOSE_ARGS);
+    bufferlist::iterator i(bl.begin());
+    CrushWrapper c_new;
+    c_new.decode(i);
+    ASSERT_EQ(1u, c_new.choose_args.size());
+    ASSERT_EQ(1u, c_new.choose_args[0].args[-1-id].weight_set_size);
+    ASSERT_EQ(weights, c_new.choose_args[0].args[-1-id].weight_set[0].weights[0]);
+    ASSERT_EQ(weight, c_new.get_bucket_item_weightf(id, 0));
+  }
+
+  // if the client is not compatible, copy choose_arg in the weights
+  {
+    c.choose_args[0] = arg_map;
+    bufferlist bl;
+    c.encode(bl, features);
+    c.choose_args.clear();
+    bufferlist::iterator i(bl.begin());
+    CrushWrapper c_new;
+    c_new.decode(i);
+    ASSERT_EQ(0u, c_new.choose_args.size());
+    ASSERT_EQ((int)weights, c_new.get_bucket_item_weight(id, 0));
+  }
+}
+
 TEST(CrushWrapper, remove_unused_root) {
   CrushWrapper c;
   c.create();