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