2 #include "CrushCompiler.h"
12 #include "common/errno.h"
13 #include <boost/algorithm/string.hpp>
17 static void print_type_name(ostream
& out
, int t
, CrushWrapper
&crush
)
19 const char *name
= crush
.get_type_name(t
);
28 static void print_item_name(ostream
& out
, int t
, CrushWrapper
&crush
)
30 const char *name
= crush
.get_item_name(t
);
36 out
<< "bucket" << (-1-t
);
39 static void print_bucket_class_ids(ostream
& out
, int t
, CrushWrapper
&crush
)
41 if (crush
.class_bucket
.count(t
) == 0)
43 auto &class_to_id
= crush
.class_bucket
[t
];
44 for (auto &i
: class_to_id
) {
47 const char* class_name
= crush
.get_class_name(c
);
49 out
<< "\tid " << cid
<< " class " << class_name
<< "\t\t# do not change unnecessarily\n";
53 static void print_item_class(ostream
& out
, int t
, CrushWrapper
&crush
)
55 const char *c
= crush
.get_item_class(t
);
57 out
<< " class " << c
;
60 static void print_class(ostream
& out
, int t
, CrushWrapper
&crush
)
62 const char *c
= crush
.get_class_name(t
);
64 out
<< " class " << c
;
66 out
<< " # unexpected class " << t
;
69 static void print_rule_name(ostream
& out
, int t
, CrushWrapper
&crush
)
71 const char *name
= crush
.get_rule_name(t
);
78 static void print_fixedpoint(ostream
& out
, int i
)
81 snprintf(s
, sizeof(s
), "%.3f", (float)i
/ (float)0x10000);
85 int CrushCompiler::decompile_bucket_impl(int i
, ostream
&out
)
87 const char *name
= crush
.get_item_name(i
);
88 if (name
&& !crush
.is_valid_crush_name(name
))
90 int type
= crush
.get_bucket_type(i
);
91 print_type_name(out
, type
, crush
);
93 print_item_name(out
, i
, crush
);
95 out
<< "\tid " << i
<< "\t\t# do not change unnecessarily\n";
96 print_bucket_class_ids(out
, i
, crush
);
99 print_fixedpoint(out
, crush
.get_bucket_weight(i
));
102 int n
= crush
.get_bucket_size(i
);
104 int alg
= crush
.get_bucket_alg(i
);
105 out
<< "\talg " << crush_bucket_alg_name(alg
);
107 // notate based on alg type
110 case CRUSH_BUCKET_UNIFORM
:
111 out
<< "\t# do not change bucket size (" << n
<< ") unnecessarily";
114 case CRUSH_BUCKET_LIST
:
115 out
<< "\t# add new items at the end; do not change order unnecessarily";
117 case CRUSH_BUCKET_TREE
:
118 out
<< "\t# do not change pos for existing items unnecessarily";
124 int hash
= crush
.get_bucket_hash(i
);
125 out
<< "\thash " << hash
<< "\t# " << crush_hash_name(hash
) << "\n";
127 for (int j
=0; j
<n
; j
++) {
128 int item
= crush
.get_bucket_item(i
, j
);
129 int w
= crush
.get_bucket_item_weight(i
, j
);
131 print_item_name(out
, item
, crush
);
133 print_fixedpoint(out
, w
);
143 /* Basically, we just descend recursively into all of the buckets,
144 * executing a depth-first traversal of the graph. Since the buckets form a
145 * directed acyclic graph, this should work just fine. The graph isn't
146 * necessarily a tree, so we have to keep track of what buckets we already
147 * outputted. We don't want to output anything twice. We also keep track of
148 * what buckets are in progress so that we can detect cycles. These can
149 * arise through user error.
151 int CrushCompiler::decompile_bucket(int cur
,
152 std::map
<int, dcb_state_t
>& dcb_states
,
155 if ((cur
== 0) || (!crush
.bucket_exists(cur
)))
158 std::map
<int, dcb_state_t
>::iterator c
= dcb_states
.find(cur
);
159 if (c
== dcb_states
.end()) {
160 // Mark this bucket as "in progress."
161 std::map
<int, dcb_state_t
>::value_type
val(cur
, DCB_STATE_IN_PROGRESS
);
162 std::pair
<std::map
<int, dcb_state_t
>::iterator
, bool> rval
163 (dcb_states
.insert(val
));
167 else if (c
->second
== DCB_STATE_DONE
) {
168 // We already did this bucket.
171 else if (c
->second
== DCB_STATE_IN_PROGRESS
) {
172 err
<< "decompile_crush_bucket: logic error: tried to decompile "
173 "a bucket that is already being decompiled" << std::endl
;
177 err
<< "decompile_crush_bucket: logic error: illegal bucket state! "
178 << c
->second
<< std::endl
;
182 int bsize
= crush
.get_bucket_size(cur
);
183 for (int i
= 0; i
< bsize
; ++i
) {
184 int item
= crush
.get_bucket_item(cur
, i
);
185 std::map
<int, dcb_state_t
>::iterator d
= dcb_states
.find(item
);
186 if (d
== dcb_states
.end()) {
187 int ret
= decompile_bucket(item
, dcb_states
, out
);
191 else if (d
->second
== DCB_STATE_IN_PROGRESS
) {
192 err
<< "decompile_crush_bucket: error: while trying to output bucket "
193 << cur
<< ", we found out that it contains one of the buckets that "
194 << "contain it. This is not allowed. The buckets must form a "
195 << "directed acyclic graph." << std::endl
;
198 else if (d
->second
!= DCB_STATE_DONE
) {
199 err
<< "decompile_crush_bucket: logic error: illegal bucket state "
200 << d
->second
<< std::endl
;
204 decompile_bucket_impl(cur
, out
);
205 c
->second
= DCB_STATE_DONE
;
209 int CrushCompiler::decompile_weight_set_weights(crush_weight_set weight_set
,
213 for (__u32 i
= 0; i
< weight_set
.size
; i
++) {
214 print_fixedpoint(out
, weight_set
.weights
[i
]);
221 int CrushCompiler::decompile_weight_set(crush_weight_set
*weight_set
,
225 out
<< " weight_set [\n";
226 for (__u32 i
= 0; i
< size
; i
++) {
227 int r
= decompile_weight_set_weights(weight_set
[i
], out
);
235 int CrushCompiler::decompile_ids(__s32
*ids
,
240 for (__u32 i
= 0; i
< size
; i
++)
241 out
<< ids
[i
] << " ";
246 int CrushCompiler::decompile_choose_arg(crush_choose_arg
*arg
,
252 out
<< " bucket_id " << bucket_id
<< "\n";
253 if (arg
->weight_set_size
> 0) {
254 r
= decompile_weight_set(arg
->weight_set
, arg
->weight_set_size
, out
);
258 if (arg
->ids_size
> 0) {
259 r
= decompile_ids(arg
->ids
, arg
->ids_size
, out
);
267 int CrushCompiler::decompile_choose_arg_map(crush_choose_arg_map arg_map
,
270 for (__u32 i
= 0; i
< arg_map
.size
; i
++) {
271 if ((arg_map
.args
[i
].ids_size
== 0) &&
272 (arg_map
.args
[i
].weight_set_size
== 0))
274 int r
= decompile_choose_arg(&arg_map
.args
[i
], -1-i
, out
);
281 int CrushCompiler::decompile_choose_args(const std::pair
<const long unsigned int, crush_choose_arg_map
> &i
,
284 out
<< "choose_args " << i
.first
<< " {\n";
285 int r
= decompile_choose_arg_map(i
.second
, out
);
292 int CrushCompiler::decompile(ostream
&out
)
294 out
<< "# begin crush map\n";
296 // only dump tunables if they differ from the defaults
297 if (crush
.get_choose_local_tries() != 2)
298 out
<< "tunable choose_local_tries " << crush
.get_choose_local_tries() << "\n";
299 if (crush
.get_choose_local_fallback_tries() != 5)
300 out
<< "tunable choose_local_fallback_tries " << crush
.get_choose_local_fallback_tries() << "\n";
301 if (crush
.get_choose_total_tries() != 19)
302 out
<< "tunable choose_total_tries " << crush
.get_choose_total_tries() << "\n";
303 if (crush
.get_chooseleaf_descend_once() != 0)
304 out
<< "tunable chooseleaf_descend_once " << crush
.get_chooseleaf_descend_once() << "\n";
305 if (crush
.get_chooseleaf_vary_r() != 0)
306 out
<< "tunable chooseleaf_vary_r " << crush
.get_chooseleaf_vary_r() << "\n";
307 if (crush
.get_chooseleaf_stable() != 0)
308 out
<< "tunable chooseleaf_stable " << crush
.get_chooseleaf_stable() << "\n";
309 if (crush
.get_straw_calc_version() != 0)
310 out
<< "tunable straw_calc_version " << crush
.get_straw_calc_version() << "\n";
311 if (crush
.get_allowed_bucket_algs() != CRUSH_LEGACY_ALLOWED_BUCKET_ALGS
)
312 out
<< "tunable allowed_bucket_algs " << crush
.get_allowed_bucket_algs()
315 out
<< "\n# devices\n";
316 for (int i
=0; i
<crush
.get_max_devices(); i
++) {
317 out
<< "device " << i
<< " ";
318 print_item_name(out
, i
, crush
);
319 print_item_class(out
, i
, crush
);
323 out
<< "\n# types\n";
324 int n
= crush
.get_num_type_names();
325 for (int i
=0; n
; i
++) {
326 const char *name
= crush
.get_type_name(i
);
328 if (i
== 0) out
<< "type 0 osd\n";
332 out
<< "type " << i
<< " " << name
<< "\n";
335 out
<< "\n# buckets\n";
336 std::map
<int, dcb_state_t
> dcb_states
;
337 for (int bucket
= -1; bucket
> -1-crush
.get_max_buckets(); --bucket
) {
338 int ret
= decompile_bucket(bucket
, dcb_states
, out
);
343 out
<< "\n# rules\n";
344 for (int i
=0; i
<crush
.get_max_rules(); i
++) {
345 if (!crush
.rule_exists(i
))
348 if (crush
.get_rule_name(i
))
349 print_rule_name(out
, i
, crush
);
351 out
<< "\tid " << i
<< "\n";
352 if (i
!= crush
.get_rule_mask_ruleset(i
)) {
353 out
<< "\t# WARNING: ruleset " << crush
.get_rule_mask_ruleset(i
) << " != id " << i
<< "; this will not recompile to the same map\n";
356 switch (crush
.get_rule_mask_type(i
)) {
357 case CEPH_PG_TYPE_REPLICATED
:
358 out
<< "\ttype replicated\n";
360 case CEPH_PG_TYPE_ERASURE
:
361 out
<< "\ttype erasure\n";
364 out
<< "\ttype " << crush
.get_rule_mask_type(i
) << "\n";
367 out
<< "\tmin_size " << crush
.get_rule_mask_min_size(i
) << "\n";
368 out
<< "\tmax_size " << crush
.get_rule_mask_max_size(i
) << "\n";
370 for (int j
=0; j
<crush
.get_rule_len(i
); j
++) {
371 switch (crush
.get_rule_op(i
, j
)) {
372 case CRUSH_RULE_NOOP
:
373 out
<< "\tstep noop\n";
375 case CRUSH_RULE_TAKE
:
376 out
<< "\tstep take ";
378 int step_item
= crush
.get_rule_arg1(i
, j
);
381 int res
= crush
.split_id_class(step_item
, &original_item
, &c
);
385 step_item
= original_item
;
386 print_item_name(out
, step_item
, crush
);
388 print_class(out
, c
, crush
);
392 case CRUSH_RULE_EMIT
:
393 out
<< "\tstep emit\n";
395 case CRUSH_RULE_SET_CHOOSE_TRIES
:
396 out
<< "\tstep set_choose_tries " << crush
.get_rule_arg1(i
, j
)
399 case CRUSH_RULE_SET_CHOOSE_LOCAL_TRIES
:
400 out
<< "\tstep set_choose_local_tries " << crush
.get_rule_arg1(i
, j
)
403 case CRUSH_RULE_SET_CHOOSE_LOCAL_FALLBACK_TRIES
:
404 out
<< "\tstep set_choose_local_fallback_tries " << crush
.get_rule_arg1(i
, j
)
407 case CRUSH_RULE_SET_CHOOSELEAF_TRIES
:
408 out
<< "\tstep set_chooseleaf_tries " << crush
.get_rule_arg1(i
, j
)
411 case CRUSH_RULE_SET_CHOOSELEAF_VARY_R
:
412 out
<< "\tstep set_chooseleaf_vary_r " << crush
.get_rule_arg1(i
, j
)
415 case CRUSH_RULE_SET_CHOOSELEAF_STABLE
:
416 out
<< "\tstep set_chooseleaf_stable " << crush
.get_rule_arg1(i
, j
)
419 case CRUSH_RULE_CHOOSE_FIRSTN
:
420 out
<< "\tstep choose firstn "
421 << crush
.get_rule_arg1(i
, j
)
423 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
426 case CRUSH_RULE_CHOOSE_INDEP
:
427 out
<< "\tstep choose indep "
428 << crush
.get_rule_arg1(i
, j
)
430 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
433 case CRUSH_RULE_CHOOSELEAF_FIRSTN
:
434 out
<< "\tstep chooseleaf firstn "
435 << crush
.get_rule_arg1(i
, j
)
437 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
440 case CRUSH_RULE_CHOOSELEAF_INDEP
:
441 out
<< "\tstep chooseleaf indep "
442 << crush
.get_rule_arg1(i
, j
)
444 print_type_name(out
, crush
.get_rule_arg2(i
, j
), crush
);
451 if (crush
.choose_args
.size() > 0) {
452 out
<< "\n# choose_args\n";
453 for (auto i
: crush
.choose_args
) {
454 int ret
= decompile_choose_args(i
, out
);
459 out
<< "\n# end crush map" << std::endl
;
464 // ================================================================
466 string
CrushCompiler::string_node(node_t
&node
)
468 return boost::trim_copy(string(node
.value
.begin(), node
.value
.end()));
471 int CrushCompiler::int_node(node_t
&node
)
473 string str
= string_node(node
);
474 return strtol(str
.c_str(), 0, 10);
477 float CrushCompiler::float_node(node_t
&node
)
479 string s
= string_node(node
);
480 return strtof(s
.c_str(), 0);
483 int CrushCompiler::parse_device(iter_t
const& i
)
485 int id
= int_node(i
->children
[1]);
487 string name
= string_node(i
->children
[2]);
488 crush
.set_item_name(id
, name
.c_str());
489 if (item_id
.count(name
)) {
490 err
<< "item " << name
<< " defined twice" << std::endl
;
496 if (verbose
) err
<< "device " << id
<< " '" << name
<< "'";
498 if (i
->children
.size() > 3) {
499 string c
= string_node(i
->children
[4]);
500 crush
.set_item_class(id
, c
);
501 if (verbose
) err
<< " class" << " '" << c
<< "'" << std::endl
;
503 if (verbose
) err
<< std::endl
;
508 int CrushCompiler::parse_tunable(iter_t
const& i
)
510 string name
= string_node(i
->children
[1]);
511 int val
= int_node(i
->children
[2]);
513 if (name
== "choose_local_tries")
514 crush
.set_choose_local_tries(val
);
515 else if (name
== "choose_local_fallback_tries")
516 crush
.set_choose_local_fallback_tries(val
);
517 else if (name
== "choose_total_tries")
518 crush
.set_choose_total_tries(val
);
519 else if (name
== "chooseleaf_descend_once")
520 crush
.set_chooseleaf_descend_once(val
);
521 else if (name
== "chooseleaf_vary_r")
522 crush
.set_chooseleaf_vary_r(val
);
523 else if (name
== "chooseleaf_stable")
524 crush
.set_chooseleaf_stable(val
);
525 else if (name
== "straw_calc_version")
526 crush
.set_straw_calc_version(val
);
527 else if (name
== "allowed_bucket_algs")
528 crush
.set_allowed_bucket_algs(val
);
530 err
<< "tunable " << name
<< " not recognized" << std::endl
;
536 current crop of tunables are all now "safe". re-enable this when we
537 add new ones that are ... new.
539 if (!unsafe_tunables) {
540 err << "tunables are NOT FULLY IMPLEMENTED; enable with --enable-unsafe-tunables to enable this feature" << std::endl;
545 if (verbose
) err
<< "tunable " << name
<< " " << val
<< std::endl
;
549 int CrushCompiler::parse_bucket_type(iter_t
const& i
)
551 int id
= int_node(i
->children
[1]);
552 string name
= string_node(i
->children
[2]);
553 if (verbose
) err
<< "type " << id
<< " '" << name
<< "'" << std::endl
;
555 crush
.set_type_name(id
, name
.c_str());
559 int CrushCompiler::parse_bucket(iter_t
const& i
)
561 string tname
= string_node(i
->children
[0]);
562 if (!type_id
.count(tname
)) {
563 err
<< "bucket type '" << tname
<< "' is not defined" << std::endl
;
566 int type
= type_id
[tname
];
568 string name
= string_node(i
->children
[1]);
569 if (item_id
.count(name
)) {
570 err
<< "bucket or device '" << name
<< "' is already defined" << std::endl
;
574 int id
= 0; // none, yet!
579 map
<int32_t, int32_t> class_id
;
581 for (unsigned p
=3; p
<i
->children
.size()-1; p
++) {
582 iter_t sub
= i
->children
.begin() + p
;
583 string tag
= string_node(sub
->children
[0]);
584 //err << "tag " << tag << std::endl;
586 int maybe_id
= int_node(sub
->children
[1]);
587 if (verbose
) err
<< "bucket " << name
<< " id " << maybe_id
;
588 if (sub
->children
.size() > 2) {
589 string class_name
= string_node(sub
->children
[3]);
590 if (!crush
.class_exists(class_name
)) {
591 err
<< " unknown device class '" << class_name
<< "'" << std::endl
;
594 int cid
= crush
.get_class_id(class_name
);
595 if (class_id
.count(cid
) != 0) {
596 err
<< "duplicate device class " << class_name
<< " for bucket " << name
<< std::endl
;
599 class_id
[cid
] = maybe_id
;
600 if (verbose
) err
<< " class" << " '" << class_name
<< "'" << std::endl
;
603 if (verbose
) err
<< std::endl
;
605 } else if (tag
== "alg") {
606 string a
= string_node(sub
->children
[1]);
608 alg
= CRUSH_BUCKET_UNIFORM
;
609 else if (a
== "list")
610 alg
= CRUSH_BUCKET_LIST
;
611 else if (a
== "tree")
612 alg
= CRUSH_BUCKET_TREE
;
613 else if (a
== "straw")
614 alg
= CRUSH_BUCKET_STRAW
;
615 else if (a
== "straw2")
616 alg
= CRUSH_BUCKET_STRAW2
;
618 err
<< "unknown bucket alg '" << a
<< "'" << std::endl
<< std::endl
;
622 else if (tag
== "hash") {
623 string a
= string_node(sub
->children
[1]);
624 if (a
== "rjenkins1")
625 hash
= CRUSH_HASH_RJENKINS1
;
627 hash
= atoi(a
.c_str());
629 else if (tag
== "item") {
630 // first, just determine which item pos's are already used
632 for (unsigned q
= 2; q
< sub
->children
.size(); q
++) {
633 string tag
= string_node(sub
->children
[q
++]);
635 int pos
= int_node(sub
->children
[q
]);
636 if (used_items
.count(pos
)) {
637 err
<< "item '" << string_node(sub
->children
[1]) << "' in bucket '" << name
<< "' has explicit pos " << pos
<< ", which is occupied" << std::endl
;
640 used_items
.insert(pos
);
648 if (!used_items
.empty())
649 size
= MAX(size
, *used_items
.rbegin());
650 vector
<int> items(size
);
651 vector
<int> weights(size
);
654 unsigned bucketweight
= 0;
655 bool have_uniform_weight
= false;
656 unsigned uniform_weight
= 0;
657 for (unsigned p
=3; p
<i
->children
.size()-1; p
++) {
658 iter_t sub
= i
->children
.begin() + p
;
659 string tag
= string_node(sub
->children
[0]);
662 string iname
= string_node(sub
->children
[1]);
663 if (!item_id
.count(iname
)) {
664 err
<< "item '" << iname
<< "' in bucket '" << name
<< "' is not defined" << std::endl
;
667 int itemid
= item_id
[iname
];
669 unsigned weight
= 0x10000;
670 if (item_weight
.count(itemid
))
671 weight
= item_weight
[itemid
];
674 for (unsigned q
= 2; q
< sub
->children
.size(); q
++) {
675 string tag
= string_node(sub
->children
[q
++]);
676 if (tag
== "weight") {
677 weight
= float_node(sub
->children
[q
]) * (float)0x10000;
678 if (weight
> CRUSH_MAX_DEVICE_WEIGHT
&& itemid
>= 0) {
679 err
<< "device weight limited to " << CRUSH_MAX_DEVICE_WEIGHT
/ 0x10000 << std::endl
;
682 else if (weight
> CRUSH_MAX_BUCKET_WEIGHT
&& itemid
< 0) {
683 err
<< "bucket weight limited to " << CRUSH_MAX_BUCKET_WEIGHT
/ 0x10000
684 << " to prevent overflow" << std::endl
;
688 else if (tag
== "pos")
689 pos
= int_node(sub
->children
[q
]);
694 if (alg
== CRUSH_BUCKET_UNIFORM
) {
695 if (!have_uniform_weight
) {
696 have_uniform_weight
= true;
697 uniform_weight
= weight
;
699 if (uniform_weight
!= weight
) {
700 err
<< "item '" << iname
<< "' in uniform bucket '" << name
<< "' has weight " << weight
701 << " but previous item(s) have weight " << (float)uniform_weight
/(float)0x10000
702 << "; uniform bucket items must all have identical weights." << std::endl
;
709 err
<< "item '" << iname
<< "' in bucket '" << name
<< "' has pos " << pos
<< " >= size " << size
<< std::endl
;
713 while (used_items
.count(curpos
)) curpos
++;
716 //err << " item " << iname << " (" << itemid << ") pos " << pos << " weight " << weight << std::endl;
718 weights
[pos
] = weight
;
720 if (crush_addition_is_unsafe(bucketweight
, weight
)) {
721 err
<< "oh no! our bucket weights are overflowing all over the place, better lower the item weights" << std::endl
;
725 bucketweight
+= weight
;
730 for (id
=-1; id_item
.count(id
); id
--) ;
731 //err << "assigned id " << id << std::endl;
734 for (auto &i
: class_id
)
735 class_bucket
[id
][i
.first
] = i
.second
;
737 if (verbose
) err
<< "bucket " << name
<< " (" << id
<< ") " << size
<< " items and weight "
738 << (float)bucketweight
/ (float)0x10000 << std::endl
;
741 item_weight
[id
] = bucketweight
;
744 int r
= crush
.add_bucket(id
, alg
, hash
, type
, size
, &items
[0], &weights
[0], NULL
);
747 err
<< "Duplicate bucket id " << id
<< std::endl
;
749 err
<< "add_bucket failed " << cpp_strerror(r
) << std::endl
;
752 r
= crush
.set_item_name(id
, name
.c_str());
756 int CrushCompiler::parse_rule(iter_t
const& i
)
758 int start
; // rule name is optional!
760 string rname
= string_node(i
->children
[1]);
762 if (rule_id
.count(rname
)) {
763 err
<< "rule name '" << rname
<< "' already defined\n" << std::endl
;
772 int ruleno
= int_node(i
->children
[start
]);
774 string tname
= string_node(i
->children
[start
+2]);
776 if (tname
== "replicated")
777 type
= CEPH_PG_TYPE_REPLICATED
;
778 else if (tname
== "erasure")
779 type
= CEPH_PG_TYPE_ERASURE
;
783 int minsize
= int_node(i
->children
[start
+4]);
784 int maxsize
= int_node(i
->children
[start
+6]);
786 int steps
= i
->children
.size() - start
- 8;
787 //err << "num steps " << steps << std::endl;
789 if (crush
.rule_exists(ruleno
)) {
790 err
<< "rule " << ruleno
<< " already exists" << std::endl
;
793 int r
= crush
.add_rule(ruleno
, steps
, type
, minsize
, maxsize
);
795 err
<< "unable to add rule id " << ruleno
<< " for rule '" << rname
799 if (rname
.length()) {
800 crush
.set_rule_name(ruleno
, rname
.c_str());
801 rule_id
[rname
] = ruleno
;
805 for (iter_t p
= i
->children
.begin() + start
+ 7; step
< steps
; p
++) {
806 iter_t s
= p
->children
.begin() + 1;
807 int stepid
= s
->value
.id().to_long();
809 case crush_grammar::_step_take
:
811 string item
= string_node(s
->children
[1]);
812 if (!item_id
.count(item
)) {
813 err
<< "in rule '" << rname
<< "' item '" << item
<< "' not defined" << std::endl
;
816 int id
= item_id
[item
];
819 if (s
->children
.size() > 2) {
820 class_name
= string_node(s
->children
[3]);
821 c
= crush
.get_class_id(class_name
);
824 if (crush
.class_bucket
.count(id
) == 0) {
825 err
<< "in rule '" << rname
<< "' step take " << item
826 << " has no class information" << std::endl
;
829 if (crush
.class_bucket
[id
].count(c
) == 0) {
830 err
<< "in rule '" << rname
<< "' step take " << item
831 << " no matching bucket for class " << class_name
<< std::endl
;
834 id
= crush
.class_bucket
[id
][c
];
837 err
<< "rule " << rname
<< " take " << item
;
841 err
<< " remapped to " << crush
.get_item_name(id
) << std::endl
;
844 crush
.set_rule_step_take(ruleno
, step
++, id
);
848 case crush_grammar::_step_set_choose_tries
:
850 int val
= int_node(s
->children
[1]);
851 crush
.set_rule_step_set_choose_tries(ruleno
, step
++, val
);
855 case crush_grammar::_step_set_choose_local_tries
:
857 int val
= int_node(s
->children
[1]);
858 crush
.set_rule_step_set_choose_local_tries(ruleno
, step
++, val
);
862 case crush_grammar::_step_set_choose_local_fallback_tries
:
864 int val
= int_node(s
->children
[1]);
865 crush
.set_rule_step_set_choose_local_fallback_tries(ruleno
, step
++, val
);
869 case crush_grammar::_step_set_chooseleaf_tries
:
871 int val
= int_node(s
->children
[1]);
872 crush
.set_rule_step_set_chooseleaf_tries(ruleno
, step
++, val
);
876 case crush_grammar::_step_set_chooseleaf_vary_r
:
878 int val
= int_node(s
->children
[1]);
879 crush
.set_rule_step_set_chooseleaf_vary_r(ruleno
, step
++, val
);
883 case crush_grammar::_step_set_chooseleaf_stable
:
885 int val
= int_node(s
->children
[1]);
886 crush
.set_rule_step_set_chooseleaf_stable(ruleno
, step
++, val
);
890 case crush_grammar::_step_choose
:
891 case crush_grammar::_step_chooseleaf
:
893 string type
= string_node(s
->children
[4]);
894 if (!type_id
.count(type
)) {
895 err
<< "in rule '" << rname
<< "' type '" << type
<< "' not defined" << std::endl
;
898 string choose
= string_node(s
->children
[0]);
899 string mode
= string_node(s
->children
[1]);
900 if (choose
== "choose") {
901 if (mode
== "firstn")
902 crush
.set_rule_step_choose_firstn(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
903 else if (mode
== "indep")
904 crush
.set_rule_step_choose_indep(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
906 } else if (choose
== "chooseleaf") {
907 if (mode
== "firstn")
908 crush
.set_rule_step_choose_leaf_firstn(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
909 else if (mode
== "indep")
910 crush
.set_rule_step_choose_leaf_indep(ruleno
, step
++, int_node(s
->children
[2]), type_id
[type
]);
916 case crush_grammar::_step_emit
:
917 crush
.set_rule_step_emit(ruleno
, step
++);
921 err
<< "bad crush step " << stepid
<< std::endl
;
925 assert(step
== steps
);
929 int CrushCompiler::parse_weight_set_weights(iter_t
const& i
, int bucket_id
, crush_weight_set
*weight_set
)
931 // -2 for the enclosing [ ]
932 __u32 size
= i
->children
.size() - 2;
933 __u32 bucket_size
= crush
.get_bucket_size(bucket_id
);
934 if (size
!= bucket_size
) {
935 err
<< bucket_id
<< " needs exactly " << bucket_size
936 << " weights but got " << size
<< std::endl
;
939 weight_set
->size
= size
;
940 weight_set
->weights
= (__u32
*)calloc(weight_set
->size
, sizeof(__u32
));
942 for (iter_t p
= i
->children
.begin() + 1; p
!= i
->children
.end(); p
++, pos
++)
944 weight_set
->weights
[pos
] = float_node(*p
) * (float)0x10000;
948 int CrushCompiler::parse_weight_set(iter_t
const& i
, int bucket_id
, crush_choose_arg
*arg
)
950 // -3 stands for the leading "weight_set" keyword and the enclosing [ ]
951 arg
->weight_set_size
= i
->children
.size() - 3;
952 arg
->weight_set
= (crush_weight_set
*)calloc(arg
->weight_set_size
, sizeof(crush_weight_set
));
954 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
956 switch((int)p
->value
.id().to_long()) {
957 case crush_grammar::_weight_set_weights
:
958 if (pos
< arg
->weight_set_size
) {
959 r
= parse_weight_set_weights(p
, bucket_id
, &arg
->weight_set
[pos
]);
962 err
<< "invalid weight_set syntax" << std::endl
;
972 int CrushCompiler::parse_choose_arg_ids(iter_t
const& i
, int bucket_id
, crush_choose_arg
*arg
)
974 // -3 for the leading "ids" keyword and the enclosing [ ]
975 __u32 size
= i
->children
.size() - 3;
976 __u32 bucket_size
= crush
.get_bucket_size(bucket_id
);
977 if (size
!= bucket_size
) {
978 err
<< bucket_id
<< " needs exactly " << bucket_size
979 << " ids but got " << size
<< std::endl
;
982 arg
->ids_size
= size
;
983 arg
->ids
= (__s32
*)calloc(arg
->ids_size
, sizeof(__s32
));
985 for (iter_t p
= i
->children
.begin() + 2; pos
< size
; p
++, pos
++)
986 arg
->ids
[pos
] = int_node(*p
);
990 int CrushCompiler::parse_choose_arg(iter_t
const& i
, crush_choose_arg
*args
)
992 int bucket_id
= int_node(i
->children
[2]);
993 if (-1-bucket_id
< 0 || -1-bucket_id
>= crush
.get_max_buckets()) {
994 err
<< bucket_id
<< " is out of range" << std::endl
;
997 if (!crush
.bucket_exists(bucket_id
)) {
998 err
<< bucket_id
<< " does not exist" << std::endl
;
1001 crush_choose_arg
*arg
= &args
[-1-bucket_id
];
1002 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1004 switch((int)p
->value
.id().to_long()) {
1005 case crush_grammar::_weight_set
:
1006 r
= parse_weight_set(p
, bucket_id
, arg
);
1008 case crush_grammar::_choose_arg_ids
:
1009 r
= parse_choose_arg_ids(p
, bucket_id
, arg
);
1018 int CrushCompiler::parse_choose_args(iter_t
const& i
)
1020 int choose_arg_index
= int_node(i
->children
[1]);
1021 if (crush
.choose_args
.find(choose_arg_index
) != crush
.choose_args
.end()) {
1022 err
<< choose_arg_index
<< " duplicated" << std::endl
;
1025 crush_choose_arg_map arg_map
;
1026 arg_map
.size
= crush
.get_max_buckets();
1027 arg_map
.args
= (crush_choose_arg
*)calloc(arg_map
.size
, sizeof(crush_choose_arg
));
1028 for (iter_t p
= i
->children
.begin() + 2; p
!= i
->children
.end(); p
++) {
1030 switch((int)p
->value
.id().to_long()) {
1031 case crush_grammar::_choose_arg
:
1032 r
= parse_choose_arg(p
, arg_map
.args
);
1036 crush
.destroy_choose_args(arg_map
);
1040 crush
.choose_args
[choose_arg_index
] = arg_map
;
1044 void CrushCompiler::find_used_bucket_ids(iter_t
const& i
)
1046 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1047 if ((int)p
->value
.id().to_long() == crush_grammar::_bucket
) {
1048 iter_t firstline
= p
->children
.begin() + 3;
1049 string tag
= string_node(firstline
->children
[0]);
1051 int id
= int_node(firstline
->children
[1]);
1052 //err << "saw bucket id " << id << std::endl;
1053 id_item
[id
] = string();
1059 int CrushCompiler::parse_crush(iter_t
const& i
)
1061 find_used_bucket_ids(i
);
1062 bool saw_rule
= false;
1063 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); p
++) {
1065 switch (p
->value
.id().to_long()) {
1066 case crush_grammar::_tunable
:
1067 r
= parse_tunable(p
);
1069 case crush_grammar::_device
:
1070 r
= parse_device(p
);
1072 case crush_grammar::_bucket_type
:
1073 r
= parse_bucket_type(p
);
1075 case crush_grammar::_bucket
:
1077 err
<< "buckets must be defined before rules" << std::endl
;
1080 r
= parse_bucket(p
);
1082 case crush_grammar::_crushrule
:
1085 crush
.populate_classes(class_bucket
);
1089 case crush_grammar::_choose_args
:
1090 r
= parse_choose_args(p
);
1100 //err << "max_devices " << crush.get_max_devices() << std::endl;
1106 // squash runs of whitespace to one space, excepting newlines
1107 string
CrushCompiler::consolidate_whitespace(string in
)
1112 for (unsigned p
=0; p
<in
.length(); p
++) {
1113 if (isspace(in
[p
]) && in
[p
] != '\n') {
1119 if (out
.length()) out
+= " ";
1126 err
<< " \"" << in
<< "\" -> \"" << out
<< "\"" << std::endl
;
1130 void CrushCompiler::dump(iter_t
const& i
, int ind
)
1133 for (int j
=0; j
<ind
; j
++)
1135 long id
= i
->value
.id().to_long();
1137 err
<< "'" << string(i
->value
.begin(), i
->value
.end())
1138 << "' " << i
->children
.size() << " children" << std::endl
;
1139 for (unsigned int j
= 0; j
< i
->children
.size(); j
++)
1140 dump(i
->children
.begin() + j
, ind
+1);
1144 * This function fix the problem like below
1145 * rack using_foo { item foo }
1148 * if an item being used by a bucket is defined after that bucket.
1149 * CRUSH compiler will create a map by which we can
1150 * not identify that item when selecting in that bucket.
1152 int CrushCompiler::adjust_bucket_item_place(iter_t
const &i
)
1154 map
<string
,set
<string
> > bucket_items
;
1155 map
<string
,iter_t
> bucket_itrer
;
1156 vector
<string
> buckets
;
1157 for (iter_t p
= i
->children
.begin(); p
!= i
->children
.end(); ++p
) {
1158 if ((int)p
->value
.id().to_long() == crush_grammar::_bucket
) {
1159 string name
= string_node(p
->children
[1]);
1160 buckets
.push_back(name
);
1161 bucket_itrer
[name
] = p
;
1162 //skip non-bucket-item children in the bucket's parse tree
1163 for (unsigned q
=3; q
< p
->children
.size()-1; ++q
) {
1164 iter_t sub
= p
->children
.begin() + q
;
1165 if ((int)sub
->value
.id().to_long()
1166 == crush_grammar::_bucket_item
) {
1167 string iname
= string_node(sub
->children
[1]);
1168 bucket_items
[name
].insert(iname
);
1175 for (unsigned i
=0; i
< buckets
.size(); ++i
) {
1176 for (unsigned j
=i
+1; j
< buckets
.size(); ++j
) {
1177 if (bucket_items
[buckets
[i
]].count(buckets
[j
])) {
1178 if (bucket_items
[buckets
[j
]].count(buckets
[i
])) {
1179 err
<< "bucket '" << buckets
[i
] << "' and bucket '"
1180 << buckets
[j
] << "' are included each other" << std::endl
;
1183 std::iter_swap(bucket_itrer
[buckets
[i
]], bucket_itrer
[buckets
[j
]]);
1192 int CrushCompiler::compile(istream
& in
, const char *infn
)
1197 // always start with legacy tunables, so that the compiled result of
1198 // a given crush file is fixed for all time.
1199 crush
.set_tunables_legacy();
1204 map
<int,int> line_pos
; // pos -> line
1205 map
<int,string
> line_val
;
1206 while (getline(in
, str
)) {
1208 int l
= str
.length();
1209 if (l
&& str
[l
- 1] == '\n')
1212 line_val
[line
] = str
;
1215 int n
= str
.find("#");
1217 str
.erase(n
, str
.length()-n
);
1219 if (verbose
>1) err
<< line
<< ": " << str
<< std::endl
;
1221 // work around spirit crankiness by removing extraneous
1222 // whitespace. there is probably a more elegant solution, but
1223 // this only broke with the latest spirit (with the switchover to
1224 // "classic"), i don't want to spend too much time figuring it
1226 string stripped
= consolidate_whitespace(str
);
1227 if (stripped
.length() && big
.length() && big
[big
.length()-1] != ' ') big
+= " ";
1229 line_pos
[big
.length()] = line
;
1234 if (verbose
> 2) err
<< "whole file is: \"" << big
<< "\"" << std::endl
;
1236 crush_grammar crushg
;
1237 const char *start
= big
.c_str();
1238 //tree_parse_info<const char *> info = ast_parse(start, crushg, space_p);
1239 tree_parse_info
<> info
= ast_parse(start
, crushg
, space_p
);
1243 int cpos
= info
.stop
- start
;
1244 //out << "cpos " << cpos << std::endl;
1245 //out << " linemap " << line_pos << std::endl;
1246 assert(!line_pos
.empty());
1247 map
<int,int>::iterator p
= line_pos
.upper_bound(cpos
);
1248 if (p
!= line_pos
.begin())
1250 int line
= p
->second
;
1251 int pos
= cpos
- p
->first
;
1252 err
<< infn
<< ":" << line
//<< ":" << (pos+1)
1253 << " error: parse error at '" << line_val
[line
].substr(pos
) << "'" << std::endl
;
1257 int r
= adjust_bucket_item_place(info
.trees
.begin());
1261 //out << "parsing succeeded\n";
1262 //dump(info.trees.begin());
1263 return parse_crush(info
.trees
.begin());