aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/kref.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/linux/kref.h')
-rw-r--r--include/linux/kref.h42
1 files changed, 39 insertions, 3 deletions
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 4972e6e9ca93..484604d184be 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -19,6 +19,7 @@
19#include <linux/atomic.h> 19#include <linux/atomic.h>
20#include <linux/kernel.h> 20#include <linux/kernel.h>
21#include <linux/mutex.h> 21#include <linux/mutex.h>
22#include <linux/spinlock.h>
22 23
23struct kref { 24struct kref {
24 atomic_t refcount; 25 atomic_t refcount;
@@ -39,8 +40,11 @@ static inline void kref_init(struct kref *kref)
39 */ 40 */
40static inline void kref_get(struct kref *kref) 41static inline void kref_get(struct kref *kref)
41{ 42{
42 WARN_ON(!atomic_read(&kref->refcount)); 43 /* If refcount was 0 before incrementing then we have a race
43 atomic_inc(&kref->refcount); 44 * condition when this kref is freeing by some other thread right now.
45 * In this case one should use kref_get_unless_zero()
46 */
47 WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
44} 48}
45 49
46/** 50/**
@@ -95,12 +99,44 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)
95 return kref_sub(kref, 1, release); 99 return kref_sub(kref, 1, release);
96} 100}
97 101
102/**
103 * kref_put_spinlock_irqsave - decrement refcount for object.
104 * @kref: object.
105 * @release: pointer to the function that will clean up the object when the
106 * last reference to the object is released.
107 * This pointer is required, and it is not acceptable to pass kfree
108 * in as this function.
109 * @lock: lock to take in release case
110 *
111 * Behaves identical to kref_put with one exception. If the reference count
112 * drops to zero, the lock will be taken atomically wrt dropping the reference
113 * count. The release function has to call spin_unlock() without _irqrestore.
114 */
115static inline int kref_put_spinlock_irqsave(struct kref *kref,
116 void (*release)(struct kref *kref),
117 spinlock_t *lock)
118{
119 unsigned long flags;
120
121 WARN_ON(release == NULL);
122 if (atomic_add_unless(&kref->refcount, -1, 1))
123 return 0;
124 spin_lock_irqsave(lock, flags);
125 if (atomic_dec_and_test(&kref->refcount)) {
126 release(kref);
127 local_irq_restore(flags);
128 return 1;
129 }
130 spin_unlock_irqrestore(lock, flags);
131 return 0;
132}
133
98static inline int kref_put_mutex(struct kref *kref, 134static inline int kref_put_mutex(struct kref *kref,
99 void (*release)(struct kref *kref), 135 void (*release)(struct kref *kref),
100 struct mutex *lock) 136 struct mutex *lock)
101{ 137{
102 WARN_ON(release == NULL); 138 WARN_ON(release == NULL);
103 if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) { 139 if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
104 mutex_lock(lock); 140 mutex_lock(lock);
105 if (unlikely(!atomic_dec_and_test(&kref->refcount))) { 141 if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
106 mutex_unlock(lock); 142 mutex_unlock(lock);