Blame view

lib/lockref.c 4.5 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
2f4f12e57   Linus Torvalds   lockref: uninline...
2
3
  #include <linux/export.h>
  #include <linux/lockref.h>
57f4257ea   Peter Zijlstra   lockref: use BLOA...
4
  #if USE_CMPXCHG_LOCKREF
bc08b449e   Linus Torvalds   lockref: implemen...
5
6
7
8
9
10
  
  /*
   * Note that the "cmpxchg()" reloads the "old" value for the
   * failure case.
   */
  #define CMPXCHG_LOOP(CODE, SUCCESS) do {					\
893a7d32e   Jan Glauber   lockref: Limit nu...
11
  	int retry = 100;							\
bc08b449e   Linus Torvalds   lockref: implemen...
12
13
  	struct lockref old;							\
  	BUILD_BUG_ON(sizeof(old) != 8);						\
4d3199e4c   Davidlohr Bueso   locking: Remove A...
14
  	old.lock_count = READ_ONCE(lockref->lock_count);			\
bc08b449e   Linus Torvalds   lockref: implemen...
15
16
17
  	while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) {  	\
  		struct lockref new = old, prev = old;				\
  		CODE								\
d2212b4dc   Will Deacon   lockref: allow re...
18
19
20
  		old.lock_count = cmpxchg64_relaxed(&lockref->lock_count,	\
  						   old.lock_count,		\
  						   new.lock_count);		\
bc08b449e   Linus Torvalds   lockref: implemen...
21
22
23
  		if (likely(old.lock_count == prev.lock_count)) {		\
  			SUCCESS;						\
  		}								\
893a7d32e   Jan Glauber   lockref: Limit nu...
24
25
  		if (!--retry)							\
  			break;							\
f2f09a4ce   Christian Borntraeger   locking/core: Rem...
26
  		cpu_relax();							\
bc08b449e   Linus Torvalds   lockref: implemen...
27
28
29
30
31
32
33
34
  	}									\
  } while (0)
  
  #else
  
  #define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
  
  #endif
