xref: /openbmc/linux/lib/lockref.c (revision 4beec1d7)
1 #include <linux/export.h>
2 #include <linux/lockref.h>
3 
4 #if USE_CMPXCHG_LOCKREF
5 
6 /*
7  * Note that the "cmpxchg()" reloads the "old" value for the
8  * failure case.
9  */
10 #define CMPXCHG_LOOP(CODE, SUCCESS) do {					\
11 	struct lockref old;							\
12 	BUILD_BUG_ON(sizeof(old) != 8);						\
13 	old.lock_count = READ_ONCE(lockref->lock_count);			\
14 	while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) {  	\
15 		struct lockref new = old, prev = old;				\
16 		CODE								\
17 		old.lock_count = cmpxchg64_relaxed(&lockref->lock_count,	\
18 						   old.lock_count,		\
19 						   new.lock_count);		\
20 		if (likely(old.lock_count == prev.lock_count)) {		\
21 			SUCCESS;						\
22 		}								\
23 		cpu_relax();							\
24 	}									\
25 } while (0)
26 
27 #else
28 
29 #define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
30 
31 #endif
32 
33 /**
34  * lockref_get - Increments reference count unconditionally
35  * @lockref: pointer to lockref structure
36  *
37  * This operation is only valid if you already hold a reference
38  * to the object, so you know the count cannot be zero.
39  */
40 void lockref_get(struct lockref *lockref)
41 {
42 	CMPXCHG_LOOP(
43 		new.count++;
44 	,
45 		return;
46 	);
47 
48 	spin_lock(&lockref->lock);
49 	lockref->count++;
50 	spin_unlock(&lockref->lock);
51 }
52 EXPORT_SYMBOL(lockref_get);
53 
54 /**
55  * lockref_get_not_zero - Increments count unless the count is 0 or dead
56  * @lockref: pointer to lockref structure
57  * Return: 1 if count updated successfully or 0 if count was zero
58  */
59 int lockref_get_not_zero(struct lockref *lockref)
60 {
61 	int retval;
62 
63 	CMPXCHG_LOOP(
64 		new.count++;
65 		if (old.count <= 0)
66 			return 0;
67 	,
68 		return 1;
69 	);
70 
71 	spin_lock(&lockref->lock);
72 	retval = 0;
73 	if (lockref->count > 0) {
74 		lockref->count++;
75 		retval = 1;
76 	}
77 	spin_unlock(&lockref->lock);
78 	return retval;
79 }
80 EXPORT_SYMBOL(lockref_get_not_zero);
81 
82 /**
83  * lockref_get_or_lock - Increments count unless the count is 0 or dead
84  * @lockref: pointer to lockref structure
85  * Return: 1 if count updated successfully or 0 if count was zero
86  * and we got the lock instead.
87  */
88 int lockref_get_or_lock(struct lockref *lockref)
89 {
90 	CMPXCHG_LOOP(
91 		new.count++;
92 		if (old.count <= 0)
93 			break;
94 	,
95 		return 1;
96 	);
97 
98 	spin_lock(&lockref->lock);
99 	if (lockref->count <= 0)
100 		return 0;
101 	lockref->count++;
102 	spin_unlock(&lockref->lock);
103 	return 1;
104 }
105 EXPORT_SYMBOL(lockref_get_or_lock);
106 
107 /**
108  * lockref_put_return - Decrement reference count if possible
109  * @lockref: pointer to lockref structure
110  *
111  * Decrement the reference count and return the new value.
112  * If the lockref was dead or locked, return an error.
113  */
114 int lockref_put_return(struct lockref *lockref)
115 {
116 	CMPXCHG_LOOP(
117 		new.count--;
118 		if (old.count <= 0)
119 			return -1;
120 	,
121 		return new.count;
122 	);
123 	return -1;
124 }
125 EXPORT_SYMBOL(lockref_put_return);
126 
127 /**
128  * lockref_put_or_lock - decrements count unless count <= 1 before decrement
129  * @lockref: pointer to lockref structure
130  * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
131  */
132 int lockref_put_or_lock(struct lockref *lockref)
133 {
134 	CMPXCHG_LOOP(
135 		new.count--;
136 		if (old.count <= 1)
137 			break;
138 	,
139 		return 1;
140 	);
141 
142 	spin_lock(&lockref->lock);
143 	if (lockref->count <= 1)
144 		return 0;
145 	lockref->count--;
146 	spin_unlock(&lockref->lock);
147 	return 1;
148 }
149 EXPORT_SYMBOL(lockref_put_or_lock);
150 
151 /**
152  * lockref_mark_dead - mark lockref dead
153  * @lockref: pointer to lockref structure
154  */
155 void lockref_mark_dead(struct lockref *lockref)
156 {
157 	assert_spin_locked(&lockref->lock);
158 	lockref->count = -128;
159 }
160 EXPORT_SYMBOL(lockref_mark_dead);
161 
162 /**
163  * lockref_get_not_dead - Increments count unless the ref is dead
164  * @lockref: pointer to lockref structure
165  * Return: 1 if count updated successfully or 0 if lockref was dead
166  */
167 int lockref_get_not_dead(struct lockref *lockref)
168 {
169 	int retval;
170 
171 	CMPXCHG_LOOP(
172 		new.count++;
173 		if (old.count < 0)
174 			return 0;
175 	,
176 		return 1;
177 	);
178 
179 	spin_lock(&lockref->lock);
180 	retval = 0;
181 	if (lockref->count >= 0) {
182 		lockref->count++;
183 		retval = 1;
184 	}
185 	spin_unlock(&lockref->lock);
186 	return retval;
187 }
188 EXPORT_SYMBOL(lockref_get_not_dead);
189