Blame view

lib/lockref.c 3.63 KB
2f4f12e57   Linus Torvalds   lockref: uninline...
1
2
  #include <linux/export.h>
  #include <linux/lockref.h>
57f4257ea   Peter Zijlstra   lockref: use BLOA...
3
  #if USE_CMPXCHG_LOCKREF
bc08b449e   Linus Torvalds   lockref: implemen...
4
5
  
  /*
d2212b4dc   Will Deacon   lockref: allow re...
6
7
8
9
10
11
12
13
   * Allow weakly-ordered memory architectures to provide barrier-less
   * cmpxchg semantics for lockref updates.
   */
  #ifndef cmpxchg64_relaxed
  # define cmpxchg64_relaxed cmpxchg64
  #endif
  
  /*
bc08b449e   Linus Torvalds   lockref: implemen...
14
15
16
17
18
19
20
21
22
23
   * Note that the "cmpxchg()" reloads the "old" value for the
   * failure case.
   */
  #define CMPXCHG_LOOP(CODE, SUCCESS) do {					\
  	struct lockref old;							\
  	BUILD_BUG_ON(sizeof(old) != 8);						\
  	old.lock_count = ACCESS_ONCE(lockref->lock_count);			\
  	while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) {  	\
  		struct lockref new = old, prev = old;				\
  		CODE								\
d2212b4dc   Will Deacon   lockref: allow re...
24
25
26
  		old.lock_count = cmpxchg64_relaxed(&lockref->lock_count,	\
  						   old.lock_count,		\
  						   new.lock_count);		\
bc08b449e   Linus Torvalds   lockref: implemen...
27
28
29
  		if (likely(old.lock_count == prev.lock_count)) {		\
  			SUCCESS;						\
  		}								\
3a6bfbc91   Davidlohr Bueso   arch, locking: Ci...
30
  		cpu_relax_lowlatency();						\
bc08b449e   Linus Torvalds   lockref: implemen...
31
32
33
34
35
36
37
38
  	}									\
  } while (0)
  
  #else
  
  #define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
  
  #endif
2f4f12e57   Linus Torvalds   lockref: uninline...
39
40
  /**
   * lockref_get - Increments reference count unconditionally
44a0cf929   Linus Torvalds   lockref: fix docb...
41
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
42
43
44
45
46
47
   *
   * This operation is only valid if you already hold a reference
   * to the object, so you know the count cannot be zero.
   */
  void lockref_get(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
48
49
50
51
52
  	CMPXCHG_LOOP(
  		new.count++;
  	,
  		return;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
53
54
55
56
57
58
59
60
  	spin_lock(&lockref->lock);
  	lockref->count++;
  	spin_unlock(&lockref->lock);
  }
  EXPORT_SYMBOL(lockref_get);
  
  /**
   * lockref_get_not_zero - Increments count unless the count is 0
44a0cf929   Linus Torvalds   lockref: fix docb...
61
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
62
63
64
65
   * Return: 1 if count updated successfully or 0 if count was zero
   */
  int lockref_get_not_zero(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
66
67
68
69
70
71
72
73
74
  	int retval;
  
  	CMPXCHG_LOOP(
  		new.count++;
  		if (!old.count)
  			return 0;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
75
76
  
  	spin_lock(&lockref->lock);
bc08b449e   Linus Torvalds   lockref: implemen...
77
  	retval = 0;
2f4f12e57   Linus Torvalds   lockref: uninline...
78
79
80
81
82
83
84
85
86
87
88
  	if (lockref->count) {
  		lockref->count++;
  		retval = 1;
  	}
  	spin_unlock(&lockref->lock);
  	return retval;
  }
  EXPORT_SYMBOL(lockref_get_not_zero);
  
  /**
   * lockref_get_or_lock - Increments count unless the count is 0
44a0cf929   Linus Torvalds   lockref: fix docb...
89
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
90
91
92
93
94
   * Return: 1 if count updated successfully or 0 if count was zero
   * and we got the lock instead.
   */
  int lockref_get_or_lock(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
95
96
97
98
99
100
101
  	CMPXCHG_LOOP(
  		new.count++;
  		if (!old.count)
  			break;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
102
103
104
105
106
107
108
109
110
111
112
  	spin_lock(&lockref->lock);
  	if (!lockref->count)
  		return 0;
  	lockref->count++;
  	spin_unlock(&lockref->lock);
  	return 1;
  }
  EXPORT_SYMBOL(lockref_get_or_lock);
  
  /**
   * lockref_put_or_lock - decrements count unless count <= 1 before decrement
44a0cf929   Linus Torvalds   lockref: fix docb...
113
   * @lockref: pointer to lockref structure
2f4f12e57   Linus Torvalds   lockref: uninline...
114
115
116
117
   * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
   */
  int lockref_put_or_lock(struct lockref *lockref)
  {
bc08b449e   Linus Torvalds   lockref: implemen...
118
119
120
121
122
123
124
  	CMPXCHG_LOOP(
  		new.count--;
  		if (old.count <= 1)
  			break;
  	,
  		return 1;
  	);
2f4f12e57   Linus Torvalds   lockref: uninline...
125
126
127
128
129
130
131
132
  	spin_lock(&lockref->lock);
  	if (lockref->count <= 1)
  		return 0;
  	lockref->count--;
  	spin_unlock(&lockref->lock);
  	return 1;
  }
  EXPORT_SYMBOL(lockref_put_or_lock);
e7d33bb5e   Linus Torvalds   lockref: add abil...
133
134
135
136
137
138
139
140
141
142
  
  /**
   * lockref_mark_dead - mark lockref dead
   * @lockref: pointer to lockref structure
   */
  void lockref_mark_dead(struct lockref *lockref)
  {
  	assert_spin_locked(&lockref->lock);
  	lockref->count = -128;
  }
e66cf1610   Steven Whitehouse   GFS2: Use lockref...
143
  EXPORT_SYMBOL(lockref_mark_dead);
e7d33bb5e   Linus Torvalds   lockref: add abil...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  
  /**
   * lockref_get_not_dead - Increments count unless the ref is dead
   * @lockref: pointer to lockref structure
   * Return: 1 if count updated successfully or 0 if lockref was dead
   */
  int lockref_get_not_dead(struct lockref *lockref)
  {
  	int retval;
  
  	CMPXCHG_LOOP(
  		new.count++;
  		if ((int)old.count < 0)
  			return 0;
  	,
  		return 1;
  	);
  
  	spin_lock(&lockref->lock);
  	retval = 0;
  	if ((int) lockref->count >= 0) {
  		lockref->count++;
  		retval = 1;
  	}
  	spin_unlock(&lockref->lock);
  	return retval;
  }
  EXPORT_SYMBOL(lockref_get_not_dead);