2f4f12e57   Linus Torvalds   lockref: uninline...
35
36
  /**
   * lockref_get - Increments reference count unconditionally
44a0cf929   Linus Torvalds   lockref: fix docb...
37
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
38
39
40
41
42
43
   *
   * This operation is only valid if you already hold a reference
   * to the object, so you know the count cannot be zero.
   */
  void lockref_get(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
44
45
46
47
48
  	CMPXCHG_LOOP(
  		new.count++;
  	,
  		return;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
49
50
51
52
53
54
55
  	spin_lock(&lockref->lock);
  	lockref->count++;
  	spin_unlock(&lockref->lock);
  }
  EXPORT_SYMBOL(lockref_get);
  
  /**
360f54796   Linus Torvalds   dcache: let the d...
56
   * lockref_get_not_zero - Increments count unless the count is 0 or dead
44a0cf929   Linus Torvalds   lockref: fix docb...
57
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
58
59
60
61
   * Return: 1 if count updated successfully or 0 if count was zero
   */
  int lockref_get_not_zero(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
62
63
64
65
  	int retval;
  
  	CMPXCHG_LOOP(
  		new.count++;
360f54796   Linus Torvalds   dcache: let the d...
66
  		if (old.count <= 0)
bc08b449e   Linus Torvalds   lockref: implemen...
67
68
69
70
  			return 0;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
71
72
  
  	spin_lock(&lockref->lock);
bc08b449e   Linus Torvalds   lockref: implemen...
73
  	retval = 0;
360f54796   Linus Torvalds   dcache: let the d...
74
  	if (lockref->count > 0) {
2f4f12e57   Linus Torvalds   lockref: uninline...
75
76
77
78
79
80
81
82
83
  		lockref->count++;
  		retval = 1;
  	}
  	spin_unlock(&lockref->lock);
  	return retval;
  }
  EXPORT_SYMBOL(lockref_get_not_zero);
  
  /**
450b1f6f5   Andreas Gruenbacher   lockref: Add lock...
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
   * lockref_put_not_zero - Decrements count unless count <= 1 before decrement
   * @lockref: pointer to lockref structure
   * Return: 1 if count updated successfully or 0 if count would become zero
   */
  int lockref_put_not_zero(struct lockref *lockref)
  {
  	int retval;
  
  	CMPXCHG_LOOP(
  		new.count--;
  		if (old.count <= 1)
  			return 0;
  	,
  		return 1;
  	);
  
  	spin_lock(&lockref->lock);
  	retval = 0;
  	if (lockref->count > 1) {
  		lockref->count--;
  		retval = 1;
  	}
  	spin_unlock(&lockref->lock);
  	return retval;
  }
  EXPORT_SYMBOL(lockref_put_not_zero);
  
  /**
360f54796   Linus Torvalds   dcache: let the d...
112
   * lockref_get_or_lock - Increments count unless the count is 0 or dead
44a0cf929   Linus Torvalds   lockref: fix docb...
113
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
114
115
116
117
118
   * Return: 1 if count updated successfully or 0 if count was zero
   * and we got the lock instead.
   */
  int lockref_get_or_lock(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
119
120
  	CMPXCHG_LOOP(
  		new.count++;
360f54796   Linus Torvalds   dcache: let the d...
121
  		if (old.count <= 0)
bc08b449e   Linus Torvalds   lockref: implemen...
122
123
124
125
  			break;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
126
  	spin_lock(&lockref->lock);
360f54796   Linus Torvalds   dcache: let the d...
127
  	if (lockref->count <= 0)
2f4f12e57   Linus Torvalds   lockref: uninline...
128
129
130
131
132
133
134
135
  		return 0;
  	lockref->count++;
  	spin_unlock(&lockref->lock);
  	return 1;
  }
  EXPORT_SYMBOL(lockref_get_or_lock);
  
  /**
360f54796   Linus Torvalds   dcache: let the d...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
   * lockref_put_return - Decrement reference count if possible
   * @lockref: pointer to lockref structure
   *
   * Decrement the reference count and return the new value.
   * If the lockref was dead or locked, return an error.
   */
  int lockref_put_return(struct lockref *lockref)
  {
  	CMPXCHG_LOOP(
  		new.count--;
  		if (old.count <= 0)
  			return -1;
  	,
  		return new.count;
  	);
  	return -1;
  }
  EXPORT_SYMBOL(lockref_put_return);
  
  /**
2f4f12e57   Linus Torvalds   lockref: uninline...
156
   * lockref_put_or_lock - decrements count unless count <= 1 before decrement
44a0cf929   Linus Torvalds   lockref: fix docb...
157
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
158
159
160
161
   * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
   */
  int lockref_put_or_lock(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
162
163
164
165
166
167
168
  	CMPXCHG_LOOP(
  		new.count--;
  		if (old.count <= 1)
  			break;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
169
170
171
172
173
174
175
176
  	spin_lock(&lockref->lock);
  	if (lockref->count <= 1)
  		return 0;
  	lockref->count--;
  	spin_unlock(&lockref->lock);
  	return 1;
  }
  EXPORT_SYMBOL(lockref_put_or_lock);
e7d33bb5e   Linus Torvalds   lockref: add abil...
177
178
179
180
181
182
183
184
185
186
  
  /**
   * lockref_mark_dead - mark lockref dead
   * @lockref: pointer to lockref structure
   */
  void lockref_mark_dead(struct lockref *lockref)
  {
  	assert_spin_locked(&lockref->lock);
  	lockref->count = -128;
  }
e66cf1610   Steven Whitehouse   GFS2: Use lockref...
187
  EXPORT_SYMBOL(lockref_mark_dead);
e7d33bb5e   Linus Torvalds   lockref: add abil...
188
189
190
191
192
193
194
195
196
197
198
199
  
  /**
   * lockref_get_not_dead - Increments count unless the ref is dead
   * @lockref: pointer to lockref structure
   * Return: 1 if count updated successfully or 0 if lockref was dead
   */
  int lockref_get_not_dead(struct lockref *lockref)
  {
  	int retval;
  
  	CMPXCHG_LOOP(
  		new.count++;
360f54796   Linus Torvalds   dcache: let the d...
200
  		if (old.count < 0)
e7d33bb5e   Linus Torvalds   lockref: add abil...
201
202
203
204
205
206
207
  			return 0;
  	,
  		return 1;
  	);
  
  	spin_lock(&lockref->lock);
  	retval = 0;
360f54796   Linus Torvalds   dcache: let the d...
208
  	if (lockref->count >= 0) {
e7d33bb5e   Linus Torvalds   lockref: add abil...
209
210
211
212
213
214
215
  		lockref->count++;
  		retval = 1;
  	}
  	spin_unlock(&lockref->lock);
  	return retval;
  }
  EXPORT_SYMBOL(lockref_get_not_dead);