xref: /linux-6.15/lib/lockref.c (revision 6d2868d5)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
22f4f12e5SLinus Torvalds #include <linux/export.h>
32f4f12e5SLinus Torvalds #include <linux/lockref.h>
42f4f12e5SLinus Torvalds 
557f4257eSPeter Zijlstra #if USE_CMPXCHG_LOCKREF
6bc08b449SLinus Torvalds 
7bc08b449SLinus Torvalds /*
8bc08b449SLinus Torvalds  * Note that the "cmpxchg()" reloads the "old" value for the
9bc08b449SLinus Torvalds  * failure case.
10bc08b449SLinus Torvalds  */
11bc08b449SLinus Torvalds #define CMPXCHG_LOOP(CODE, SUCCESS) do {					\
12893a7d32SJan Glauber 	int retry = 100;							\
13bc08b449SLinus Torvalds 	struct lockref old;							\
14bc08b449SLinus Torvalds 	BUILD_BUG_ON(sizeof(old) != 8);						\
154d3199e4SDavidlohr Bueso 	old.lock_count = READ_ONCE(lockref->lock_count);			\
16bc08b449SLinus Torvalds 	while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) {  	\
173378323bSUros Bizjak 		struct lockref new = old;					\
18bc08b449SLinus Torvalds 		CODE								\
193378323bSUros Bizjak 		if (likely(try_cmpxchg64_relaxed(&lockref->lock_count,		\
203378323bSUros Bizjak 						 &old.lock_count,		\
213378323bSUros Bizjak 						 new.lock_count))) {		\
22bc08b449SLinus Torvalds 			SUCCESS;						\
23bc08b449SLinus Torvalds 		}								\
24893a7d32SJan Glauber 		if (!--retry)							\
25893a7d32SJan Glauber 			break;							\
26bc08b449SLinus Torvalds 	}									\
27bc08b449SLinus Torvalds } while (0)
28bc08b449SLinus Torvalds 
29bc08b449SLinus Torvalds #else
30bc08b449SLinus Torvalds 
31bc08b449SLinus Torvalds #define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
32bc08b449SLinus Torvalds 
33bc08b449SLinus Torvalds #endif
34bc08b449SLinus Torvalds 
352f4f12e5SLinus Torvalds /**
362f4f12e5SLinus Torvalds  * lockref_get - Increments reference count unconditionally
3744a0cf92SLinus Torvalds  * @lockref: pointer to lockref structure
382f4f12e5SLinus Torvalds  *
392f4f12e5SLinus Torvalds  * This operation is only valid if you already hold a reference
402f4f12e5SLinus Torvalds  * to the object, so you know the count cannot be zero.
412f4f12e5SLinus Torvalds  */
lockref_get(struct lockref * lockref)422f4f12e5SLinus Torvalds void lockref_get(struct lockref *lockref)
432f4f12e5SLinus Torvalds {
44bc08b449SLinus Torvalds 	CMPXCHG_LOOP(
45bc08b449SLinus Torvalds 		new.count++;
46bc08b449SLinus Torvalds 	,
47bc08b449SLinus Torvalds 		return;
48bc08b449SLinus Torvalds 	);
49bc08b449SLinus Torvalds 
502f4f12e5SLinus Torvalds 	spin_lock(&lockref->lock);
512f4f12e5SLinus Torvalds 	lockref->count++;
522f4f12e5SLinus Torvalds 	spin_unlock(&lockref->lock);
532f4f12e5SLinus Torvalds }
542f4f12e5SLinus Torvalds EXPORT_SYMBOL(lockref_get);
552f4f12e5SLinus Torvalds 
562f4f12e5SLinus Torvalds /**
57360f5479SLinus Torvalds  * lockref_get_not_zero - Increments count unless the count is 0 or dead
5844a0cf92SLinus Torvalds  * @lockref: pointer to lockref structure
592f4f12e5SLinus Torvalds  * Return: 1 if count updated successfully or 0 if count was zero
602f4f12e5SLinus Torvalds  */
lockref_get_not_zero(struct lockref * lockref)61*6d2868d5SChristoph Hellwig bool lockref_get_not_zero(struct lockref *lockref)
622f4f12e5SLinus Torvalds {
63*6d2868d5SChristoph Hellwig 	bool retval = false;
64bc08b449SLinus Torvalds 
65bc08b449SLinus Torvalds 	CMPXCHG_LOOP(
66bc08b449SLinus Torvalds 		new.count++;
67360f5479SLinus Torvalds 		if (old.count <= 0)
68*6d2868d5SChristoph Hellwig 			return false;
69bc08b449SLinus Torvalds 	,
70*6d2868d5SChristoph Hellwig 		return true;
71bc08b449SLinus Torvalds 	);
722f4f12e5SLinus Torvalds 
732f4f12e5SLinus Torvalds 	spin_lock(&lockref->lock);
74360f5479SLinus Torvalds 	if (lockref->count > 0) {
752f4f12e5SLinus Torvalds 		lockref->count++;
76*6d2868d5SChristoph Hellwig 		retval = true;
772f4f12e5SLinus Torvalds 	}
782f4f12e5SLinus Torvalds 	spin_unlock(&lockref->lock);
792f4f12e5SLinus Torvalds 	return retval;
802f4f12e5SLinus Torvalds }
812f4f12e5SLinus Torvalds EXPORT_SYMBOL(lockref_get_not_zero);
822f4f12e5SLinus Torvalds 
832f4f12e5SLinus Torvalds /**
84360f5479SLinus Torvalds  * lockref_put_return - Decrement reference count if possible
85360f5479SLinus Torvalds  * @lockref: pointer to lockref structure
86360f5479SLinus Torvalds  *
87360f5479SLinus Torvalds  * Decrement the reference count and return the new value.
88d60f2280SChristoph Hellwig  * If the lockref was dead or locked, return -1.
89360f5479SLinus Torvalds  */
lockref_put_return(struct lockref * lockref)90360f5479SLinus Torvalds int lockref_put_return(struct lockref *lockref)
91360f5479SLinus Torvalds {
92360f5479SLinus Torvalds 	CMPXCHG_LOOP(
93360f5479SLinus Torvalds 		new.count--;
94360f5479SLinus Torvalds 		if (old.count <= 0)
95360f5479SLinus Torvalds 			return -1;
96360f5479SLinus Torvalds 	,
97360f5479SLinus Torvalds 		return new.count;
98360f5479SLinus Torvalds 	);
99360f5479SLinus Torvalds 	return -1;
100360f5479SLinus Torvalds }
101360f5479SLinus Torvalds EXPORT_SYMBOL(lockref_put_return);
102360f5479SLinus Torvalds 
103360f5479SLinus Torvalds /**
1042f4f12e5SLinus Torvalds  * lockref_put_or_lock - decrements count unless count <= 1 before decrement
10544a0cf92SLinus Torvalds  * @lockref: pointer to lockref structure
1062f4f12e5SLinus Torvalds  * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
1072f4f12e5SLinus Torvalds  */
lockref_put_or_lock(struct lockref * lockref)108*6d2868d5SChristoph Hellwig bool lockref_put_or_lock(struct lockref *lockref)
1092f4f12e5SLinus Torvalds {
110bc08b449SLinus Torvalds 	CMPXCHG_LOOP(
111bc08b449SLinus Torvalds 		new.count--;
112bc08b449SLinus Torvalds 		if (old.count <= 1)
113bc08b449SLinus Torvalds 			break;
114bc08b449SLinus Torvalds 	,
115*6d2868d5SChristoph Hellwig 		return true;
116bc08b449SLinus Torvalds 	);
117bc08b449SLinus Torvalds 
1182f4f12e5SLinus Torvalds 	spin_lock(&lockref->lock);
1192f4f12e5SLinus Torvalds 	if (lockref->count <= 1)
120*6d2868d5SChristoph Hellwig 		return false;
1212f4f12e5SLinus Torvalds 	lockref->count--;
1222f4f12e5SLinus Torvalds 	spin_unlock(&lockref->lock);
123*6d2868d5SChristoph Hellwig 	return true;
1242f4f12e5SLinus Torvalds }
1252f4f12e5SLinus Torvalds EXPORT_SYMBOL(lockref_put_or_lock);
126e7d33bb5SLinus Torvalds 
127e7d33bb5SLinus Torvalds /**
128e7d33bb5SLinus Torvalds  * lockref_mark_dead - mark lockref dead
129e7d33bb5SLinus Torvalds  * @lockref: pointer to lockref structure
130e7d33bb5SLinus Torvalds  */
lockref_mark_dead(struct lockref * lockref)131e7d33bb5SLinus Torvalds void lockref_mark_dead(struct lockref *lockref)
132e7d33bb5SLinus Torvalds {
133e7d33bb5SLinus Torvalds 	assert_spin_locked(&lockref->lock);
134e7d33bb5SLinus Torvalds 	lockref->count = -128;
135e7d33bb5SLinus Torvalds }
136e66cf161SSteven Whitehouse EXPORT_SYMBOL(lockref_mark_dead);
137e7d33bb5SLinus Torvalds 
138e7d33bb5SLinus Torvalds /**
139e7d33bb5SLinus Torvalds  * lockref_get_not_dead - Increments count unless the ref is dead
140e7d33bb5SLinus Torvalds  * @lockref: pointer to lockref structure
141e7d33bb5SLinus Torvalds  * Return: 1 if count updated successfully or 0 if lockref was dead
142e7d33bb5SLinus Torvalds  */
lockref_get_not_dead(struct lockref * lockref)143*6d2868d5SChristoph Hellwig bool lockref_get_not_dead(struct lockref *lockref)
144e7d33bb5SLinus Torvalds {
145*6d2868d5SChristoph Hellwig 	bool retval = false;
146e7d33bb5SLinus Torvalds 
147e7d33bb5SLinus Torvalds 	CMPXCHG_LOOP(
148e7d33bb5SLinus Torvalds 		new.count++;
149360f5479SLinus Torvalds 		if (old.count < 0)
150*6d2868d5SChristoph Hellwig 			return false;
151e7d33bb5SLinus Torvalds 	,
152*6d2868d5SChristoph Hellwig 		return true;
153e7d33bb5SLinus Torvalds 	);
154e7d33bb5SLinus Torvalds 
155e7d33bb5SLinus Torvalds 	spin_lock(&lockref->lock);
156360f5479SLinus Torvalds 	if (lockref->count >= 0) {
157e7d33bb5SLinus Torvalds 		lockref->count++;
158*6d2868d5SChristoph Hellwig 		retval = true;
159e7d33bb5SLinus Torvalds 	}
160e7d33bb5SLinus Torvalds 	spin_unlock(&lockref->lock);
161e7d33bb5SLinus Torvalds 	return retval;
162e7d33bb5SLinus Torvalds }
163e7d33bb5SLinus Torvalds EXPORT_SYMBOL(lockref_get_not_dead);
164