xref: /linux-6.15/lib/lockref.c (revision bc08b449)
1 #include <linux/export.h>
2 #include <linux/lockref.h>
3 
4 #ifdef CONFIG_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 = ACCESS_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 = cmpxchg(&lockref->lock_count,			\
18 					 old.lock_count, new.lock_count);	\
19 		if (likely(old.lock_count == prev.lock_count)) {		\
20 			SUCCESS;						\
21 		}								\
22 	}									\
23 } while (0)
24 
25 #else
26 
27 #define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
28 
29 #endif
30 
31 /**
32  * lockref_get - Increments reference count unconditionally
33  * @lockcnt: pointer to lockref structure
34  *
35  * This operation is only valid if you already hold a reference
36  * to the object, so you know the count cannot be zero.
37  */
38 void lockref_get(struct lockref *lockref)
39 {
40 	CMPXCHG_LOOP(
41 		new.count++;
42 	,
43 		return;
44 	);
45 
46 	spin_lock(&lockref->lock);
47 	lockref->count++;
48 	spin_unlock(&lockref->lock);
49 }
50 EXPORT_SYMBOL(lockref_get);
51 
52 /**
53  * lockref_get_not_zero - Increments count unless the count is 0
54  * @lockcnt: pointer to lockref structure
55  * Return: 1 if count updated successfully or 0 if count was zero
56  */
57 int lockref_get_not_zero(struct lockref *lockref)
58 {
59 	int retval;
60 
61 	CMPXCHG_LOOP(
62 		new.count++;
63 		if (!old.count)
64 			return 0;
65 	,
66 		return 1;
67 	);
68 
69 	spin_lock(&lockref->lock);
70 	retval = 0;
71 	if (lockref->count) {
72 		lockref->count++;
73 		retval = 1;
74 	}
75 	spin_unlock(&lockref->lock);
76 	return retval;
77 }
78 EXPORT_SYMBOL(lockref_get_not_zero);
79 
80 /**
81  * lockref_get_or_lock - Increments count unless the count is 0
82  * @lockcnt: pointer to lockref structure
83  * Return: 1 if count updated successfully or 0 if count was zero
84  * and we got the lock instead.
85  */
86 int lockref_get_or_lock(struct lockref *lockref)
87 {
88 	CMPXCHG_LOOP(
89 		new.count++;
90 		if (!old.count)
91 			break;
92 	,
93 		return 1;
94 	);
95 
96 	spin_lock(&lockref->lock);
97 	if (!lockref->count)
98 		return 0;
99 	lockref->count++;
100 	spin_unlock(&lockref->lock);
101 	return 1;
102 }
103 EXPORT_SYMBOL(lockref_get_or_lock);
104 
105 /**
106  * lockref_put_or_lock - decrements count unless count <= 1 before decrement
107  * @lockcnt: pointer to lockref structure
108  * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
109  */
110 int lockref_put_or_lock(struct lockref *lockref)
111 {
112 	CMPXCHG_LOOP(
113 		new.count--;
114 		if (old.count <= 1)
115 			break;
116 	,
117 		return 1;
118 	);
119 
120 	spin_lock(&lockref->lock);
121 	if (lockref->count <= 1)
122 		return 0;
123 	lockref->count--;
124 	spin_unlock(&lockref->lock);
125 	return 1;
126 }
127 EXPORT_SYMBOL(lockref_put_or_lock);
128