summaryrefslogtreecommitdiff
path: root/fs/bcachefs/keylist.h
blob: 8fc92986f22f157ec1e6e210f0f1021332073d92 (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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#ifndef _BCACHE_KEYLIST_H
#define _BCACHE_KEYLIST_H

#include "keylist_types.h"

static inline void bch_keylist_init(struct keylist *l, u64 *inline_keys,
				    size_t nr_inline_u64s)
{
	l->bot_p = l->top_p = l->start_keys_p = inline_keys;
	l->end_keys_p = l->start_keys_p + nr_inline_u64s;
	l->has_buf = false;
}

static inline size_t bch_keylist_capacity(struct keylist *l)
{
	return l->end_keys_p - l->start_keys_p;
}

/*
 * XXX: why are we using BKEY_EXTENT_U64s_MAX here? keylists aren't used just
 * for extents, this doesn't make any sense
 */

static inline bool bch_keylist_fits(struct keylist *l, size_t u64s)
{
	if (l->bot_p > l->top_p)
		return (l->bot_p - l->top_p) > u64s;
	else if (l->top_p + u64s + BKEY_EXTENT_U64s_MAX > l->end_keys_p)
		return l->start_keys_p != l->bot_p;
	else
		return true;
}

static inline struct bkey_i *__bch_keylist_next(struct keylist *l,
						struct bkey_i *k)
{
	k = bkey_next(k);
	BUG_ON(k > l->end_keys);

	/* single_keylists don't wrap */
	if (k == l->top)
		return k;

	if ((u64 *) k + BKEY_EXTENT_U64s_MAX > l->end_keys_p)
		return l->start_keys;

	return k;
}

#define for_each_keylist_key(_keys, _k)					\
	for (_k = ACCESS_ONCE((_keys)->bot);				\
	     _k != (_keys)->top;					\
	     _k = __bch_keylist_next(_keys, _k))

static inline void bch_keylist_enqueue(struct keylist *l)
{
	BUG_ON(!bch_keylist_fits(l, l->top->k.u64s));
	l->top = __bch_keylist_next(l, l->top);
}

static inline void bch_keylist_add(struct keylist *l, const struct bkey_i *k)
{
	bkey_copy(l->top, k);
	bch_keylist_enqueue(l);
}

static inline bool bch_keylist_empty(struct keylist *l)
{
	return l->bot == l->top;
}

static inline void bch_keylist_free(struct keylist *l)
{
	if (l->has_buf)
		kfree(l->start_keys_p);
	memset(l, 0, sizeof(*l));
}

/*
 * This returns the number of u64s, rather than the number of keys. As keys are
 * variable sized, the actual number of keys would have to be counted.
 */
static inline size_t bch_keylist_nkeys(struct keylist *l)
{
	/*
	 * We don't know the exact number of u64s in the wrapped case
	 * because of internal fragmentation at the end!
	 */
	if (l->top_p >= l->bot_p)
		return l->top_p - l->bot_p;
	else
		return ((l->top_p - l->start_keys_p) +
			(l->end_keys_p - l->bot_p));
}

static inline struct bkey_i *bch_keylist_front(struct keylist *l)
{
	return l->bot;
}

static inline void bch_keylist_dequeue(struct keylist *l)
{
	BUG_ON(bch_keylist_empty(l));
	l->bot = __bch_keylist_next(l, l->bot);
}

#define keylist_single(k)						\
((struct keylist) {							\
	.start_keys = k,						\
	.top = bkey_next(k),						\
	.bot = k,							\
	.end_keys = bkey_next(k)					\
})

void bch_keylist_add_in_order(struct keylist *, struct bkey_i *);
int bch_keylist_realloc(struct keylist *, unsigned);
int bch_keylist_realloc_max(struct keylist *, unsigned, unsigned);


#endif /* _BCACHE_KEYLIST_H */