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