blob: 7819c2d1d315eff9a010b989d716f1b46387ef4e [file] [log] [blame]
Linus Torvalds2f4f12e2013-09-02 11:58:20 -07001#include <linux/export.h>
2#include <linux/lockref.h>
3
Linus Torvaldsbc08b442013-09-02 12:12:15 -07004#ifdef CONFIG_CMPXCHG_LOCKREF
5
6/*
7 * Note that the "cmpxchg()" reloads the "old" value for the
8 * failure case.
9 */
10#define CMPXCHG_LOOP(CODE, SUCCESS) do { \
11 struct lockref old; \
12 BUILD_BUG_ON(sizeof(old) != 8); \
13 old.lock_count = ACCESS_ONCE(lockref->lock_count); \
14 while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \
15 struct lockref new = old, prev = old; \
16 CODE \
17 old.lock_count = cmpxchg(&lockref->lock_count, \
18 old.lock_count, new.lock_count); \
19 if (likely(old.lock_count == prev.lock_count)) { \
20 SUCCESS; \
21 } \
22 } \
23} while (0)
24
25#else
26
27#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
28
29#endif
30
Linus Torvalds2f4f12e2013-09-02 11:58:20 -070031/**
32 * lockref_get - Increments reference count unconditionally
33 * @lockcnt: pointer to lockref structure
34 *
35 * This operation is only valid if you already hold a reference
36 * to the object, so you know the count cannot be zero.
37 */
38void lockref_get(struct lockref *lockref)
39{
Linus Torvaldsbc08b442013-09-02 12:12:15 -070040 CMPXCHG_LOOP(
41 new.count++;
42 ,
43 return;
44 );
45
Linus Torvalds2f4f12e2013-09-02 11:58:20 -070046 spin_lock(&lockref->lock);
47 lockref->count++;
48 spin_unlock(&lockref->lock);
49}
50EXPORT_SYMBOL(lockref_get);
51
52/**
53 * lockref_get_not_zero - Increments count unless the count is 0
54 * @lockcnt: pointer to lockref structure
55 * Return: 1 if count updated successfully or 0 if count was zero
56 */
57int lockref_get_not_zero(struct lockref *lockref)
58{
Linus Torvaldsbc08b442013-09-02 12:12:15 -070059 int retval;
60
61 CMPXCHG_LOOP(
62 new.count++;
63 if (!old.count)
64 return 0;
65 ,
66 return 1;
67 );
Linus Torvalds2f4f12e2013-09-02 11:58:20 -070068
69 spin_lock(&lockref->lock);
Linus Torvaldsbc08b442013-09-02 12:12:15 -070070 retval = 0;
Linus Torvalds2f4f12e2013-09-02 11:58:20 -070071 if (lockref->count) {
72 lockref->count++;
73 retval = 1;
74 }
75 spin_unlock(&lockref->lock);
76 return retval;
77}
78EXPORT_SYMBOL(lockref_get_not_zero);
79
80/**
81 * lockref_get_or_lock - Increments count unless the count is 0
82 * @lockcnt: pointer to lockref structure
83 * Return: 1 if count updated successfully or 0 if count was zero
84 * and we got the lock instead.
85 */
86int lockref_get_or_lock(struct lockref *lockref)
87{
Linus Torvaldsbc08b442013-09-02 12:12:15 -070088 CMPXCHG_LOOP(
89 new.count++;
90 if (!old.count)
91 break;
92 ,
93 return 1;
94 );
95
Linus Torvalds2f4f12e2013-09-02 11:58:20 -070096 spin_lock(&lockref->lock);
97 if (!lockref->count)
98 return 0;
99 lockref->count++;
100 spin_unlock(&lockref->lock);
101 return 1;
102}
103EXPORT_SYMBOL(lockref_get_or_lock);
104
105/**
106 * lockref_put_or_lock - decrements count unless count <= 1 before decrement
107 * @lockcnt: pointer to lockref structure
108 * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
109 */
110int lockref_put_or_lock(struct lockref *lockref)
111{
Linus Torvaldsbc08b442013-09-02 12:12:15 -0700112 CMPXCHG_LOOP(
113 new.count--;
114 if (old.count <= 1)
115 break;
116 ,
117 return 1;
118 );
119
Linus Torvalds2f4f12e2013-09-02 11:58:20 -0700120 spin_lock(&lockref->lock);
121 if (lockref->count <= 1)
122 return 0;
123 lockref->count--;
124 spin_unlock(&lockref->lock);
125 return 1;
126}
127EXPORT_SYMBOL(lockref_put_or_lock);