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