summaryrefslogtreecommitdiff
path: root/fs/bcachefs/hashtable.c
blob: 604f9e7f6b15e78b7f4f022a1442c39d8b2464dd (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
#include "hashtable.h"

#include "linux/random.h"
#include <linux/slab.h>

struct htable_free_rcu {
	struct rcu_head		rcu;
	unsigned		order;
	unsigned long		addr;
};

static void htable_free_rcu(struct rcu_head *rcu)
{
	struct htable_free_rcu *free_rcu =
		container_of(rcu, struct htable_free_rcu, rcu);

	free_pages(free_rcu->addr, free_rcu->order);
	kfree(free_rcu);
}

void bch2_htable_expand(struct htable *ht, const struct htable_params p)
{
	struct htable_ptr old, new;
	struct hlist_node *pos, *n;
	struct htable_free_rcu *free_rcu;
	unsigned shift = ht->table & ~PAGE_MASK;
	unsigned i;

	old = htable_read_ptr(ht);
	new.size = old.size * 2;

	free_rcu = kmalloc(sizeof(*free_rcu), GFP_KERNEL);
	if (!free_rcu)
		return;

	free_rcu->order = shift;
	free_rcu->addr = (unsigned long) old.table;

	new.table = (void *) __get_free_pages(GFP_KERNEL|__GFP_ZERO, shift + 1);
	if (!new.table) {
		kfree(free_rcu);
		return;
	}

	for (i = 0; i < old.size; i++) {
		hlist_for_each_safe(pos, n, old.table + i) {
			void *obj = htable_obj(pos, p);
			unsigned hash = htable_obj_get_hash(obj, ht->hash_seed, p);

			hlist_del_rcu(pos);
			hlist_add_head_rcu(pos, htable_bucket(new, hash));
		}
	}

	smp_store_release(&ht->table, (unsigned long) new.table | (shift + 1));
	ht->max_chain = 1;

	call_rcu(&free_rcu->rcu, htable_free_rcu);
}

void bch2_htable_exit(struct htable *ht)
{
	unsigned long table = ht->table & PAGE_MASK;
	unsigned order = ht->table & ~PAGE_MASK;

	free_pages(table, order);
}

int bch2_htable_init(struct htable *ht)
{
	mutex_init(&ht->lock);

	ht->hash_seed = get_random_u32();
	ht->nelems = 0;
	ht->max_chain = 1;
	ht->table = __get_free_page(GFP_KERNEL|__GFP_ZERO);
	if (!ht->table)
		return -ENOMEM;

	return 0;
}