162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/export.h> 362306a36Sopenharmony_ci#include <linux/lockref.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#if USE_CMPXCHG_LOCKREF 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * Note that the "cmpxchg()" reloads the "old" value for the 962306a36Sopenharmony_ci * failure case. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#define CMPXCHG_LOOP(CODE, SUCCESS) do { \ 1262306a36Sopenharmony_ci int retry = 100; \ 1362306a36Sopenharmony_ci struct lockref old; \ 1462306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(old) != 8); \ 1562306a36Sopenharmony_ci old.lock_count = READ_ONCE(lockref->lock_count); \ 1662306a36Sopenharmony_ci while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \ 1762306a36Sopenharmony_ci struct lockref new = old; \ 1862306a36Sopenharmony_ci CODE \ 1962306a36Sopenharmony_ci if (likely(try_cmpxchg64_relaxed(&lockref->lock_count, \ 2062306a36Sopenharmony_ci &old.lock_count, \ 2162306a36Sopenharmony_ci new.lock_count))) { \ 2262306a36Sopenharmony_ci SUCCESS; \ 2362306a36Sopenharmony_ci } \ 2462306a36Sopenharmony_ci if (!--retry) \ 2562306a36Sopenharmony_ci break; \ 2662306a36Sopenharmony_ci } \ 2762306a36Sopenharmony_ci} while (0) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#endif 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * lockref_get - Increments reference count unconditionally 3762306a36Sopenharmony_ci * @lockref: pointer to lockref structure 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * This operation is only valid if you already hold a reference 4062306a36Sopenharmony_ci * to the object, so you know the count cannot be zero. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_civoid lockref_get(struct lockref *lockref) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci CMPXCHG_LOOP( 4562306a36Sopenharmony_ci new.count++; 4662306a36Sopenharmony_ci , 4762306a36Sopenharmony_ci return; 4862306a36Sopenharmony_ci ); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci spin_lock(&lockref->lock); 5162306a36Sopenharmony_ci lockref->count++; 5262306a36Sopenharmony_ci spin_unlock(&lockref->lock); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_get); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * lockref_get_not_zero - Increments count unless the count is 0 or dead 5862306a36Sopenharmony_ci * @lockref: pointer to lockref structure 5962306a36Sopenharmony_ci * Return: 1 if count updated successfully or 0 if count was zero 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciint lockref_get_not_zero(struct lockref *lockref) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int retval; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci CMPXCHG_LOOP( 6662306a36Sopenharmony_ci new.count++; 6762306a36Sopenharmony_ci if (old.count <= 0) 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci , 7062306a36Sopenharmony_ci return 1; 7162306a36Sopenharmony_ci ); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_lock(&lockref->lock); 7462306a36Sopenharmony_ci retval = 0; 7562306a36Sopenharmony_ci if (lockref->count > 0) { 7662306a36Sopenharmony_ci lockref->count++; 7762306a36Sopenharmony_ci retval = 1; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci spin_unlock(&lockref->lock); 8062306a36Sopenharmony_ci return retval; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_get_not_zero); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * lockref_put_not_zero - Decrements count unless count <= 1 before decrement 8662306a36Sopenharmony_ci * @lockref: pointer to lockref structure 8762306a36Sopenharmony_ci * Return: 1 if count updated successfully or 0 if count would become zero 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ciint lockref_put_not_zero(struct lockref *lockref) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int retval; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci CMPXCHG_LOOP( 9462306a36Sopenharmony_ci new.count--; 9562306a36Sopenharmony_ci if (old.count <= 1) 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci , 9862306a36Sopenharmony_ci return 1; 9962306a36Sopenharmony_ci ); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci spin_lock(&lockref->lock); 10262306a36Sopenharmony_ci retval = 0; 10362306a36Sopenharmony_ci if (lockref->count > 1) { 10462306a36Sopenharmony_ci lockref->count--; 10562306a36Sopenharmony_ci retval = 1; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci spin_unlock(&lockref->lock); 10862306a36Sopenharmony_ci return retval; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_put_not_zero); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * lockref_put_return - Decrement reference count if possible 11462306a36Sopenharmony_ci * @lockref: pointer to lockref structure 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Decrement the reference count and return the new value. 11762306a36Sopenharmony_ci * If the lockref was dead or locked, return an error. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ciint lockref_put_return(struct lockref *lockref) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci CMPXCHG_LOOP( 12262306a36Sopenharmony_ci new.count--; 12362306a36Sopenharmony_ci if (old.count <= 0) 12462306a36Sopenharmony_ci return -1; 12562306a36Sopenharmony_ci , 12662306a36Sopenharmony_ci return new.count; 12762306a36Sopenharmony_ci ); 12862306a36Sopenharmony_ci return -1; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_put_return); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * lockref_put_or_lock - decrements count unless count <= 1 before decrement 13462306a36Sopenharmony_ci * @lockref: pointer to lockref structure 13562306a36Sopenharmony_ci * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ciint lockref_put_or_lock(struct lockref *lockref) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci CMPXCHG_LOOP( 14062306a36Sopenharmony_ci new.count--; 14162306a36Sopenharmony_ci if (old.count <= 1) 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci , 14462306a36Sopenharmony_ci return 1; 14562306a36Sopenharmony_ci ); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock(&lockref->lock); 14862306a36Sopenharmony_ci if (lockref->count <= 1) 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci lockref->count--; 15162306a36Sopenharmony_ci spin_unlock(&lockref->lock); 15262306a36Sopenharmony_ci return 1; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_put_or_lock); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * lockref_mark_dead - mark lockref dead 15862306a36Sopenharmony_ci * @lockref: pointer to lockref structure 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_civoid lockref_mark_dead(struct lockref *lockref) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci assert_spin_locked(&lockref->lock); 16362306a36Sopenharmony_ci lockref->count = -128; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_mark_dead); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * lockref_get_not_dead - Increments count unless the ref is dead 16962306a36Sopenharmony_ci * @lockref: pointer to lockref structure 17062306a36Sopenharmony_ci * Return: 1 if count updated successfully or 0 if lockref was dead 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ciint lockref_get_not_dead(struct lockref *lockref) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int retval; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci CMPXCHG_LOOP( 17762306a36Sopenharmony_ci new.count++; 17862306a36Sopenharmony_ci if (old.count < 0) 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci , 18162306a36Sopenharmony_ci return 1; 18262306a36Sopenharmony_ci ); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci spin_lock(&lockref->lock); 18562306a36Sopenharmony_ci retval = 0; 18662306a36Sopenharmony_ci if (lockref->count >= 0) { 18762306a36Sopenharmony_ci lockref->count++; 18862306a36Sopenharmony_ci retval = 1; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci spin_unlock(&lockref->lock); 19162306a36Sopenharmony_ci return retval; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ciEXPORT_SYMBOL(lockref_get_not_dead); 194