194 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/export.h>3#include <linux/lockref.h>4 5#if USE_CMPXCHG_LOCKREF6 7/*8 * Note that the "cmpxchg()" reloads the "old" value for the9 * 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#else30 31#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)32 33#endif34 35/**36 * lockref_get - Increments reference count unconditionally37 * @lockref: pointer to lockref structure38 *39 * This operation is only valid if you already hold a reference40 * to the object, so you know the count cannot be zero.41 */42void 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}54EXPORT_SYMBOL(lockref_get);55 56/**57 * lockref_get_not_zero - Increments count unless the count is 0 or dead58 * @lockref: pointer to lockref structure59 * Return: 1 if count updated successfully or 0 if count was zero60 */61int 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}82EXPORT_SYMBOL(lockref_get_not_zero);83 84/**85 * lockref_put_not_zero - Decrements count unless count <= 1 before decrement86 * @lockref: pointer to lockref structure87 * Return: 1 if count updated successfully or 0 if count would become zero88 */89int 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 spin_lock(&lockref->lock);102 retval = 0;103 if (lockref->count > 1) {104 lockref->count--;105 retval = 1;106 }107 spin_unlock(&lockref->lock);108 return retval;109}110EXPORT_SYMBOL(lockref_put_not_zero);111 112/**113 * lockref_put_return - Decrement reference count if possible114 * @lockref: pointer to lockref structure115 *116 * Decrement the reference count and return the new value.117 * If the lockref was dead or locked, return an error.118 */119int 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 return -1;129}130EXPORT_SYMBOL(lockref_put_return);131 132/**133 * lockref_put_or_lock - decrements count unless count <= 1 before decrement134 * @lockref: pointer to lockref structure135 * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken136 */137int 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 spin_lock(&lockref->lock);148 if (lockref->count <= 1)149 return 0;150 lockref->count--;151 spin_unlock(&lockref->lock);152 return 1;153}154EXPORT_SYMBOL(lockref_put_or_lock);155 156/**157 * lockref_mark_dead - mark lockref dead158 * @lockref: pointer to lockref structure159 */160void lockref_mark_dead(struct lockref *lockref)161{162 assert_spin_locked(&lockref->lock);163 lockref->count = -128;164}165EXPORT_SYMBOL(lockref_mark_dead);166 167/**168 * lockref_get_not_dead - Increments count unless the ref is dead169 * @lockref: pointer to lockref structure170 * Return: 1 if count updated successfully or 0 if lockref was dead171 */172int 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 spin_lock(&lockref->lock);185 retval = 0;186 if (lockref->count >= 0) {187 lockref->count++;188 retval = 1;189 }190 spin_unlock(&lockref->lock);191 return retval;192}193EXPORT_SYMBOL(lockref_get_not_dead);194