summaryrefslogtreecommitdiff
path: root/Cuckoo.mdwn
blob: adcf1d7433ec5b8cb00d6389c7d48dc86a66ba68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

#!highlight c 
[[!format txt """
static inline unsigned cuckoo_hash(unsigned val, unsigned bits)
{
        return hash_32(val, bits);
}

static inline unsigned cuckoo_next(unsigned h, unsigned val, unsigned bits)
{
        return (val * GOLDEN_RATIO_PRIME_32 - h) & ~(~0 << bits);
}

static struct btree *btree_cache_find(struct cache_set *c, struct bkey *k)
{
        struct btree **hash;
        struct btree *b = NULL;
        unsigned bits, h, val = PTR_HASH(c, k);

        rcu_read_lock();
        hash = rcu_dereference(c->bucket_hash);

        bits = ((unsigned long) hash) & (PAGE_SIZE - 1);
        hash = ((void *) hash) - bits;

        h = cuckoo_hash(val, bits);
        if (hash[h] && hash[h]->key.ptr[0] == k->ptr[0])
                b = hash[h];

        h = cuckoo_next(h, val, bits);
        if (hash[h] && hash[h]->key.ptr[0] == k->ptr[0])
                b = hash[h];

        rcu_read_unlock();
        return b;
}

static void btree_cache_insert(struct cache_set *c, struct btree *b)
{
        bool __insert(struct btree **hash, unsigned bits)
        {
                unsigned h = cuckoo_hash(PTR_HASH(c, &b->key), bits);
                if (!hash[h])
                        goto found;

                for (unsigned i = 0; i < 16; i++) {
                        h = cuckoo_next(h, PTR_HASH(c, &b->key), bits);
                        if (!hash[h])
                                goto found;

                        swap(b, hash[h]);
                }

                return false;
found:
                hash[h] = b;
                return true;
        }

        struct btree **n, **hash = c->bucket_hash;
        unsigned bits;

        /* We need to use rcu_assign_pointer() for the first insert - but not
         * the rest, so one barrier here is fine.
         */
        smp_wmb();

        bits = ((unsigned long) hash) & (PAGE_SIZE - 1);
        hash = ((void *) hash) - bits;

        if (__insert(hash, bits))
                return;
rehash:
        bits++;
        n = (void *) __get_free_pages(__GFP_ZERO|GFP_NOIO,
                                      max_t(int, bits - PAGE_SHIFT, 0));
        if (!n)
                BUG();

        __insert(n, bits);

        for (unsigned i = 0; i < 1U << (bits - 1); i++) {
                b = hash[i];

                if (b && !__insert(n, bits)) {
                        free_pages((unsigned long) n,
                                   max_t(int, bits - PAGE_SHIFT, 0));
                        goto rehash;
                }
        }

        n = ((void *) n) + bits;
        rcu_assign_pointer(c->bucket_hash, n);
}
"""]]