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