162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/export.h>
362306a36Sopenharmony_ci#include <linux/spinlock.h>
462306a36Sopenharmony_ci#include <linux/atomic.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * This is an implementation of the notion of "decrement a
862306a36Sopenharmony_ci * reference count, and return locked if it decremented to zero".
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * NOTE NOTE NOTE! This is _not_ equivalent to
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *	if (atomic_dec_and_test(&atomic)) {
1362306a36Sopenharmony_ci *		spin_lock(&lock);
1462306a36Sopenharmony_ci *		return 1;
1562306a36Sopenharmony_ci *	}
1662306a36Sopenharmony_ci *	return 0;
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * because the spin-lock and the decrement must be
1962306a36Sopenharmony_ci * "atomic".
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ciint _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
2462306a36Sopenharmony_ci	if (atomic_add_unless(atomic, -1, 1))
2562306a36Sopenharmony_ci		return 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* Otherwise do it the slow way */
2862306a36Sopenharmony_ci	spin_lock(lock);
2962306a36Sopenharmony_ci	if (atomic_dec_and_test(atomic))
3062306a36Sopenharmony_ci		return 1;
3162306a36Sopenharmony_ci	spin_unlock(lock);
3262306a36Sopenharmony_ci	return 0;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciEXPORT_SYMBOL(_atomic_dec_and_lock);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciint _atomic_dec_and_lock_irqsave(atomic_t *atomic, spinlock_t *lock,
3862306a36Sopenharmony_ci				 unsigned long *flags)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
4162306a36Sopenharmony_ci	if (atomic_add_unless(atomic, -1, 1))
4262306a36Sopenharmony_ci		return 0;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* Otherwise do it the slow way */
4562306a36Sopenharmony_ci	spin_lock_irqsave(lock, *flags);
4662306a36Sopenharmony_ci	if (atomic_dec_and_test(atomic))
4762306a36Sopenharmony_ci		return 1;
4862306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, *flags);
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ciEXPORT_SYMBOL(_atomic_dec_and_lock_irqsave);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciint _atomic_dec_and_raw_lock(atomic_t *atomic, raw_spinlock_t *lock)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
5662306a36Sopenharmony_ci	if (atomic_add_unless(atomic, -1, 1))
5762306a36Sopenharmony_ci		return 0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Otherwise do it the slow way */
6062306a36Sopenharmony_ci	raw_spin_lock(lock);
6162306a36Sopenharmony_ci	if (atomic_dec_and_test(atomic))
6262306a36Sopenharmony_ci		return 1;
6362306a36Sopenharmony_ci	raw_spin_unlock(lock);
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ciEXPORT_SYMBOL(_atomic_dec_and_raw_lock);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint _atomic_dec_and_raw_lock_irqsave(atomic_t *atomic, raw_spinlock_t *lock,
6962306a36Sopenharmony_ci				     unsigned long *flags)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	/* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
7262306a36Sopenharmony_ci	if (atomic_add_unless(atomic, -1, 1))
7362306a36Sopenharmony_ci		return 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Otherwise do it the slow way */
7662306a36Sopenharmony_ci	raw_spin_lock_irqsave(lock, *flags);
7762306a36Sopenharmony_ci	if (atomic_dec_and_test(atomic))
7862306a36Sopenharmony_ci		return 1;
7962306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(lock, *flags);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ciEXPORT_SYMBOL(_atomic_dec_and_raw_lock_irqsave);
83