From: Loic Dachary Date: Mon, 8 May 2017 16:57:23 +0000 (+0200) Subject: crush: encode can override weights with weight set X-Git-Tag: v12.1.0~10^2~94^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F15002%2Fhead;p=ceph.git crush: encode can override weights with weight set 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 --- diff --git a/src/crush/CrushWrapper.cc b/src/crush/CrushWrapper.cc index 04246a84225..a0a54066907 100644 --- a/src/crush/CrushWrapper.cc +++ b/src/crush/CrushWrapper.cc @@ -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; imax_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; jbuckets[i]->size; j++) { - ::encode((reinterpret_cast(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->buckets[i]))->item_weights; + } + for (unsigned j=0; jbuckets[i]->size; j++) { + ::encode(weights[j], bl); + } } break; diff --git a/src/test/crush/CrushWrapper.cc b/src/test/crush/CrushWrapper.cc index 6e6fb05e5e7..42e67f006dd 100644 --- a/src/test/crush/CrushWrapper.cc +++ b/src/test/crush/CrushWrapper.cc @@ -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 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();