aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/kref.txt
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2012-11-20 07:16:48 -0500
committerDave Airlie <airlied@redhat.com>2012-11-28 03:36:06 -0500
commita82b8db02f33b38a24fc3858968153940f7c4740 (patch)
treeaaede8d6c91398b4052431809adea0c00f263937 /Documentation/kref.txt
parent384cc2f9688994dfd505011ba3b08e0a702030b0 (diff)
kref: Add kref_get_unless_zero documentation
Document how kref_get_unless_zero should be used and how it helps solve a typical kref / locking problem. Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'Documentation/kref.txt')
-rw-r--r--Documentation/kref.txt88
1 files changed, 88 insertions, 0 deletions
diff --git a/Documentation/kref.txt b/Documentation/kref.txt
index 48ba715d5a63..ddf85a5dde0c 100644
--- a/Documentation/kref.txt
+++ b/Documentation/kref.txt
@@ -213,3 +213,91 @@ presentation on krefs, which can be found at:
213and: 213and:
214 http://www.kroah.com/linux/talks/ols_2004_kref_talk/ 214 http://www.kroah.com/linux/talks/ols_2004_kref_talk/
215 215
216
217The above example could also be optimized using kref_get_unless_zero() in
218the following way:
219
220static struct my_data *get_entry()
221{
222 struct my_data *entry = NULL;
223 mutex_lock(&mutex);
224 if (!list_empty(&q)) {
225 entry = container_of(q.next, struct my_data, link);
226 if (!kref_get_unless_zero(&entry->refcount))
227 entry = NULL;
228 }
229 mutex_unlock(&mutex);
230 return entry;
231}
232
233static void release_entry(struct kref *ref)
234{
235 struct my_data *entry = container_of(ref, struct my_data, refcount);
236
237 mutex_lock(&mutex);
238 list_del(&entry->link);
239 mutex_unlock(&mutex);
240 kfree(entry);
241}
242
243static void put_entry(struct my_data *entry)
244{
245 kref_put(&entry->refcount, release_entry);
246}
247
248Which is useful to remove the mutex lock around kref_put() in put_entry(), but
249it's important that kref_get_unless_zero is enclosed in the same critical
250section that finds the entry in the lookup table,
251otherwise kref_get_unless_zero may reference already freed memory.
252Note that it is illegal to use kref_get_unless_zero without checking its
253return value. If you are sure (by already having a valid pointer) that
254kref_get_unless_zero() will return true, then use kref_get() instead.
255
256The function kref_get_unless_zero also makes it possible to use rcu
257locking for lookups in the above example:
258
259struct my_data
260{
261 struct rcu_head rhead;
262 .
263 struct kref refcount;
264 .
265 .
266};
267
268static struct my_data *get_entry_rcu()
269{
270 struct my_data *entry = NULL;
271 rcu_read_lock();
272 if (!list_empty(&q)) {
273 entry = container_of(q.next, struct my_data, link);
274 if (!kref_get_unless_zero(&entry->refcount))
275 entry = NULL;
276 }
277 rcu_read_unlock();
278 return entry;
279}
280
281static void release_entry_rcu(struct kref *ref)
282{
283 struct my_data *entry = container_of(ref, struct my_data, refcount);
284
285 mutex_lock(&mutex);
286 list_del_rcu(&entry->link);
287 mutex_unlock(&mutex);
288 kfree_rcu(entry, rhead);
289}
290
291static void put_entry(struct my_data *entry)
292{
293 kref_put(&entry->refcount, release_entry_rcu);
294}
295
296But note that the struct kref member needs to remain in valid memory for a
297rcu grace period after release_entry_rcu was called. That can be accomplished
298by using kfree_rcu(entry, rhead) as done above, or by calling synchronize_rcu()
299before using kfree, but note that synchronize_rcu() may sleep for a
300substantial amount of time.
301
302
303Thomas Hellstrom <thellstrom@vmware.com>