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