linux/net/ceph/crush/mapper.c
<<
>>
Prefs
   1
   2#ifdef __KERNEL__
   3# include <linux/string.h>
   4# include <linux/slab.h>
   5# include <linux/bug.h>
   6# include <linux/kernel.h>
   7# ifndef dprintk
   8#  define dprintk(args...)
   9# endif
  10#else
  11# include <string.h>
  12# include <stdio.h>
  13# include <stdlib.h>
  14# include <assert.h>
  15# define BUG_ON(x) assert(!(x))
  16# define dprintk(args...) /* printf(args) */
  17# define kmalloc(x, f) malloc(x)
  18# define kfree(x) free(x)
  19#endif
  20
  21#include <linux/crush/crush.h>
  22#include <linux/crush/hash.h>
  23#include <linux/crush/mapper.h>
  24
  25/*
  26 * Implement the core CRUSH mapping algorithm.
  27 */
  28
  29/**
  30 * crush_find_rule - find a crush_rule id for a given ruleset, type, and size.
  31 * @map: the crush_map
  32 * @ruleset: the storage ruleset id (user defined)
  33 * @type: storage ruleset type (user defined)
  34 * @size: output set size
  35 */
  36int crush_find_rule(const struct crush_map *map, int ruleset, int type, int size)
  37{
  38        __u32 i;
  39
  40        for (i = 0; i < map->max_rules; i++) {
  41                if (map->rules[i] &&
  42                    map->rules[i]->mask.ruleset == ruleset &&
  43                    map->rules[i]->mask.type == type &&
  44                    map->rules[i]->mask.min_size <= size &&
  45                    map->rules[i]->mask.max_size >= size)
  46                        return i;
  47        }
  48        return -1;
  49}
  50
  51
  52/*
  53 * bucket choose methods
  54 *
  55 * For each bucket algorithm, we have a "choose" method that, given a
  56 * crush input @x and replica position (usually, position in output set) @r,
  57 * will produce an item in the bucket.
  58 */
  59
  60/*
  61 * Choose based on a random permutation of the bucket.
  62 *
  63 * We used to use some prime number arithmetic to do this, but it
  64 * wasn't very random, and had some other bad behaviors.  Instead, we
  65 * calculate an actual random permutation of the bucket members.
  66 * Since this is expensive, we optimize for the r=0 case, which
  67 * captures the vast majority of calls.
  68 */
  69static int bucket_perm_choose(struct crush_bucket *bucket,
  70                              int x, int r)
  71{
  72        unsigned int pr = r % bucket->size;
  73        unsigned int i, s;
  74
  75        /* start a new permutation if @x has changed */
  76        if (bucket->perm_x != (__u32)x || bucket->perm_n == 0) {
  77                dprintk("bucket %d new x=%d\n", bucket->id, x);
  78                bucket->perm_x = x;
  79
  80                /* optimize common r=0 case */
  81                if (pr == 0) {
  82                        s = crush_hash32_3(bucket->hash, x, bucket->id, 0) %
  83                                bucket->size;
  84                        bucket->perm[0] = s;
  85                        bucket->perm_n = 0xffff;   /* magic value, see below */
  86                        goto out;
  87                }
  88
  89                for (i = 0; i < bucket->size; i++)
  90                        bucket->perm[i] = i;
  91                bucket->perm_n = 0;
  92        } else if (bucket->perm_n == 0xffff) {
  93                /* clean up after the r=0 case above */
  94                for (i = 1; i < bucket->size; i++)
  95                        bucket->perm[i] = i;
  96                bucket->perm[bucket->perm[0]] = 0;
  97                bucket->perm_n = 1;
  98        }
  99
 100        /* calculate permutation up to pr */
 101        for (i = 0; i < bucket->perm_n; i++)
 102                dprintk(" perm_choose have %d: %d\n", i, bucket->perm[i]);
 103        while (bucket->perm_n <= pr) {
 104                unsigned int p = bucket->perm_n;
 105                /* no point in swapping the final entry */
 106                if (p < bucket->size - 1) {
 107                        i = crush_hash32_3(bucket->hash, x, bucket->id, p) %
 108                                (bucket->size - p);
 109                        if (i) {
 110                                unsigned int t = bucket->perm[p + i];
 111                                bucket->perm[p + i] = bucket->perm[p];
 112                                bucket->perm[p] = t;
 113                        }
 114                        dprintk(" perm_choose swap %d with %d\n", p, p+i);
 115                }
 116                bucket->perm_n++;
 117        }
 118        for (i = 0; i < bucket->size; i++)
 119                dprintk(" perm_choose  %d: %d\n", i, bucket->perm[i]);
 120
 121        s = bucket->perm[pr];
 122out:
 123        dprintk(" perm_choose %d sz=%d x=%d r=%d (%d) s=%d\n", bucket->id,
 124                bucket->size, x, r, pr, s);
 125        return bucket->items[s];
 126}
 127
 128/* uniform */
 129static int bucket_uniform_choose(struct crush_bucket_uniform *bucket,
 130                                 int x, int r)
 131{
 132        return bucket_perm_choose(&bucket->h, x, r);
 133}
 134
 135/* list */
 136static int bucket_list_choose(struct crush_bucket_list *bucket,
 137                              int x, int r)
 138{
 139        int i;
 140
 141        for (i = bucket->h.size-1; i >= 0; i--) {
 142                __u64 w = crush_hash32_4(bucket->h.hash,x, bucket->h.items[i],
 143                                         r, bucket->h.id);
 144                w &= 0xffff;
 145                dprintk("list_choose i=%d x=%d r=%d item %d weight %x "
 146                        "sw %x rand %llx",
 147                        i, x, r, bucket->h.items[i], bucket->item_weights[i],
 148                        bucket->sum_weights[i], w);
 149                w *= bucket->sum_weights[i];
 150                w = w >> 16;
 151                /*dprintk(" scaled %llx\n", w);*/
 152                if (w < bucket->item_weights[i])
 153                        return bucket->h.items[i];
 154        }
 155
 156        dprintk("bad list sums for bucket %d\n", bucket->h.id);
 157        return bucket->h.items[0];
 158}
 159
 160
 161/* (binary) tree */
 162static int height(int n)
 163{
 164        int h = 0;
 165        while ((n & 1) == 0) {
 166                h++;
 167                n = n >> 1;
 168        }
 169        return h;
 170}
 171
 172static int left(int x)
 173{
 174        int h = height(x);
 175        return x - (1 << (h-1));
 176}
 177
 178static int right(int x)
 179{
 180        int h = height(x);
 181        return x + (1 << (h-1));
 182}
 183
 184static int terminal(int x)
 185{
 186        return x & 1;
 187}
 188
 189static int bucket_tree_choose(struct crush_bucket_tree *bucket,
 190                              int x, int r)
 191{
 192        int n, l;
 193        __u32 w;
 194        __u64 t;
 195
 196        /* start at root */
 197        n = bucket->num_nodes >> 1;
 198
 199        while (!terminal(n)) {
 200                /* pick point in [0, w) */
 201                w = bucket->node_weights[n];
 202                t = (__u64)crush_hash32_4(bucket->h.hash, x, n, r,
 203                                          bucket->h.id) * (__u64)w;
 204                t = t >> 32;
 205
 206                /* descend to the left or right? */
 207                l = left(n);
 208                if (t < bucket->node_weights[l])
 209                        n = l;
 210                else
 211                        n = right(n);
 212        }
 213
 214        return bucket->h.items[n >> 1];
 215}
 216
 217
 218/* straw */
 219
 220static int bucket_straw_choose(struct crush_bucket_straw *bucket,
 221                               int x, int r)
 222{
 223        __u32 i;
 224        int high = 0;
 225        __u64 high_draw = 0;
 226        __u64 draw;
 227
 228        for (i = 0; i < bucket->h.size; i++) {
 229                draw = crush_hash32_3(bucket->h.hash, x, bucket->h.items[i], r);
 230                draw &= 0xffff;
 231                draw *= bucket->straws[i];
 232                if (i == 0 || draw > high_draw) {
 233                        high = i;
 234                        high_draw = draw;
 235                }
 236        }
 237        return bucket->h.items[high];
 238}
 239
 240static int crush_bucket_choose(struct crush_bucket *in, int x, int r)
 241{
 242        dprintk(" crush_bucket_choose %d x=%d r=%d\n", in->id, x, r);
 243        BUG_ON(in->size == 0);
 244        switch (in->alg) {
 245        case CRUSH_BUCKET_UNIFORM:
 246                return bucket_uniform_choose((struct crush_bucket_uniform *)in,
 247                                          x, r);
 248        case CRUSH_BUCKET_LIST:
 249                return bucket_list_choose((struct crush_bucket_list *)in,
 250                                          x, r);
 251        case CRUSH_BUCKET_TREE:
 252                return bucket_tree_choose((struct crush_bucket_tree *)in,
 253                                          x, r);
 254        case CRUSH_BUCKET_STRAW:
 255                return bucket_straw_choose((struct crush_bucket_straw *)in,
 256                                           x, r);
 257        default:
 258                dprintk("unknown bucket %d alg %d\n", in->id, in->alg);
 259                return in->items[0];
 260        }
 261}
 262
 263/*
 264 * true if device is marked "out" (failed, fully offloaded)
 265 * of the cluster
 266 */
 267static int is_out(const struct crush_map *map, const __u32 *weight, int item, int x)
 268{
 269        if (weight[item] >= 0x10000)
 270                return 0;
 271        if (weight[item] == 0)
 272                return 1;
 273        if ((crush_hash32_2(CRUSH_HASH_RJENKINS1, x, item) & 0xffff)
 274            < weight[item])
 275                return 0;
 276        return 1;
 277}
 278
 279/**
 280 * crush_choose - choose numrep distinct items of given type
 281 * @map: the crush_map
 282 * @bucket: the bucket we are choose an item from
 283 * @x: crush input value
 284 * @numrep: the number of items to choose
 285 * @type: the type of item to choose
 286 * @out: pointer to output vector
 287 * @outpos: our position in that vector
 288 * @firstn: true if choosing "first n" items, false if choosing "indep"
 289 * @recurse_to_leaf: true if we want one device under each item of given type
 290 * @out2: second output vector for leaf items (if @recurse_to_leaf)
 291 */
 292static int crush_choose(const struct crush_map *map,
 293                        struct crush_bucket *bucket,
 294                        const __u32 *weight,
 295                        int x, int numrep, int type,
 296                        int *out, int outpos,
 297                        int firstn, int recurse_to_leaf,
 298                        int *out2)
 299{
 300        int rep;
 301        unsigned int ftotal, flocal;
 302        int retry_descent, retry_bucket, skip_rep;
 303        struct crush_bucket *in = bucket;
 304        int r;
 305        int i;
 306        int item = 0;
 307        int itemtype;
 308        int collide, reject;
 309
 310        dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "",
 311                bucket->id, x, outpos, numrep);
 312
 313        for (rep = outpos; rep < numrep; rep++) {
 314                /* keep trying until we get a non-out, non-colliding item */
 315                ftotal = 0;
 316                skip_rep = 0;
 317                do {
 318                        retry_descent = 0;
 319                        in = bucket;               /* initial bucket */
 320
 321                        /* choose through intervening buckets */
 322                        flocal = 0;
 323                        do {
 324                                collide = 0;
 325                                retry_bucket = 0;
 326                                r = rep;
 327                                if (in->alg == CRUSH_BUCKET_UNIFORM) {
 328                                        /* be careful */
 329                                        if (firstn || (__u32)numrep >= in->size)
 330                                                /* r' = r + f_total */
 331                                                r += ftotal;
 332                                        else if (in->size % numrep == 0)
 333                                                /* r'=r+(n+1)*f_local */
 334                                                r += (numrep+1) *
 335                                                        (flocal+ftotal);
 336                                        else
 337                                                /* r' = r + n*f_local */
 338                                                r += numrep * (flocal+ftotal);
 339                                } else {
 340                                        if (firstn)
 341                                                /* r' = r + f_total */
 342                                                r += ftotal;
 343                                        else
 344                                                /* r' = r + n*f_local */
 345                                                r += numrep * (flocal+ftotal);
 346                                }
 347
 348                                /* bucket choose */
 349                                if (in->size == 0) {
 350                                        reject = 1;
 351                                        goto reject;
 352                                }
 353                                if (map->choose_local_fallback_tries > 0 &&
 354                                    flocal >= (in->size>>1) &&
 355                                    flocal > map->choose_local_fallback_tries)
 356                                        item = bucket_perm_choose(in, x, r);
 357                                else
 358                                        item = crush_bucket_choose(in, x, r);
 359                                if (item >= map->max_devices) {
 360                                        dprintk("   bad item %d\n", item);
 361                                        skip_rep = 1;
 362                                        break;
 363                                }
 364
 365                                /* desired type? */
 366                                if (item < 0)
 367                                        itemtype = map->buckets[-1-item]->type;
 368                                else
 369                                        itemtype = 0;
 370                                dprintk("  item %d type %d\n", item, itemtype);
 371
 372                                /* keep going? */
 373                                if (itemtype != type) {
 374                                        if (item >= 0 ||
 375                                            (-1-item) >= map->max_buckets) {
 376                                                dprintk("   bad item type %d\n", type);
 377                                                skip_rep = 1;
 378                                                break;
 379                                        }
 380                                        in = map->buckets[-1-item];
 381                                        retry_bucket = 1;
 382                                        continue;
 383                                }
 384
 385                                /* collision? */
 386                                for (i = 0; i < outpos; i++) {
 387                                        if (out[i] == item) {
 388                                                collide = 1;
 389                                                break;
 390                                        }
 391                                }
 392
 393                                reject = 0;
 394                                if (recurse_to_leaf) {
 395                                        if (item < 0) {
 396                                                if (crush_choose(map,
 397                                                         map->buckets[-1-item],
 398                                                         weight,
 399                                                         x, outpos+1, 0,
 400                                                         out2, outpos,
 401                                                         firstn, 0,
 402                                                         NULL) <= outpos)
 403                                                        /* didn't get leaf */
 404                                                        reject = 1;
 405                                        } else {
 406                                                /* we already have a leaf! */
 407                                                out2[outpos] = item;
 408                                        }
 409                                }
 410
 411                                if (!reject) {
 412                                        /* out? */
 413                                        if (itemtype == 0)
 414                                                reject = is_out(map, weight,
 415                                                                item, x);
 416                                        else
 417                                                reject = 0;
 418                                }
 419
 420reject:
 421                                if (reject || collide) {
 422                                        ftotal++;
 423                                        flocal++;
 424
 425                                        if (collide && flocal <= map->choose_local_tries)
 426                                                /* retry locally a few times */
 427                                                retry_bucket = 1;
 428                                        else if (map->choose_local_fallback_tries > 0 &&
 429                                                 flocal <= in->size + map->choose_local_fallback_tries)
 430                                                /* exhaustive bucket search */
 431                                                retry_bucket = 1;
 432                                        else if (ftotal <= map->choose_total_tries)
 433                                                /* then retry descent */
 434                                                retry_descent = 1;
 435                                        else
 436                                                /* else give up */
 437                                                skip_rep = 1;
 438                                        dprintk("  reject %d  collide %d  "
 439                                                "ftotal %u  flocal %u\n",
 440                                                reject, collide, ftotal,
 441                                                flocal);
 442                                }
 443                        } while (retry_bucket);
 444                } while (retry_descent);
 445
 446                if (skip_rep) {
 447                        dprintk("skip rep\n");
 448                        continue;
 449                }
 450
 451                dprintk("CHOOSE got %d\n", item);
 452                out[outpos] = item;
 453                outpos++;
 454        }
 455
 456        dprintk("CHOOSE returns %d\n", outpos);
 457        return outpos;
 458}
 459
 460
 461/**
 462 * crush_do_rule - calculate a mapping with the given input and rule
 463 * @map: the crush_map
 464 * @ruleno: the rule id
 465 * @x: hash input
 466 * @result: pointer to result vector
 467 * @result_max: maximum result size
 468 */
 469int crush_do_rule(const struct crush_map *map,
 470                  int ruleno, int x, int *result, int result_max,
 471                  const __u32 *weight)
 472{
 473        int result_len;
 474        int a[CRUSH_MAX_SET];
 475        int b[CRUSH_MAX_SET];
 476        int c[CRUSH_MAX_SET];
 477        int recurse_to_leaf;
 478        int *w;
 479        int wsize = 0;
 480        int *o;
 481        int osize;
 482        int *tmp;
 483        struct crush_rule *rule;
 484        __u32 step;
 485        int i, j;
 486        int numrep;
 487        int firstn;
 488
 489        if ((__u32)ruleno >= map->max_rules) {
 490                dprintk(" bad ruleno %d\n", ruleno);
 491                return 0;
 492        }
 493
 494        rule = map->rules[ruleno];
 495        result_len = 0;
 496        w = a;
 497        o = b;
 498
 499        for (step = 0; step < rule->len; step++) {
 500                struct crush_rule_step *curstep = &rule->steps[step];
 501
 502                firstn = 0;
 503                switch (curstep->op) {
 504                case CRUSH_RULE_TAKE:
 505                        w[0] = curstep->arg1;
 506                        wsize = 1;
 507                        break;
 508
 509                case CRUSH_RULE_CHOOSE_LEAF_FIRSTN:
 510                case CRUSH_RULE_CHOOSE_FIRSTN:
 511                        firstn = 1;
 512                        /* fall through */
 513                case CRUSH_RULE_CHOOSE_LEAF_INDEP:
 514                case CRUSH_RULE_CHOOSE_INDEP:
 515                        if (wsize == 0)
 516                                break;
 517
 518                        recurse_to_leaf =
 519                                curstep->op ==
 520                                 CRUSH_RULE_CHOOSE_LEAF_FIRSTN ||
 521                                curstep->op ==
 522                                CRUSH_RULE_CHOOSE_LEAF_INDEP;
 523
 524                        /* reset output */
 525                        osize = 0;
 526
 527                        for (i = 0; i < wsize; i++) {
 528                                /*
 529                                 * see CRUSH_N, CRUSH_N_MINUS macros.
 530                                 * basically, numrep <= 0 means relative to
 531                                 * the provided result_max
 532                                 */
 533                                numrep = curstep->arg1;
 534                                if (numrep <= 0) {
 535                                        numrep += result_max;
 536                                        if (numrep <= 0)
 537                                                continue;
 538                                }
 539                                j = 0;
 540                                osize += crush_choose(map,
 541                                                      map->buckets[-1-w[i]],
 542                                                      weight,
 543                                                      x, numrep,
 544                                                      curstep->arg2,
 545                                                      o+osize, j,
 546                                                      firstn,
 547                                                      recurse_to_leaf, c+osize);
 548                        }
 549
 550                        if (recurse_to_leaf)
 551                                /* copy final _leaf_ values to output set */
 552                                memcpy(o, c, osize*sizeof(*o));
 553
 554                        /* swap t and w arrays */
 555                        tmp = o;
 556                        o = w;
 557                        w = tmp;
 558                        wsize = osize;
 559                        break;
 560
 561
 562                case CRUSH_RULE_EMIT:
 563                        for (i = 0; i < wsize && result_len < result_max; i++) {
 564                                result[result_len] = w[i];
 565                                result_len++;
 566                        }
 567                        wsize = 0;
 568                        break;
 569
 570                default:
 571                        dprintk(" unknown op %d at step %d\n",
 572                                curstep->op, step);
 573                        break;
 574                }
 575        }
 576        return result_len;
 577}
 578
 579
 580