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 void lockref_get(struct lockref *lockref) 43 { 44 CMPXCHG_LOOP( 45 new.count++; 46 , 47 return; 48 ); 49 50 spin_lock(&lockref->lock); 51 lockref->count++; 52 spin_unlock(&lockref->lock); 53 } 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 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 spin_lock(&lockref->lock); 74 retval = 0; 75 if (lockref->count > 0) { 76 lockref->count++; 77 retval = 1; 78 } 79 spin_unlock(&lockref->lock); 80 return retval; 81 } 82 EXPORT_SYMBOL(lockref_get_not_zero); 83 84 /** 85 * lockref_put_return - Decrement reference count if possible 86 * @lockref: pointer to lockref structure 87 * 88 * Decrement the reference count and return the new value. 89 * If the lockref was dead or locked, return -1. 90 */ 91 int lockref_put_return(struct lockref *lockref) 92 { 93 CMPXCHG_LOOP( 94 new.count--; 95 if (old.count <= 0) 96 return -1; 97 , 98 return new.count; 99 ); 100 return -1; 101 } 102 EXPORT_SYMBOL(lockref_put_return); 103 104 /** 105 * lockref_put_or_lock - decrements count unless count <= 1 before decrement 106 * @lockref: pointer to lockref structure 107 * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken 108 */ 109 int lockref_put_or_lock(struct lockref *lockref) 110 { 111 CMPXCHG_LOOP( 112 new.count--; 113 if (old.count <= 1) 114 break; 115 , 116 return 1; 117 ); 118 119 spin_lock(&lockref->lock); 120 if (lockref->count <= 1) 121 return 0; 122 lockref->count--; 123 spin_unlock(&lockref->lock); 124 return 1; 125 } 126 EXPORT_SYMBOL(lockref_put_or_lock); 127 128 /** 129 * lockref_mark_dead - mark lockref dead 130 * @lockref: pointer to lockref structure 131 */ 132 void lockref_mark_dead(struct lockref *lockref) 133 { 134 assert_spin_locked(&lockref->lock); 135 lockref->count = -128; 136 } 137 EXPORT_SYMBOL(lockref_mark_dead); 138 139 /** 140 * lockref_get_not_dead - Increments count unless the ref is dead 141 * @lockref: pointer to lockref structure 142 * Return: 1 if count updated successfully or 0 if lockref was dead 143 */ 144 int lockref_get_not_dead(struct lockref *lockref) 145 { 146 int retval; 147 148 CMPXCHG_LOOP( 149 new.count++; 150 if (old.count < 0) 151 return 0; 152 , 153 return 1; 154 ); 155 156 spin_lock(&lockref->lock); 157 retval = 0; 158 if (lockref->count >= 0) { 159 lockref->count++; 160 retval = 1; 161 } 162 spin_unlock(&lockref->lock); 163 return retval; 164 } 165 EXPORT_SYMBOL(lockref_get_not_dead); 166