18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Out-of-line refcount functions.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/mutex.h>
78c2ecf20Sopenharmony_ci#include <linux/refcount.h>
88c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
98c2ecf20Sopenharmony_ci#include <linux/bug.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define REFCOUNT_WARN(str)	WARN_ONCE(1, "refcount_t: " str ".\n")
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_civoid refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	refcount_set(r, REFCOUNT_SATURATED);
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	switch (t) {
188c2ecf20Sopenharmony_ci	case REFCOUNT_ADD_NOT_ZERO_OVF:
198c2ecf20Sopenharmony_ci		REFCOUNT_WARN("saturated; leaking memory");
208c2ecf20Sopenharmony_ci		break;
218c2ecf20Sopenharmony_ci	case REFCOUNT_ADD_OVF:
228c2ecf20Sopenharmony_ci		REFCOUNT_WARN("saturated; leaking memory");
238c2ecf20Sopenharmony_ci		break;
248c2ecf20Sopenharmony_ci	case REFCOUNT_ADD_UAF:
258c2ecf20Sopenharmony_ci		REFCOUNT_WARN("addition on 0; use-after-free");
268c2ecf20Sopenharmony_ci		break;
278c2ecf20Sopenharmony_ci	case REFCOUNT_SUB_UAF:
288c2ecf20Sopenharmony_ci		REFCOUNT_WARN("underflow; use-after-free");
298c2ecf20Sopenharmony_ci		break;
308c2ecf20Sopenharmony_ci	case REFCOUNT_DEC_LEAK:
318c2ecf20Sopenharmony_ci		REFCOUNT_WARN("decrement hit 0; leaking memory");
328c2ecf20Sopenharmony_ci		break;
338c2ecf20Sopenharmony_ci	default:
348c2ecf20Sopenharmony_ci		REFCOUNT_WARN("unknown saturation event!?");
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_warn_saturate);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/**
408c2ecf20Sopenharmony_ci * refcount_dec_if_one - decrement a refcount if it is 1
418c2ecf20Sopenharmony_ci * @r: the refcount
428c2ecf20Sopenharmony_ci *
438c2ecf20Sopenharmony_ci * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
448c2ecf20Sopenharmony_ci * success thereof.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Like all decrement operations, it provides release memory order and provides
478c2ecf20Sopenharmony_ci * a control dependency.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * It can be used like a try-delete operator; this explicit case is provided
508c2ecf20Sopenharmony_ci * and not cmpxchg in generic, because that would allow implementing unsafe
518c2ecf20Sopenharmony_ci * operations.
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * Return: true if the resulting refcount is 0, false otherwise
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cibool refcount_dec_if_one(refcount_t *r)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	int val = 1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return atomic_try_cmpxchg_release(&r->refs, &val, 0);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_if_one);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/**
648c2ecf20Sopenharmony_ci * refcount_dec_not_one - decrement a refcount if it is not 1
658c2ecf20Sopenharmony_ci * @r: the refcount
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * No atomic_t counterpart, it decrements unless the value is 1, in which case
688c2ecf20Sopenharmony_ci * it will return false.
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Was often done like: atomic_add_unless(&var, -1, 1)
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Return: true if the decrement operation was successful, false otherwise
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cibool refcount_dec_not_one(refcount_t *r)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned int new, val = atomic_read(&r->refs);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	do {
798c2ecf20Sopenharmony_ci		if (unlikely(val == REFCOUNT_SATURATED))
808c2ecf20Sopenharmony_ci			return true;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		if (val == 1)
838c2ecf20Sopenharmony_ci			return false;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		new = val - 1;
868c2ecf20Sopenharmony_ci		if (new > val) {
878c2ecf20Sopenharmony_ci			WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
888c2ecf20Sopenharmony_ci			return true;
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	} while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return true;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_not_one);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/**
988c2ecf20Sopenharmony_ci * refcount_dec_and_mutex_lock - return holding mutex if able to decrement
998c2ecf20Sopenharmony_ci *                               refcount to 0
1008c2ecf20Sopenharmony_ci * @r: the refcount
1018c2ecf20Sopenharmony_ci * @lock: the mutex to be locked
1028c2ecf20Sopenharmony_ci *
1038c2ecf20Sopenharmony_ci * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
1048c2ecf20Sopenharmony_ci * to decrement when saturated at REFCOUNT_SATURATED.
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * Provides release memory ordering, such that prior loads and stores are done
1078c2ecf20Sopenharmony_ci * before, and provides a control dependency such that free() must come after.
1088c2ecf20Sopenharmony_ci * See the comment on top.
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * Return: true and hold mutex if able to decrement refcount to 0, false
1118c2ecf20Sopenharmony_ci *         otherwise
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cibool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	if (refcount_dec_not_one(r))
1168c2ecf20Sopenharmony_ci		return false;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	mutex_lock(lock);
1198c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(r)) {
1208c2ecf20Sopenharmony_ci		mutex_unlock(lock);
1218c2ecf20Sopenharmony_ci		return false;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return true;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_and_mutex_lock);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci * refcount_dec_and_lock - return holding spinlock if able to decrement
1308c2ecf20Sopenharmony_ci *                         refcount to 0
1318c2ecf20Sopenharmony_ci * @r: the refcount
1328c2ecf20Sopenharmony_ci * @lock: the spinlock to be locked
1338c2ecf20Sopenharmony_ci *
1348c2ecf20Sopenharmony_ci * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
1358c2ecf20Sopenharmony_ci * decrement when saturated at REFCOUNT_SATURATED.
1368c2ecf20Sopenharmony_ci *
1378c2ecf20Sopenharmony_ci * Provides release memory ordering, such that prior loads and stores are done
1388c2ecf20Sopenharmony_ci * before, and provides a control dependency such that free() must come after.
1398c2ecf20Sopenharmony_ci * See the comment on top.
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * Return: true and hold spinlock if able to decrement refcount to 0, false
1428c2ecf20Sopenharmony_ci *         otherwise
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cibool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	if (refcount_dec_not_one(r))
1478c2ecf20Sopenharmony_ci		return false;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	spin_lock(lock);
1508c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(r)) {
1518c2ecf20Sopenharmony_ci		spin_unlock(lock);
1528c2ecf20Sopenharmony_ci		return false;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return true;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_and_lock);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/**
1608c2ecf20Sopenharmony_ci * refcount_dec_and_lock_irqsave - return holding spinlock with disabled
1618c2ecf20Sopenharmony_ci *                                 interrupts if able to decrement refcount to 0
1628c2ecf20Sopenharmony_ci * @r: the refcount
1638c2ecf20Sopenharmony_ci * @lock: the spinlock to be locked
1648c2ecf20Sopenharmony_ci * @flags: saved IRQ-flags if the is acquired
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * Same as refcount_dec_and_lock() above except that the spinlock is acquired
1678c2ecf20Sopenharmony_ci * with disabled interupts.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * Return: true and hold spinlock if able to decrement refcount to 0, false
1708c2ecf20Sopenharmony_ci *         otherwise
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cibool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock,
1738c2ecf20Sopenharmony_ci				   unsigned long *flags)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	if (refcount_dec_not_one(r))
1768c2ecf20Sopenharmony_ci		return false;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	spin_lock_irqsave(lock, *flags);
1798c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(r)) {
1808c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(lock, *flags);
1818c2ecf20Sopenharmony_ci		return false;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return true;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(refcount_dec_and_lock_irqsave);
187