2 #include "CrushCompiler.h"
21 #include "common/errno.h"
22 #include <boost/algorithm/string.hpp>
26 static void print_type_name(ostream
& out
, int t
, CrushWrapper
&crush
)
28 const char *name
= crush
.get_type_name(t
);
37 static void print_item_name(ostream
& out
, int t
, CrushWrapper
&crush
)
39 const char *name
= crush
.get_item_name(t
);
45 out
<< "bucket" << (-1-t
);
48 static void print_bucket_class_ids(ostream
& out
, int t
, CrushWrapper
&crush
)
50 if (crush
.class_bucket
.count(t
) == 0)
52 auto &class_to_id
= crush
.class_bucket
[t
];
53 for (auto &i
: class_to_id
) {
56 const char* class_name
= crush
.get_class_name(c
);
58 out
<< "\tid " << cid
<< " class " << class_name
<< "\t\t# do not change unnecessarily\n";
62 static void print_item_class(ostream
& out
, int t
, CrushWrapper
&crush
)
64 const char *c
= crush
.get_item_class(t
);
66 out
<< " class " << c
;
69 static void print_class(ostream
& out
, int t
, CrushWrapper
&crush
)
71 const char *c
= crush
.get_class_name(t
);
73 out
<< " class " << c
;
75 out
<< " # unexpected class " << t
;
78 static void print_rule_name(ostream
& out
, int t
, CrushWrapper
&crush
)
80 const char *name
= crush
.get_rule_name(t
);
87 static void print_fixedpoint(ostream
& out
, int i
)
90 snprintf(s
, sizeof(s
), "%.3f", (float)i
/ (float)0x10000);
94 int CrushCompiler::decompile_bucket_impl(int i
, ostream
&out
)
96 const char *name
= crush
.get_item_name(i
);
97 if (name
&& !crush
.is_valid_crush_name(name
))
99 int type
= crush
.get_bucket_type(i
);
100 print_type_name(out
, type
, crush
);
102 print_item_name(out
, i
, crush
);
104 out
<< "\tid " << i
<< "\t\t# do not change unnecessarily\n";
105 print_bucket_class_ids(out
, i
, crush
);
107 out
<< "\t# weight ";
108 print_fixedpoint(out
, crush
.get_bucket_weight(i
));
111 int n
= crush
.get_bucket_size(i
);
113 int alg
= crush
.get_bucket_alg(i
);
114 out
<< "\talg " << crush_bucket_alg_name(alg
);
116 // notate based on alg type
119 case CRUSH_BUCKET_UNIFORM
:
120 out
<< "\t# do not change bucket size (" << n
<< ") unnecessarily";
123 case CRUSH_BUCKET_LIST
:
124 out
<< "\t# add new items at the end; do not change order unnecessarily";
126 case CRUSH_BUCKET_TREE
:
127 out
<< "\t# do not change pos for existing items unnecessarily";
133 int hash
= crush
.get_bucket_hash(i
);
134 out
<< "\thash " << hash
<< "\t# " << crush_hash_name(hash
) << "\n";
136 for (int j
=0; j
<n
; j
++) {
137 int item
= crush
.get_bucket_item(i
, j
);
138 int w
= crush
.get_bucket_item_weight(i
, j
);
140 print_item_name(out
, item
, crush
);
142 print_fixedpoint(out
, w
);
152 /* Basically, we just descend recursively into all of the buckets,
153 * executing a depth-first traversal of the graph. Since the buckets form a
154 * directed acyclic graph, this should work just fine. The graph isn't
155 * necessarily a tree, so we have to keep track of what buckets we already
156 * outputted. We don't want to output anything twice. We also keep track of
157 * what buckets are in progress so that we can detect cycles. These can
158 * arise through user error.
160 int CrushCompiler::decompile_bucket(int cur
,
161 std::map
<int, dcb_state_t
>& dcb_states
,
164 if ((cur
== 0) || (!crush
.bucket_exists(cur
)))
167 std::map
<int, dcb_state_t
>::iterator c
= dcb_states
.find(cur
);
168 if (c
== dcb_states
.end()) {
169 // Mark this bucket as "in progress."
170 std::map
<int, dcb_state_t
>::value_type
val(cur
, DCB_STATE_IN_PROGRESS
);
171 std::pair
<std::map
<int, dcb_state_t
>::iterator
, bool> rval
172 (dcb_states
.insert(val
));
176 else if (c
->second
== DCB_STATE_DONE
) {
177 // We already did this bucket.
180 else if (c
->second
== DCB_STATE_IN_PROGRESS
) {
181 err
<< "decompile_crush_bucket: logic error: tried to decompile "
182 "a bucket that is already being decompiled" << std::endl
;
186 err
<< "decompile_crush_bucket: logic error: illegal bucket state! "
187 << c
->second
<< std::endl
;
191 int bsize
= crush
.get_bucket_size(cur
);
192 for (int i
= 0; i
< bsize
; ++i
) {
193 int item
= crush
.get_bucket_item(cur
, i
);
194 std::map
<int, dcb_state_t
>::iterator d
= dcb_states
.find(item
);
195 if (d
== dcb_states
.end()) {
196 int ret
= decompile_bucket(item
, dcb_states
, out
);
200 else if (d
->second
== DCB_STATE_IN_PROGRESS
) {
201 err
<< "decompile_crush_bucket: error: while trying to output bucket "
202 << cur
<< ", we found out that it contains one of the buckets that "
203 << "contain it. This is not allowed. The buckets must form a "
204 << "directed acyclic graph." << std::endl
;
207 else if (d
->second
!= DCB_STATE_DONE
) {
208 err
<< "decompile_crush_bucket: logic error: illegal bucket state "
209 << d
->second
<< std::endl
;
213 decompile_bucket_impl(cur
, out
);
214 c
->second
= DCB_STATE_DONE
;
218 int CrushCompiler::decompile_weight_set_weights(crush_weight_set weight_set
,
222 for (__u32 i
= 0; i
< weight_set
.size
; i
++) {
223 print_fixedpoint(out
, weight_set
.weights
[i
]);
230 int CrushCompiler::decompile_weight_set(crush_weight_set
*weight_set
,
234 out
<< " weight_set [\n";
235 for (__u32 i
= 0; i
< size
; i
++) {
236 int r
= decompile_weight_set_weights(weight_set
[i
], out
);
244 int CrushCompiler::decompile_ids(int *ids
,
249 for (__u32 i
= 0; i
< size
; i
++)
250 out
<< ids
[i
] << " ";
255 int CrushCompiler::decompile_choose_arg(crush_choose_arg
*arg
,
261 out
<< " bucket_id " << bucket_id
<< "\n";
262 if (arg
->weight_set_size
> 0) {
263 r
= decompile_weight_set(arg
->weight_set
, arg
->weight_set_size
, out
);
267 if (arg
->ids_size
> 0) {
268 r
= decompile_ids(arg
->ids
, arg
->ids_size
, out
);
276 int CrushCompiler::decompile_choose_arg_map(crush_choose_arg_map arg_map
,
279 for (__u32 i
= 0; i
< arg_map
.size
; i
++) {
280 if ((arg_map
.args
[i
].ids_size
== 0) &&
281 (arg_map
.args
[i
].weight_set_size
== 0))
283 int r
= decompile_choose_arg(&arg_map
.args
[i
], -1-i
, out
);
290 int CrushCompiler::decompile_choose_args(const std::pair
<const long unsigned int, crush_choose_arg_map
> &i
,
293 out
<< "choose_args " << i
.first
<< " {\n";
294 int r
= decompile_choose_arg_map(i
.second
, out
);
301 int CrushCompiler::decompile(ostream
&out
)
303 crush
.cleanup_classes();
305 out
<< "# begin crush map\n";
307 // only dump tunables if they differ from the defaults
308 if (crush
.get_choose_local_tries() != 2)
309 out
<< "tunable choose_local_tries " << crush
.get_choose_local_tries() << "\n";
310 if (crush
.get_choose_local_fallback_tries() != 5)
311 out
<< "tunable choose_local_fallback_tries " << crush
.get_choose_local_fallback_tries() << "\n";
312 if (crush
.get_choose_total_tries() != 19)
313 out
<< "tunable choose_total_tries " << crush
.get_choose_total_tries() << "\n";
314 if (crush
.get_chooseleaf_descend_once() != 0)
315 out
<< "tunable chooseleaf_descend_once " << crush
.get_chooseleaf_descend_once() << "\n";
316 if (crush
.get_chooseleaf_vary_r() != 0)
317 out
<< "tunable chooseleaf_vary_r " << crush
.get_chooseleaf_vary_r() << "\n";
318 if (crush
.get_chooseleaf_stable() != 0)
319 out
<< "tunable chooseleaf_stable " << crush
.get_chooseleaf_stable() << "\n";
320 if (crush
.get_straw_calc_version() != 0)
321 out
<< "tunable straw_calc_version " << crush
.get_straw_calc_version() << "\n";
322 if (crush
.get_allowed_bucket_algs() != CRUSH_LEGACY_ALLOWED_BUCKET_ALGS
)
323 out
<< "tunable allowed_bucket_algs " << crush
.get_allowed_bucket_algs()
326 out
<< "\n# devices\n";
327 for (int i
=0; i
<crush
.get_max_devices(); i
++) {
328 out
<< "device " << i
<< " ";
329 print_item_name(out
, i
, crush
);
330 print_item_class(out
, i
, crush
);
334 out
<< "\n# types\n";
335 int n
= crush
.get_num_type_names();
336 for (int i
=0; n
; i
++) {
337 const char *name
= crush
.get_type_name(i
);
339 if (i
== 0) out
<< "type 0 osd\n";
343 out
<< "type " << i
<< " " << name
<< "\n";
346 out
<< "\n# buckets\n";
347 std::map
<int, dcb_state_t
> dcb_states
;
348 for (int bucket
= -1; bucket
> -1-crush
.get_max_buckets(); --bucket
) {
349 int ret
= decompile_bucket(bucket
, dcb_states
, out
);
354 out
<< "\n# rules\n";
355 for (int i
=0; i
<crush
.get_max_rules(); i
++) {
356 if (!crush
.rule_exists(i
))
359 if (crush
.get_rule_name(i
))
360 print_rule_name(out
, i
, crush
);
362 out
<< "\truleset " << crush
.get_rule_mask_ruleset(i
) << "\n";
364 switch (crush
.get_rule_mask_type(i
)) {
365 case CEPH_PG_TYPE_REPLICATED
:
366 out
<< "\ttype replicated\n";
368 case CEPH_PG_TYPE_ERASURE
:
369 out
<< "\ttype erasure\n";
372 out
<< "\ttype " << crush
.get_rule_mask_type(i
) << "\n";
375 out
<< "\tmin_size " << crush
.get_rule_mask_min_size(i
) << "\n";
376 out
<< "\tmax_size " << crush
.get_rule_mask_max_size(i
) << "\n";
378 for (int j
=0; j
<crush
.get_rule_len(i
); j
++) {
379 switch (crush
.get_rule_op(i
, j
)) {
380 case CRUSH_RULE_NOOP
:
381 out
<< "\tstep noop\n";
383 case CRUSH_RULE_TAKE
:
384 out
<< "\tstep take ";
386 int step_item
= crush
.get_rule_arg1(i
, j
);
389 int res
= crush
.split_id_class(step_item
, &original_item
, &c
);
393 step_item
= original_item
;
394 print_item_name(out
, step_item
, crush
);
396 print_class(out
, c
, crush
);
400 case CRUSH_RULE_EMIT
:
401 out
<< "\tstep emit\n";
403 case CRUSH_RULE_SET_CHOOSE_TRIES
:
404 out
<< "\tstep set_choose_tries " << crush
.get_rule_arg1(i
, j
)
407 case CRUSH_RULE_SET_CHOOSE_LOCAL_TRIES
:
408 out
<< "\tstep set_choose_local_tries " << crush
.get_rule_arg1(i
, j
)
411 case CRUSH_RULE_SET_CHOOSE_LOCAL_FALLBACK_TRIES
:
412 out
<< "\tstep set_choose_local_fallback_tries " << crush
.get_rule_arg1(i
, j
)
415 case CRUSH_RULE_SET_CHOOSELEAF_TRIES
:
416 out
<< "\tstep set_chooseleaf_tries " << crush
.get_rule_arg1(i
, j
)
419 case CRUSH_RULE_SET_CHOOSELEAF_VARY_R
:
420 out
<< "\tstep set_chooseleaf_vary_r " << crush
.get_rule_arg1(i
, j
)
423 case CRUSH_RULE_SET_CHOOSELEAF_STABLE
:
424 out
<< "\tstep set_chooseleaf_stable " << crush
.get_rule_arg1(i
, j
)
427 case CRUSH_RULE_CHOOSE_FIRSTN
:
428 out
<< "\tstep choose firstn "
429 << crush
.get_rule_arg1(i
, j
)
431 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
434 case CRUSH_RULE_CHOOSE_INDEP
:
435 out
<< "\tstep choose indep "
436 << crush
.get_rule_arg1(i
, j
)
438 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
441 case CRUSH_RULE_CHOOSELEAF_FIRSTN
:
442 out
<< "\tstep chooseleaf firstn "
443 << crush
.get_rule_arg1(i
, j
)
445 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
448 case CRUSH_RULE_CHOOSELEAF_INDEP
:
449 out
<< "\tstep chooseleaf indep "
450 << crush
.get_rule_arg1(i
, j
)
452 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
459 if (crush
.choose_args
.size() > 0) {
460 out
<< "\n# choose_args\n";
461 for (auto i
: crush
.choose_args
) {
462 int ret
= decompile_choose_args(i
, out
);
467 out
<< "\n# end crush map" << std::endl
;
472 // ================================================================
474 string
CrushCompiler::string_node(node_t
&node
)
476 return boost::trim_copy(string(node
.value
.begin(), node
.value
.end()));
479 int CrushCompiler::int_node(node_t
&node
)
481 string str
= string_node(node
);
482 return strtol(str
.c_str(), 0, 10);
485 float CrushCompiler::float_node(node_t
&node
)
487 string s
= string_node(node
);
488 return strtof(s
.c_str(), 0);
491 int CrushCompiler::parse_device(iter_t
const& i
)
493 int id
= int_node(i
->children
[1]);
495 string name
= string_node(i
->children
[2]);
496 crush
.set_item_name(id
, name
.c_str());
497 if (item_id
.count(name
)) {
498 err
<< "item " << name
<< " defined twice" << std::endl
;
504 if (verbose
) err
<< "device " << id
<< " '" << name
<< "'";
506 if (i
->children
.size() > 3) {
507 string c
= string_node(i
->children
[4]);
508 crush
.set_item_class(id
, c
);
509 if (verbose
) err
<< " class" << " '" << c
<< "'" << std::endl
;
511 if (verbose
) err
<< std::endl
;
516 int CrushCompiler::parse_tunable(iter_t
const& i
)
518 string name
= string_node(i
->children
[1]);
519 int val
= int_node(i
->children
[2]);
521 if (name
== "choose_local_tries")
522 crush
.set_choose_local_tries(val
);
523 else if (name
== "choose_local_fallback_tries")
524 crush
.set_choose_local_fallback_tries(val
);
525 else if (name
== "choose_total_tries")
526 crush
.set_choose_total_tries(val
);
527 else if (name
== "chooseleaf_descend_once")
528 crush
.set_chooseleaf_descend_once(val
);
529 else if (name
== "chooseleaf_vary_r")
530 crush
.set_chooseleaf_vary_r(val
);
531 else if (name
== "chooseleaf_stable")
532 crush
.set_chooseleaf_stable(val
);
533 else if (name
== "straw_calc_version")
534 crush
.set_straw_calc_version(val
);
535 else if (name
== "allowed_bucket_algs")
536 crush
.set_allowed_bucket_algs(val
);
538 err
<< "tunable " << name
<< " not recognized" << std::endl
;
544 current crop of tunables are all now "safe". re-enable this when we
545 add new ones that are ... new.
547 if (!unsafe_tunables) {
548 err << "tunables are NOT FULLY IMPLEMENTED; enable with --enable-unsafe-tunables to enable this feature" << std::endl;
553 if (verbose
) err
<< "tunable " << name
<< " " << val
<< std::endl
;
557 int CrushCompiler::parse_bucket_type(iter_t
const& i
)
559 int id
= int_node(i
->children
[1]);
560 string name
= string_node(i
->children
[2]);
561 if (verbose
) err
<< "type " << id
<< " '" << name
<< "'" << std::endl
;
563 crush
.set_type_name(id
, name
.c_str());
567 int CrushCompiler::parse_bucket(iter_t
const& i
)
569 string tname
= string_node(i
->children
[0]);
570 if (!type_id
.count(tname
)) {
571 err
<< "bucket type '" << tname
<< "' is not defined" << std::endl
;
574 int type
= type_id
[tname
];
576 string name
= string_node(i
->children
[1]);
577 if (item_id
.count(name
)) {
578 err
<< "bucket or device '" << name
<< "' is already defined" << std::endl
;
582 int id
= 0; // none, yet!
587 map
<int32_t, int32_t> class_id
;
589 for (unsigned p
=3; p
<i
->children
.size()-1; p
++) {
590 iter_t sub
= i
->children
.begin() + p
;
591 string tag
= string_node(sub
->children
[0]);
592 //err << "tag " << tag << std::endl;
594 int maybe_id
= int_node(sub
->children
[1]);
595 if (verbose
) err
<< "bucket " << name
<< " id " << maybe_id
;
596 if (sub
->children
.size() > 2) {
597 string class_name
= string_node(sub
->children
[3]);
598 if (!crush
.class_exists(class_name
)) {
599 err
<< " unknown device class '" << class_name
<< "'" << std::endl
;
602 int cid
= crush
.get_class_id(class_name
);
603 if (class_id
.count(cid
) != 0) {
604 err
<< "duplicate device class " << class_name
<< " for bucket " << name
<< std::endl
;
607 class_id
[cid
] = maybe_id
;
608 if (verbose
) err
<< " class" << " '" << class_name
<< "'" << std::endl
;
611 if (verbose
) err
<< std::endl
;
613 } else if (tag
== "alg") {
614 string a
= string_node(sub
->children
[1]);
616 alg
= CRUSH_BUCKET_UNIFORM
;
617 else if (a
== "list")
618 alg
= CRUSH_BUCKET_LIST
;
619 else if (a
== "tree")
620 alg
= CRUSH_BUCKET_TREE
;
621 else if (a
== "straw")
622 alg
= CRUSH_BUCKET_STRAW
;
623 else if (a
== "straw2")
624 alg
= CRUSH_BUCKET_STRAW2
;
626 err
<< "unknown bucket alg '" << a
<< "'" << std::endl
<< std::endl
;
630 else if (tag
== "hash") {
631 string a
= string_node(sub
->children
[1]);
632 if (a
== "rjenkins1")
633 hash
= CRUSH_HASH_RJENKINS1
;
635 hash
= atoi(a
.c_str());
637 else if (tag
== "item") {
638 // first, just determine which item pos's are already used
640 for (unsigned q
= 2; q
< sub
->children
.size(); q
++) {
641 string tag
= string_node(sub
->children
[q
++]);
643 int pos
= int_node(sub
->children
[q
]);
644 if (used_items
.count(pos
)) {
645 err
<< "item '" << string_node(sub
->children
[1]) << "' in bucket '" << name
<< "' has explicit pos " << pos
<< ", which is occupied" << std::endl
;
648 used_items
.insert(pos
);
656 if (!used_items
.empty())
657 size
= MAX(size
, *used_items
.rbegin());
658 vector
<int> items(size
);
659 vector
<int> weights(size
);
662 unsigned bucketweight
= 0;
663 bool have_uniform_weight
= false;
664 unsigned uniform_weight
= 0;
665 for (unsigned p
=3; p
<i
->children
.size()-1; p
++) {
666 iter_t sub
= i
->children
.begin() + p
;
667 string tag
= string_node(sub
->children
[0]);
670 string iname
= string_node(sub
->children
[1]);
671 if (!item_id
.count(iname
)) {
672 err
<< "item '" << iname
<< "' in bucket '" << name
<< "' is not defined" << std::endl
;
675 int itemid
= item_id
[iname
];
677 unsigned weight
= 0x10000;
678 if (item_weight
.count(itemid
))
679 weight
= item_weight
[itemid
];
682 for (unsigned q
= 2; q
< sub
->children
.size(); q
++) {
683 string tag
= string_node(sub
->children
[q
++]);
684 if (tag
== "weight") {
685 weight
= float_node(sub
->children
[q
]) * (float)0x10000;
686 if (weight
> CRUSH_MAX_DEVICE_WEIGHT
&& itemid
>= 0) {
687 err
<< "device weight limited to " << CRUSH_MAX_DEVICE_WEIGHT
/ 0x10000 << std::endl
;
690 else if (weight
> CRUSH_MAX_BUCKET_WEIGHT
&& itemid
< 0) {
691 err
<< "bucket weight limited to " << CRUSH_MAX_BUCKET_WEIGHT
/ 0x10000
692 << " to prevent overflow" << std::endl
;
696 else if (tag
== "pos")
697 pos
= int_node(sub
->children
[q
]);
702 if (alg
== CRUSH_BUCKET_UNIFORM
) {
703 if (!have_uniform_weight
) {
704 have_uniform_weight
= true;
705 uniform_weight
= weight
;
707 if (uniform_weight
!= weight
) {
708 err
<< "item '" << iname
<< "' in uniform bucket '" << name
<< "' has weight " << weight
709 << " but previous item(s) have weight " << (float)uniform_weight
/(float)0x10000
710 << "; uniform bucket items must all have identical weights." << std::endl
;
717 err
<< "item '" << iname
<< "' in bucket '" << name
<< "' has pos " << pos
<< " >= size " << size
<< std::endl
;
721 while (used_items
.count(curpos
)) curpos
++;
724 //err << " item " << iname << " (" << itemid << ") pos " << pos << " weight " << weight << std::endl;
726 weights
[pos
] = weight
;
728 if (crush_addition_is_unsafe(bucketweight
, weight
)) {
729 err
<< "oh no! our bucket weights are overflowing all over the place, better lower the item weights" << std::endl
;
733 bucketweight
+= weight
;
738 for (id
=-1; id_item
.count(id
); id
--) ;
739 //err << "assigned id " << id << std::endl;
742 for (auto &i
: class_id
)
743 crush
.class_bucket
[id
][i
.first
] = i
.second
;
745 if (verbose
) err
<< "bucket " << name
<< " (" << id
<< ") " << size
<< " items and weight "
746 << (float)bucketweight
/ (float)0x10000 << std::endl
;
749 item_weight
[id
] = bucketweight
;
752 int r
= crush
.add_bucket(id
, alg
, hash
, type
, size
, &items
[0], &weights
[0], NULL
);
755 err
<< "Duplicate bucket id " << id
<< std::endl
;
757 err
<< "add_bucket failed " << cpp_strerror(r
) << std::endl
;
760 r
= crush
.set_item_name(id
, name
.c_str());
764 int CrushCompiler::parse_rule(iter_t
const& i
)
766 crush
.populate_classes();
768 int start
; // rule name is optional!
770 string rname
= string_node(i
->children
[1]);
772 if (rule_id
.count(rname
)) {
773 err
<< "rule name '" << rname
<< "' already defined\n" << std::endl
;
782 int ruleset
= int_node(i
->children
[start
]);
784 string tname
= string_node(i
->children
[start
+2]);
786 if (tname
== "replicated")
787 type
= CEPH_PG_TYPE_REPLICATED
;
788 else if (tname
== "erasure")
789 type
= CEPH_PG_TYPE_ERASURE
;
793 int minsize
= int_node(i
->children
[start
+4]);
794 int maxsize
= int_node(i
->children
[start
+6]);
796 int steps
= i
->children
.size() - start
- 8;
797 //err << "num steps " << steps << std::endl;
799 int ruleno
= crush
.add_rule(steps
, ruleset
, type
, minsize
, maxsize
, -1);
800 if (rname
.length()) {
801 crush
.set_rule_name(ruleno
, rname
.c_str());
802 rule_id
[rname
] = ruleno
;
806 for (iter_t p
= i
->children
.begin() + start
+ 7; step
< steps
; p
++) {
807 iter_t s
= p
->children
.begin() + 1;
808 int stepid
= s
->value
.id().to_long();
810 case crush_grammar::_step_take
:
812 string item
= string_node(s
->children
[1]);
813 if (!item_id
.count(item
)) {
814 err
<< "in rule '" << rname
<< "' item '" << item
<< "' not defined" << std::endl
;
817 int id
= item_id
[item
];
820 if (s
->children
.size() > 2) {
821 class_name
= string_node(s
->children
[3]);
822 c
= crush
.get_class_id(class_name
);
825 if (crush
.class_bucket
.count(id
) == 0) {
826 err
<< "in rule '" << rname
<< "' step take " << item
827 << " has no class information" << std::endl
;
830 if (crush
.class_bucket
[id
].count(c
) == 0) {
831 err
<< "in rule '" << rname
<< "' step take " << item
832 << " no matching bucket for class " << class_name
<< std::endl
;
835 id
= crush
.class_bucket
[id
][c
];
838 err
<< "rule " << rname
<< " take " << item
;
842 err
<< " remapped to " << crush
.get_item_name(id
) << std::endl
;
845 crush
.set_rule_step_take(ruleno
, step
++, id
);
849 case crush_grammar::_step_set_choose_tries
:
851 int val
= int_node(s
->children
[1]);
852 crush
.set_rule_step_set_choose_tries(ruleno
, step
++, val
);
856 case crush_grammar::_step_set_choose_local_tries
:
858 int val
= int_node(s
->children
[1]);
859 crush
.set_rule_step_set_choose_local_tries(ruleno
, step
++, val
);
863 case crush_grammar::_step_set_choose_local_fallback_tries
:
865 int val
= int_node(s
->children
[1]);
866 crush
.set_rule_step_set_choose_local_fallback_tries(ruleno
, step
++, val
);
870 case crush_grammar::_step_set_chooseleaf_tries
:
872 int val
= int_node(s
->children
[1]);
873 crush
.set_rule_step_set_chooseleaf_tries(ruleno
, step
++, val
);
877 case crush_grammar::_step_set_chooseleaf_vary_r
:
879 int val
= int_node(s
->children
[1]);
880 crush
.set_rule_step_set_chooseleaf_vary_r(ruleno
, step
++, val
);
884 case crush_grammar::_step_set_chooseleaf_stable
:
886 int val
= int_node(s
->children
[1]);
887 crush
.set_rule_step_set_chooseleaf_stable(ruleno
, step
++, val
);
891 case crush_grammar::_step_choose
:
892 case crush_grammar::_step_chooseleaf
:
894 string type
= string_node(s
->children
[4]);
895 if (!type_id
.count(type
)) {
896 err
<< "in rule '" << rname
<< "' type '" << type
<< "' not defined" << std::endl
;
899 string choose
= string_node(s
->children
[0]);
900 string mode
= string_node(s
->children
[1]);
901 if (choose
== "choose") {
902 if (mode
== "firstn")
903 crush
.set_rule_step_choose_firstn(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
904 else if (mode
== "indep")
905 crush
.set_rule_step_choose_indep(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
907 } else if (choose
== "chooseleaf") {
908 if (mode
== "firstn")
909 crush
.set_rule_step_choose_leaf_firstn(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
910 else if (mode
== "indep")
911 crush
.set_rule_step_choose_leaf_indep(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
917 case crush_grammar::_step_emit
:
918 crush
.set_rule_step_emit(ruleno
, step
++);
922 err
<< "bad crush step " << stepid
<< std::endl
;
926 assert(step
== steps
);
930 int CrushCompiler::parse_weight_set_weights(iter_t
const& i
, int bucket_id
, crush_weight_set
*weight_set
)
932 // -2 for the enclosing [ ]
933 __u32 size
= i
->children
.size() - 2;
934 __u32 bucket_size
= crush
.get_bucket_size(bucket_id
);
935 if (size
!= bucket_size
) {
936 err
<< bucket_id
<< " needs exactly " << bucket_size
937 << " weights but got " << size
<< std::endl
;
940 weight_set
->size
= size
;
941 weight_set
->weights
= (__u32
*)calloc(weight_set
->size
, sizeof(__u32
));
943 for (iter_t p
= i
->children
.begin() + 1; p
!= i
->children
.end(); p
++, pos
++)
945 weight_set
->weights
[pos
] = float_node(*p
) * (float)0x10000;
949 int CrushCompiler::parse_weight_set(iter_t
const& i
, int bucket_id
, crush_choose_arg
*arg
)
951 // -3 stands for the leading "weight_set" keyword and the enclosing [ ]
952 arg
->weight_set_size
= i
->children
.size() - 3;
953 arg
->weight_set
= (crush_weight_set
*)calloc(arg
->weight_set_size
, sizeof(crush_weight_set
));
955 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
957 switch((int)p
->value
.id().to_long()) {
958 case crush_grammar::_weight_set_weights
:
959 if (pos
< arg
->weight_set_size
) {
960 r
= parse_weight_set_weights(p
, bucket_id
, &arg
->weight_set
[pos
]);
963 err
<< "invalid weight_set syntax" << std::endl
;
973 int CrushCompiler::parse_choose_arg_ids(iter_t
const& i
, int bucket_id
, crush_choose_arg
*arg
)
975 // -3 for the leading "ids" keyword and the enclosing [ ]
976 __u32 size
= i
->children
.size() - 3;
977 __u32 bucket_size
= crush
.get_bucket_size(bucket_id
);
978 if (size
!= bucket_size
) {
979 err
<< bucket_id
<< " needs exactly " << bucket_size
980 << " ids but got " << size
<< std::endl
;
983 arg
->ids_size
= size
;
984 arg
->ids
= (int *)calloc(arg
->ids_size
, sizeof(int));
986 for (iter_t p
= i
->children
.begin() + 2; pos
< size
; p
++, pos
++)
987 arg
->ids
[pos
] = int_node(*p
);
991 int CrushCompiler::parse_choose_arg(iter_t
const& i
, crush_choose_arg
*args
)
993 int bucket_id
= int_node(i
->children
[2]);
994 if (-1-bucket_id
< 0 || -1-bucket_id
>= crush
.get_max_buckets()) {
995 err
<< bucket_id
<< " is out of range" << std::endl
;
998 if (!crush
.bucket_exists(bucket_id
)) {
999 err
<< bucket_id
<< " does not exist" << std::endl
;
1002 crush_choose_arg
*arg
= &args
[-1-bucket_id
];
1003 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1005 switch((int)p
->value
.id().to_long()) {
1006 case crush_grammar::_weight_set
:
1007 r
= parse_weight_set(p
, bucket_id
, arg
);
1009 case crush_grammar::_choose_arg_ids
:
1010 r
= parse_choose_arg_ids(p
, bucket_id
, arg
);
1019 int CrushCompiler::parse_choose_args(iter_t
const& i
)
1021 int choose_arg_index
= int_node(i
->children
[1]);
1022 if (crush
.choose_args
.find(choose_arg_index
) != crush
.choose_args
.end()) {
1023 err
<< choose_arg_index
<< " duplicated" << std::endl
;
1026 crush_choose_arg_map arg_map
;
1027 arg_map
.size
= crush
.get_max_buckets();
1028 arg_map
.args
= (crush_choose_arg
*)calloc(arg_map
.size
, sizeof(crush_choose_arg
));
1029 for (iter_t p
= i
->children
.begin() + 2; p
!= i
->children
.end(); p
++) {
1031 switch((int)p
->value
.id().to_long()) {
1032 case crush_grammar::_choose_arg
:
1033 r
= parse_choose_arg(p
, arg_map
.args
);
1037 crush
.destroy_choose_args(arg_map
);
1041 crush
.choose_args
[choose_arg_index
] = arg_map
;
1045 void CrushCompiler::find_used_bucket_ids(iter_t
const& i
)
1047 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1048 if ((int)p
->value
.id().to_long() == crush_grammar::_bucket
) {
1049 iter_t firstline
= p
->children
.begin() + 3;
1050 string tag
= string_node(firstline
->children
[0]);
1052 int id
= int_node(firstline
->children
[1]);
1053 //err << "saw bucket id " << id << std::endl;
1054 id_item
[id
] = string();
1060 int CrushCompiler::parse_crush(iter_t
const& i
)
1062 find_used_bucket_ids(i
);
1064 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1066 switch (p
->value
.id().to_long()) {
1067 case crush_grammar::_tunable
:
1068 r
= parse_tunable(p
);
1070 case crush_grammar::_device
:
1071 r
= parse_device(p
);
1073 case crush_grammar::_bucket_type
:
1074 r
= parse_bucket_type(p
);
1076 case crush_grammar::_bucket
:
1077 r
= parse_bucket(p
);
1079 case crush_grammar::_crushrule
:
1082 case crush_grammar::_choose_args
:
1083 r
= parse_choose_args(p
);
1093 //err << "max_devices " << crush.get_max_devices() << std::endl;
1094 crush
.cleanup_classes();
1100 // squash runs of whitespace to one space, excepting newlines
1101 string
CrushCompiler::consolidate_whitespace(string in
)
1106 for (unsigned p
=0; p
<in
.length(); p
++) {
1107 if (isspace(in
[p
]) && in
[p
] != '\n') {
1113 if (out
.length()) out
+= " ";
1120 err
<< " \"" << in
<< "\" -> \"" << out
<< "\"" << std::endl
;
1124 void CrushCompiler::dump(iter_t
const& i
, int ind
)
1127 for (int j
=0; j
<ind
; j
++)
1129 long id
= i
->value
.id().to_long();
1131 err
<< "'" << string(i
->value
.begin(), i
->value
.end())
1132 << "' " << i
->children
.size() << " children" << std::endl
;
1133 for (unsigned int j
= 0; j
< i
->children
.size(); j
++)
1134 dump(i
->children
.begin() + j
, ind
+1);
1138 * This function fix the problem like below
1139 * rack using_foo { item foo }
1142 * if an item being used by a bucket is defined after that bucket.
1143 * CRUSH compiler will create a map by which we can
1144 * not identify that item when selecting in that bucket.
1146 int CrushCompiler::adjust_bucket_item_place(iter_t
const &i
)
1148 map
<string
,set
<string
> > bucket_items
;
1149 map
<string
,iter_t
> bucket_itrer
;
1150 vector
<string
> buckets
;
1151 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); ++p
) {
1152 if ((int)p
->value
.id().to_long() == crush_grammar::_bucket
) {
1153 string name
= string_node(p
->children
[1]);
1154 buckets
.push_back(name
);
1155 bucket_itrer
[name
] = p
;
1156 //skip non-bucket-item children in the bucket's parse tree
1157 for (unsigned q
=3; q
< p
->children
.size()-1; ++q
) {
1158 iter_t sub
= p
->children
.begin() + q
;
1159 if ((int)sub
->value
.id().to_long()
1160 == crush_grammar::_bucket_item
) {
1161 string iname
= string_node(sub
->children
[1]);
1162 bucket_items
[name
].insert(iname
);
1169 for (unsigned i
=0; i
< buckets
.size(); ++i
) {
1170 for (unsigned j
=i
+1; j
< buckets
.size(); ++j
) {
1171 if (bucket_items
[buckets
[i
]].count(buckets
[j
])) {
1172 if (bucket_items
[buckets
[j
]].count(buckets
[i
])) {
1173 err
<< "bucket '" << buckets
[i
] << "' and bucket '"
1174 << buckets
[j
] << "' are included each other" << std::endl
;
1177 std::iter_swap(bucket_itrer
[buckets
[i
]], bucket_itrer
[buckets
[j
]]);
1186 int CrushCompiler::compile(istream
& in
, const char *infn
)
1191 // always start with legacy tunables, so that the compiled result of
1192 // a given crush file is fixed for all time.
1193 crush
.set_tunables_legacy();
1198 map
<int,int> line_pos
; // pos -> line
1199 map
<int,string
> line_val
;
1200 while (getline(in
, str
)) {
1202 int l
= str
.length();
1203 if (l
&& str
[l
- 1] == '\n')
1206 line_val
[line
] = str
;
1209 int n
= str
.find("#");
1211 str
.erase(n
, str
.length()-n
);
1213 if (verbose
>1) err
<< line
<< ": " << str
<< std::endl
;
1215 // work around spirit crankiness by removing extraneous
1216 // whitespace. there is probably a more elegant solution, but
1217 // this only broke with the latest spirit (with the switchover to
1218 // "classic"), i don't want to spend too much time figuring it
1220 string stripped
= consolidate_whitespace(str
);
1221 if (stripped
.length() && big
.length() && big
[big
.length()-1] != ' ') big
+= " ";
1223 line_pos
[big
.length()] = line
;
1228 if (verbose
> 2) err
<< "whole file is: \"" << big
<< "\"" << std::endl
;
1230 crush_grammar crushg
;
1231 const char *start
= big
.c_str();
1232 //tree_parse_info<const char *> info = ast_parse(start, crushg, space_p);
1233 tree_parse_info
<> info
= ast_parse(start
, crushg
, space_p
);
1237 int cpos
= info
.stop
- start
;
1238 //out << "cpos " << cpos << std::endl;
1239 //out << " linemap " << line_pos << std::endl;
1240 assert(!line_pos
.empty());
1241 map
<int,int>::iterator p
= line_pos
.upper_bound(cpos
);
1242 if (p
!= line_pos
.begin())
1244 int line
= p
->second
;
1245 int pos
= cpos
- p
->first
;
1246 err
<< infn
<< ":" << line
//<< ":" << (pos+1)
1247 << " error: parse error at '" << line_val
[line
].substr(pos
) << "'" << std::endl
;
1251 int r
= adjust_bucket_item_place(info
.trees
.begin());
1255 //out << "parsing succeeded\n";
1256 //dump(info.trees.begin());
1257 return parse_crush(info
.trees
.begin());