From 1ed7d25614ad708ab5249b5ccfdc2036310ec68b Mon Sep 17 00:00:00 2001 From: sageweil Date: Wed, 17 Oct 2007 22:29:19 +0000 Subject: [PATCH] more crush2 git-svn-id: https://ceph.svn.sf.net/svnroot/ceph@1960 29311d96-e01e-0410-9327-a35deaab8ce9 --- trunk/ceph/crush2/Makefile | 2 +- trunk/ceph/crush2/buckets.c | 92 +++++--- trunk/ceph/crush2/buckets.h | 44 ++-- trunk/ceph/crush2/builder.c | 304 ++++++++++++++++++++++++++ trunk/ceph/crush2/crush.c | 417 ++++++++++++++++++------------------ trunk/ceph/crush2/crush.h | 46 ++-- trunk/ceph/crush2/hash.h | 90 ++++---- trunk/ceph/crush2/types.h | 7 + 8 files changed, 672 insertions(+), 330 deletions(-) create mode 100644 trunk/ceph/crush2/builder.c diff --git a/trunk/ceph/crush2/Makefile b/trunk/ceph/crush2/Makefile index 7db40f789198e..6a9bae06f0ee5 100644 --- a/trunk/ceph/crush2/Makefile +++ b/trunk/ceph/crush2/Makefile @@ -5,7 +5,7 @@ CFLAGS += -O3 -g LD = ld RM = rm -all: depend libcrush.o +all: depend builder.o libcrush.o clean: rm -f *.o libcrush.o diff --git a/trunk/ceph/crush2/buckets.c b/trunk/ceph/crush2/buckets.c index 2a2e170bbbb6c..557dc68d00778 100644 --- a/trunk/ceph/crush2/buckets.c +++ b/trunk/ceph/crush2/buckets.c @@ -5,52 +5,78 @@ int crush_bucket_uniform_choose(struct crush_bucket_uniform *bucket, int x, int r) { - unsigned o, p, s; - o = crush_hash32_2(x, bucket->h.id); - p = bucket->primes[crush_hash32_2(bucket->h.id, x) % bucket->h.size]; - s = (x + o + (r+1)*p) % bucket->h.size; - return bucket->h.items[s]; + unsigned o, p, s; + o = crush_hash32_2(x, bucket->h.id); + p = bucket->primes[crush_hash32_2(bucket->h.id, x) % bucket->h.size]; + s = (x + o + (r+1)*p) % bucket->h.size; + return bucket->h.items[s]; } int crush_bucket_list_choose(struct crush_bucket_list *bucket, int x, int r) { - int i; - __u64 w; - - for (i=0; ih.size; i++) { - w = crush_hash32_4(x, bucket->h.items[i], r, bucket->h.id) & 0xffff; - w = (w * bucket->sum_weights[i]) >> 32; - if (w < bucket->item_weights[i]) - return bucket->h.items[i]; - } - - BUG_ON(1); - return 0; + int i; + __u64 w; + + for (i=0; ih.size; i++) { + w = crush_hash32_4(x, bucket->h.items[i], r, bucket->h.id) & 0xffff; + w = (w * bucket->sum_weights[i]) >> 32; + if (w < bucket->item_weights[i]) + return bucket->h.items[i]; + } + + BUG_ON(1); + return 0; } int crush_bucket_tree_choose(struct crush_bucket_tree *bucket, int x, int r) { - return 0; + return 0; } int crush_bucket_straw_choose(struct crush_bucket_straw *bucket, int x, int r) { - int i; - int high = 0; - unsigned high_draw = 0; - __u64 draw; - - for (i=0; ih.size; i++) { - draw = (crush_hash32_3(x, bucket->h.items[i], r) & 0xffff) * bucket->straws[i]; - draw = draw >> 32; - if (i == 0 || draw > high_draw) { - high = i; - high_draw = draw; - } - } - - return high; + int i; + int high = 0; + unsigned high_draw = 0; + __u64 draw; + + for (i=0; ih.size; i++) { + draw = (crush_hash32_3(x, bucket->h.items[i], r) & 0xffff) * bucket->straws[i]; + draw = draw >> 32; + if (i == 0 || draw > high_draw) { + high = i; + high_draw = draw; + } + } + + return high; +} + + + +void crush_destroy_bucket_uniform(struct crush_bucket_uniform *b) +{ + free(b->primes); + free(b->h.items); +} + +void crush_destroy_bucket_list(struct crush_bucket_list *b) +{ + free(b->item_weights); + free(b->sum_weights); + free(b->h.items); +} + +void crush_destroy_bucket_tree(struct crush_bucket_tree *b) +{ + free(b->h.items); +} + +void crush_destroy_bucket_straw(struct crush_bucket_straw *b) +{ + free(b->straws); + free(b->h.items); } diff --git a/trunk/ceph/crush2/buckets.h b/trunk/ceph/crush2/buckets.h index c83d522159ffc..aab536b28b2f3 100644 --- a/trunk/ceph/crush2/buckets.h +++ b/trunk/ceph/crush2/buckets.h @@ -4,41 +4,40 @@ #include "types.h" enum { - CRUSH_BUCKET_UNIFORM = 1, - CRUSH_BUCKET_LIST = 2, - CRUSH_BUCKET_TREE = 3, - CRUSH_BUCKET_STRAW = 4 + CRUSH_BUCKET_UNIFORM = 1, + CRUSH_BUCKET_LIST = 2, + CRUSH_BUCKET_TREE = 3, + CRUSH_BUCKET_STRAW = 4 }; struct crush_bucket { - __u32 id; - __u32 type; - __u32 weight; /* 16-bit fixed point */ - __u32 size; /* num items */ - __s32 *items; + __u32 id; + __u32 type; + __u32 weight; /* 16-bit fixed point */ + __u32 size; /* num items */ + __s32 *items; }; struct crush_bucket_uniform { - struct crush_bucket h; - __u32 item_weight; /* 16-bit fixed point */ - __u32 item_type; - __u32 *primes; + struct crush_bucket h; + __u32 item_weight; /* 16-bit fixed point */ + __u32 *primes; }; struct crush_bucket_list { - struct crush_bucket h; - __u32 *item_weights; /* 16-bit fixed point */ - __u32 *sum_weights; /* 16-bit fixed point */ + struct crush_bucket h; + __u32 *item_weights; /* 16-bit fixed point */ + __u32 *sum_weights; /* 16-bit fixed point. element i is sum of weights 0..i, inclusive */ }; struct crush_bucket_tree { - struct crush_bucket h; - + struct crush_bucket h; + }; struct crush_bucket_straw { - struct crush_bucket h; - __u32 *straws; /* 16-bit fixed point */ + struct crush_bucket h; + __u32 *straws; /* 16-bit fixed point */ }; extern int crush_bucket_uniform_choose(struct crush_bucket_uniform *bucket, int x, int r); @@ -46,4 +45,9 @@ extern int crush_bucket_list_choose(struct crush_bucket_list *bucket, int x, int extern int crush_bucket_tree_choose(struct crush_bucket_tree *bucket, int x, int r); extern int crush_bucket_straw_choose(struct crush_bucket_straw *bucket, int x, int r); +extern void crush_destroy_bucket_uniform(struct crush_bucket_uniform *); +extern void crush_destroy_bucket_list(struct crush_bucket_list *); +extern void crush_destroy_bucket_tree(struct crush_bucket_tree *); +extern void crush_destroy_bucket_straw(struct crush_bucket_straw *); + #endif diff --git a/trunk/ceph/crush2/builder.c b/trunk/ceph/crush2/builder.c new file mode 100644 index 0000000000000..803eb9d1561a0 --- /dev/null +++ b/trunk/ceph/crush2/builder.c @@ -0,0 +1,304 @@ + +#include +#include +#include + +#include "builder.h" +#include "hash.h" + + +struct crush_map *crush_new() +{ + struct crush_map *m; + m = malloc(sizeof(*m)); + memset(&m, 0, sizeof(m)); + return m; +} + +/* + * finalize should be called _after_ all buckets are added to the map. + */ +void crush_finalize(struct crush_map *map) +{ + int b, i, c; + + /* calc max_devices */ + for (b=0; bmax_buckets; b++) { + if (map->buckets[b] == 0) continue; + for (i=0; ibuckets[b]->size; i++) + if (map->buckets[b]->items[i] > map->max_devices) + map->max_devices = map->buckets[b]->items[i]; + } + + /* allocate arrays */ + map->device_parents = malloc(sizeof(map->device_parents[0]) * map->max_devices); + map->device_offload = malloc(sizeof(map->device_offload[0]) * map->max_devices); + memset(map->device_parents, 0, sizeof(map->device_parents[0]) * map->max_devices); + memset(map->device_offload, 0, sizeof(map->device_offload[0]) * map->max_devices); + map->bucket_parents = malloc(sizeof(map->bucket_parents[0]) * map->max_buckets); + memset(map->bucket_parents, 0, sizeof(map->bucket_parents[0]) * map->max_buckets); + + /* build reverse map */ + for (b=0; bmax_buckets; b++) { + if (map->buckets[b] == 0) continue; + for (i=0; ibuckets[b]->size; i++) { + c = map->buckets[b]->items[i]; + if (c >= 0) + map->device_parents[c] = map->buckets[b]->id; + else + map->bucket_parents[-c] = map->buckets[b]->id; + } + } +} + +/* + * deallocate + */ +void crush_destroy(struct crush_map *map) +{ + int b; + + /* buckets */ + for (b=0; bmax_buckets; b++) { + if (map->buckets[b] == 0) continue; + switch (map->buckets[b]->type) { + case CRUSH_BUCKET_UNIFORM: + crush_destroy_bucket_uniform((struct crush_bucket_uniform*)map->buckets[b]); + break; + case CRUSH_BUCKET_LIST: + crush_destroy_bucket_list((struct crush_bucket_list*)map->buckets[b]); + break; + case CRUSH_BUCKET_TREE: + crush_destroy_bucket_tree((struct crush_bucket_tree*)map->buckets[b]); + break; + case CRUSH_BUCKET_STRAW: + crush_destroy_bucket_straw((struct crush_bucket_straw*)map->buckets[b]); + break; + } + } + free(map->buckets); + + /* rules */ + for (b=0; bmax_rules; b++) { + if (map->rules[b] == 0) continue; + if (map->rules[b]->steps) + free(map->rules[b]->steps); + free(map->rules[b]); + } + free(map->rules); + + free(map->bucket_parents); + free(map->device_parents); + free(map->device_offload); + + memset(map, 0, sizeof(*map)); +} + + + + +/** rules **/ + +int crush_add_rule(struct crush_map *map, + struct crush_rule *rule) +{ + int id; + + /* find a rule id */ + for (id=0; id < map->max_rules; id++) + if (map->rules[id] == 0) break; + if (id == map->max_rules) { + /* expand array */ + if (map->max_rules) + map->max_rules *= 2; + else + map->max_rules = 8; + map->rules = realloc(map->rules, map->max_rules * sizeof(map->rules[0])); + } + + /* add it */ + map->rules[id] = rule; + return id; +} + +struct crush_rule *crush_make_rule() +{ + struct crush_rule *rule; + + rule = malloc(sizeof(struct crush_rule)); + memset(&rule, 0, sizeof(rule)); + return rule; +} + +void crush_rule_add_step(struct crush_rule *rule, int op, int arg1, int arg2) +{ + rule->len++; + if (rule->steps) + rule->steps = malloc(sizeof(rule->steps[0])*rule->len); + else + rule->steps = realloc(rule->steps, sizeof(rule->steps[0])*rule->len); + rule->steps[rule->len-1].op = op; + rule->steps[rule->len-1].arg1 = arg1; + rule->steps[rule->len-1].arg2 = arg2; +} + + +/** buckets **/ + +int crush_add_bucket(struct crush_map *map, + struct crush_bucket *bucket) +{ + int id; + + /* find a bucket id */ + for (id=0; id < map->max_buckets; id++) + if (map->buckets[id] == 0) break; + if (id == map->max_buckets) { + /* expand array */ + if (map->max_buckets) + map->max_buckets *= 2; + else + map->max_buckets = 8; + map->buckets = realloc(map->buckets, map->max_buckets * sizeof(map->buckets[0])); + } + + /* add it */ + bucket->id = id; + map->buckets[id] = bucket; + return id; +} + + +void crush_make_uniform_bucket(struct crush_map *map, + struct crush_bucket_uniform *bucket, + int size, + int *items, + int item_weight) +{ + int i, j, x; + + memset(bucket, 0, sizeof(*bucket)); + bucket->h.size = size; + bucket->h.weight = size * item_weight; + + bucket->item_weight = item_weight; + + bucket->h.items = malloc(sizeof(__u32)*size); + for (i=0; ih.items[i] = items[i]; + + /* generate some primes */ + bucket->primes = malloc(sizeof(__u32)*size); + + x = size + 1; + x += crush_hash32(size) % (3*size); /* make it big */ + x |= 1; /* and odd */ + + i=0; + while (i < size) { + for (j=2; j*j <= x; j++) + if (x % j == 0) break; + if (j*j > x) + bucket->primes[i++] = x; + x += 2; + } +} + +void crush_make_list_bucket(struct crush_map *map, + struct crush_bucket_list *bucket, + int size, + int *items, + int *weights) +{ + int i; + int w; + + memset(bucket, 0, sizeof(*bucket)); + bucket->h.size = size; + + bucket->h.items = malloc(sizeof(__u32)*size); + bucket->item_weights = malloc(sizeof(__u32)*size); + bucket->sum_weights = malloc(sizeof(__u32)*size); + w = 0; + for (i=0; ih.items[i] = items[i]; + bucket->item_weights[i] = weights[i]; + w += weights[i]; + bucket->sum_weights[i] = w; + } + + bucket->h.weight = w; +} + + +void crush_make_straw_bucket(struct crush_map *map, + struct crush_bucket_straw *bucket, + int size, + int *items, + int *weights) +{ + int *reverse; + int i, j, k; + + double straw, wbelow, lastw, wnext, pbelow; + int numleft; + + memset(bucket, 0, sizeof(*bucket)); + bucket->h.size = size; + + bucket->h.items = malloc(sizeof(__u32)*size); + bucket->straws = malloc(sizeof(__u32)*size); + + bucket->h.weight = 0; + for (i=0; ih.items[i] = items[i]; + bucket->h.weight += weights[i]; + } + + /* reverse sort by weight */ + reverse = malloc(sizeof(int) * size); + reverse[0] = items[0]; + for (i=1; ij; k--) + reverse[k] = reverse[k-1]; + reverse[j] = items[i]; + } + } + if (j == i) + reverse[i] = items[i]; + } + + numleft = size; + straw = 1.0; + wbelow = 0; + lastw = 0; + + i=0; + while (i < size) { + /* set this item's straw */ + bucket->straws[reverse[i]] = straw * 0x10000; + i++; + if (i == size) break; + + /* same weight as previous? */ + if (weights[reverse[i]] == weights[reverse[i-1]]) + continue; + + /* adjust straw for next guy */ + wbelow += (((double)weights[reverse[i-1]] / (double)0x10000) - lastw) * numleft; + numleft--; + wnext = numleft * ((double)(weights[reverse[i]] - weights[reverse[i-1]]) / (double)0x10000); + pbelow = wbelow / (wbelow + wnext); + + straw *= pow((double)1.0 / pbelow, (double)1.0 / numleft); + + lastw = weights[reverse[i-1]]; + } + + free(reverse); + +} + diff --git a/trunk/ceph/crush2/crush.c b/trunk/ceph/crush2/crush.c index 3b420c43780d7..1c06ca7644399 100644 --- a/trunk/ceph/crush2/crush.c +++ b/trunk/ceph/crush2/crush.c @@ -8,229 +8,224 @@ static int crush_choose(struct crush_map *map, struct crush_bucket *bucket, int x, int numrep, int type, - int *out, int firstn, - int *outmap) + int *out, int firstn) { - int rep; - int ftotal, flocal; - int retry_rep, skip_rep; - struct crush_bucket *in = bucket; - int r; - int i; - int item; - int itemtype; - int outpos; - int collide, bad; - - outpos = 0; - - for (rep = 0; rep < numrep; rep++) { - /* keep trying until we get a non-out, non-colliding item */ - ftotal = 0; - skip_rep = 0; - - while (1) { - in = bucket; /* initial bucket */ - - /* choose through intervening buckets */ - flocal = 0; - retry_rep = 0; - - while (1) { - r = rep; - if (in->type == CRUSH_BUCKET_UNIFORM) { - /* be careful */ - if (firstn || numrep >= in->size) { - r += ftotal; /* r' = r + f_total */ - } else { - r += numrep * flocal; /* r' = r + n*f_local */ - /* make sure numrep is not a multiple of bucket size */ - if (in->size % numrep == 0) - /* shift seq once per pass through the bucket */ - r += numrep * flocal / in->size; - } - } else { - if (firstn) - r += ftotal; /* r' = r + f_total */ - else - r += numrep * flocal; /* r' = r + n*f_local */ - } - - /* bucket choose */ - switch (in->type) { - case CRUSH_BUCKET_UNIFORM: - item = crush_bucket_uniform_choose((struct crush_bucket_uniform*)in, x, r); - break; - case CRUSH_BUCKET_LIST: - item = crush_bucket_list_choose((struct crush_bucket_list*)in, x, r); - break; - case CRUSH_BUCKET_TREE: - item = crush_bucket_tree_choose((struct crush_bucket_tree*)in, x, r); - break; - case CRUSH_BUCKET_STRAW: - item = crush_bucket_straw_choose((struct crush_bucket_straw*)in, x, r); - break; - default: - BUG_ON(1); - } + int rep; + int ftotal, flocal; + int retry_rep, skip_rep; + struct crush_bucket *in = bucket; + int r; + int i; + int item; + int itemtype; + int outpos; + int collide, bad; - /* desired type? */ - if (in->type == CRUSH_BUCKET_UNIFORM) - itemtype = ((struct crush_bucket_uniform*)in)->item_type; - else if (item < 0) - itemtype = map->buckets[-item].type; - else - itemtype = 0; - - /* keep going? */ - if (itemtype != type) { - in = &map->buckets[-item]; - continue; - } - - /* collision? */ - collide = 0; - for (i=0; i out[item]) - bad = 1; - } - - if (bad || collide) { - ftotal++; - flocal++; - - if (collide && flocal < 3) - continue; /* locally a few times */ - if (ftotal >= 10) { - /* give up, ignore dup, fixme */ - skip_rep = 1; - break; - } - retry_rep = 1; + for (rep = 0; rep < numrep; rep++) { + /* keep trying until we get a non-out, non-colliding item */ + ftotal = 0; + skip_rep = 0; + + while (1) { + in = bucket; /* initial bucket */ + + /* choose through intervening buckets */ + flocal = 0; + retry_rep = 0; + + while (1) { + r = rep; + if (in->type == CRUSH_BUCKET_UNIFORM) { + /* be careful */ + if (firstn || numrep >= in->size) { + r += ftotal; /* r' = r + f_total */ + } else { + r += numrep * flocal; /* r' = r + n*f_local */ + /* make sure numrep is not a multiple of bucket size */ + if (in->size % numrep == 0) + /* shift seq once per pass through the bucket */ + r += numrep * flocal / in->size; + } + } else { + if (firstn) + r += ftotal; /* r' = r + f_total */ + else + r += numrep * flocal; /* r' = r + n*f_local */ + } + + /* bucket choose */ + switch (in->type) { + case CRUSH_BUCKET_UNIFORM: + item = crush_bucket_uniform_choose((struct crush_bucket_uniform*)in, x, r); + break; + case CRUSH_BUCKET_LIST: + item = crush_bucket_list_choose((struct crush_bucket_list*)in, x, r); + break; + case CRUSH_BUCKET_TREE: + item = crush_bucket_tree_choose((struct crush_bucket_tree*)in, x, r); + break; + case CRUSH_BUCKET_STRAW: + item = crush_bucket_straw_choose((struct crush_bucket_straw*)in, x, r); + break; + default: + BUG_ON(1); + } + + /* desired type? */ + if (item < 0) + itemtype = map->buckets[-item]->type; + else + itemtype = 0; + + /* keep going? */ + if (itemtype != type) { + in = map->buckets[-item]; + continue; + } + + /* collision? */ + collide = 0; + for (i=0; idevice_offload[item]) { + if (map->device_offload[item] >= 0x10000) + bad = 1; + else if ((crush_hash32_2(x, item) & 0xffff) < map->device_offload[item]) + bad = 1; + } + + if (bad || collide) { + ftotal++; + flocal++; + + if (collide && flocal < 3) + continue; /* locally a few times */ + if (ftotal >= 10) { + /* give up, ignore dup, fixme */ + skip_rep = 1; + break; + } + retry_rep = 1; + } + break; + } + + if (retry_rep) continue; + } + + if (skip_rep) continue; + + out[outpos] = item; + outpos++; } - break; - } - - if (retry_rep) continue; - } - - if (skip_rep) continue; - - out[outpos] = item; - outpos++; - } - - return outpos; + + return outpos; } int crush_do_rule(struct crush_map *map, int ruleno, int x, int *result, int result_max, - int *outmap, /* array of size max_devices, values 0...0xffff */ int forcefeed) /* -1 for none */ { - int result_len; - int force_stack[CRUSH_MAX_DEPTH]; - int force_pos = -1; - int a[CRUSH_MAX_SET]; - int b[CRUSH_MAX_SET]; - int *w; - int wsize = 0; - int *o; - int osize; - int *tmp; - struct crush_rule *rule; - int step; - int i; - int numrep; - - rule = &map->rules[ruleno]; - result_len = 0; - w = a; - o = b; - - /* determine hierarchical context of forcefeed, if any */ - if (forcefeed >= 0) { - while (1) { - force_stack[++force_pos] = forcefeed; - if (forcefeed >= 0) - forcefeed = map->device_parent_map[forcefeed]; - else - forcefeed = map->bucket_parent_map[-forcefeed]; - if (forcefeed == 0) break; - } - } - - for (step = 0; step < rule->len; step++) { - switch (rule->steps[step].op) { - case CRUSH_RULE_TAKE: - if (force_pos >= 0) { - w[0] = force_stack[force_pos]; - force_pos--; - BUG_ON(w[0] != rule->steps[step].arg1); - } else { - w[0] = rule->steps[step].arg1; - } - wsize = 1; - break; - - case CRUSH_RULE_CHOOSE_FIRSTN: - case CRUSH_RULE_CHOOSE_INDEP: - BUG_ON(wsize == 0); - - /* reset output */ - osize = 0; - - for (i = 0; i < wsize; i++) { - numrep = rule->steps[step].arg1; - - if (force_pos >= 0) { - o[osize++] = force_stack[force_pos]; - force_pos--; - numrep--; + int result_len; + int force_stack[CRUSH_MAX_DEPTH]; + int force_pos = -1; + int a[CRUSH_MAX_SET]; + int b[CRUSH_MAX_SET]; + int *w; + int wsize = 0; + int *o; + int osize; + int *tmp; + struct crush_rule *rule; + int step; + int i; + int numrep; + + rule = map->rules[ruleno]; + result_len = 0; + w = a; + o = b; + + /* determine hierarchical context of forcefeed, if any */ + if (forcefeed >= 0) { + while (1) { + force_stack[++force_pos] = forcefeed; + if (forcefeed >= 0) + forcefeed = map->device_parents[forcefeed]; + else + forcefeed = map->bucket_parents[-forcefeed]; + if (forcefeed == 0) break; + } } - if (numrep) - crush_choose(map, - &map->buckets[-w[i]], - x, numrep, rule->steps[step].arg2, - o+osize, rule->steps[step].op == CRUSH_RULE_CHOOSE_FIRSTN, - outmap); - } - - /* swap t and w arrays */ - tmp = o; - o = w; - w = o; - wsize = osize; - break; - - - case CRUSH_RULE_EMIT: - for (i=0; ilen; step++) { + switch (rule->steps[step].op) { + case CRUSH_RULE_TAKE: + if (force_pos >= 0) { + w[0] = force_stack[force_pos]; + force_pos--; + BUG_ON(w[0] != rule->steps[step].arg1); + } else { + w[0] = rule->steps[step].arg1; + } + wsize = 1; + break; + + case CRUSH_RULE_CHOOSE_FIRSTN: + case CRUSH_RULE_CHOOSE_INDEP: + BUG_ON(wsize == 0); + + /* reset output */ + osize = 0; + + for (i = 0; i < wsize; i++) { + numrep = rule->steps[step].arg1; + + if (force_pos >= 0) { + o[osize++] = force_stack[force_pos]; + force_pos--; + numrep--; + } + if (numrep) + crush_choose(map, + map->buckets[-w[i]], + x, numrep, rule->steps[step].arg2, + o+osize, rule->steps[step].op == CRUSH_RULE_CHOOSE_FIRSTN); + } + + /* swap t and w arrays */ + tmp = o; + o = w; + w = o; + wsize = osize; + break; + + + case CRUSH_RULE_EMIT: + for (i=0; i +#endif + + #include /* just for int types */ #ifndef BUG_ON -- 2.39.5