summaryrefslogtreecommitdiff
path: root/drivers/md/bcache/btree_locking.h
blob: f6789bb4a20bdba25f8b5d7fad4ef0cd72c730c1 (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#ifndef _BCACHE_BTREE_LOCKING_H
#define _BCACHE_BTREE_LOCKING_H

/*
 * Only for internal btree use:
 *
 * The btree iterator tracks what locks it wants to take, and what locks it
 * currently has - here we have wrappers for locking/unlocking btree nodes and
 * updating the iterator state
 */

#include "btree_iter.h"
#include "six.h"

/* matches six lock types */
enum btree_node_locked_type {
	BTREE_NODE_UNLOCKED		= -1,
	BTREE_NODE_READ_LOCKED		= SIX_LOCK_read,
	BTREE_NODE_INTENT_LOCKED	= SIX_LOCK_intent,
};

static inline int btree_node_locked_type(struct btree_iter *iter,
					 unsigned level)
{
	/*
	 * We're relying on the fact that if nodes_intent_locked is set
	 * nodes_locked must be set as well, so that we can compute without
	 * branches:
	 */
	return BTREE_NODE_UNLOCKED +
		((iter->nodes_locked >> level) & 1) +
		((iter->nodes_intent_locked >> level) & 1);
}

static inline bool btree_node_intent_locked(struct btree_iter *iter,
					    unsigned level)
{
	return btree_node_locked_type(iter, level) == BTREE_NODE_INTENT_LOCKED;
}

static inline bool btree_node_read_locked(struct btree_iter *iter,
					  unsigned level)
{
	return btree_node_locked_type(iter, level) == BTREE_NODE_READ_LOCKED;
}

static inline bool btree_node_locked(struct btree_iter *iter, unsigned level)
{
	return iter->nodes_locked & (1 << level);
}

static inline void mark_btree_node_unlocked(struct btree_iter *iter,
					    unsigned level)
{
	iter->nodes_locked &= ~(1 << level);
	iter->nodes_intent_locked &= ~(1 << level);
}

static inline void mark_btree_node_locked(struct btree_iter *iter,
					  unsigned level,
					  enum six_lock_type type)
{
	/* relying on this to avoid a branch */
	BUILD_BUG_ON(SIX_LOCK_read   != 0);
	BUILD_BUG_ON(SIX_LOCK_intent != 1);

	iter->nodes_locked |= 1 << level;
	iter->nodes_intent_locked |= type << level;
}

static inline void mark_btree_node_intent_locked(struct btree_iter *iter,
						 unsigned level)
{
	mark_btree_node_locked(iter, level, SIX_LOCK_intent);
}

static inline enum six_lock_type
btree_lock_want(struct btree_iter *iter, int level)
{
	return level < iter->locks_want
		? SIX_LOCK_intent
		: SIX_LOCK_read;
}

static inline bool btree_want_intent(struct btree_iter *iter, int level)
{
	return btree_lock_want(iter, level) == SIX_LOCK_intent;
}

static inline void __btree_node_unlock(struct btree_iter *iter, unsigned level,
				       struct btree *b)
{
	switch (btree_node_locked_type(iter, level)) {
	case BTREE_NODE_READ_LOCKED:
		six_unlock_read(&b->lock);
		break;
	case BTREE_NODE_INTENT_LOCKED:
		six_unlock_intent(&b->lock);
		break;
	}

	mark_btree_node_unlocked(iter, level);
}

static inline void btree_node_unlock(struct btree_iter *iter, unsigned level)
{
	__btree_node_unlock(iter, level, iter->l[level].node);
}

static inline void btree_node_lock_type(struct btree *b, struct btree_iter *iter,
					enum six_lock_type type)
{
	struct btree_iter *linked;

	if (six_trylock_type(&b->lock, type))
		return;

	for_each_linked_btree_iter(iter, linked)
		if (linked->l[b->level].node == b &&
		    btree_node_locked_type(linked, b->level) == type) {
			six_lock_increment(&b->lock, type);
			return;
		}

	six_lock_type(&b->lock, type);
}

#define __btree_node_lock(b, _iter, _level, check_if_raced)		\
({									\
	enum six_lock_type _type = btree_lock_want(_iter, _level);	\
	bool _raced;							\
									\
	btree_node_lock_type(b, _iter, _type);				\
	if ((_raced = ((check_if_raced) || ((b)->level != _level))))	\
		six_unlock_type(&(b)->lock, _type);			\
	else								\
		mark_btree_node_locked(_iter, _level, _type);		\
									\
	!_raced;							\
})

#define btree_node_lock(b, iter, level, check_if_raced)			\
	(!race_fault() &&						\
	  __btree_node_lock(b, iter, level, check_if_raced))

bool btree_node_relock(struct btree_iter *, unsigned);

void btree_node_unlock_write(struct btree *, struct btree_iter *);
void btree_node_lock_write(struct btree *, struct btree_iter *);

void __btree_node_unlock_write(struct btree *, struct btree_iter *);
void __btree_node_lock_write(struct btree *, struct btree_iter *);

#endif /* _BCACHE_BTREE_LOCKING_H */