diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2012-11-20 07:16:48 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-11-28 03:36:06 -0500 |
commit | a82b8db02f33b38a24fc3858968153940f7c4740 (patch) | |
tree | aaede8d6c91398b4052431809adea0c00f263937 /Documentation/kref.txt | |
parent | 384cc2f9688994dfd505011ba3b08e0a702030b0 (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.txt | 88 |
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: | |||
213 | and: | 213 | and: |
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 | |||
217 | The above example could also be optimized using kref_get_unless_zero() in | ||
218 | the following way: | ||
219 | |||
220 | static 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 | |||
233 | static 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 | |||
243 | static void put_entry(struct my_data *entry) | ||
244 | { | ||
245 | kref_put(&entry->refcount, release_entry); | ||
246 | } | ||
247 | |||
248 | Which is useful to remove the mutex lock around kref_put() in put_entry(), but | ||
249 | it's important that kref_get_unless_zero is enclosed in the same critical | ||
250 | section that finds the entry in the lookup table, | ||
251 | otherwise kref_get_unless_zero may reference already freed memory. | ||
252 | Note that it is illegal to use kref_get_unless_zero without checking its | ||
253 | return value. If you are sure (by already having a valid pointer) that | ||
254 | kref_get_unless_zero() will return true, then use kref_get() instead. | ||
255 | |||
256 | The function kref_get_unless_zero also makes it possible to use rcu | ||
257 | locking for lookups in the above example: | ||
258 | |||
259 | struct my_data | ||
260 | { | ||
261 | struct rcu_head rhead; | ||
262 | . | ||
263 | struct kref refcount; | ||
264 | . | ||
265 | . | ||
266 | }; | ||
267 | |||
268 | static 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 | |||
281 | static 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 | |||
291 | static void put_entry(struct my_data *entry) | ||
292 | { | ||
293 | kref_put(&entry->refcount, release_entry_rcu); | ||
294 | } | ||
295 | |||
296 | But note that the struct kref member needs to remain in valid memory for a | ||
297 | rcu grace period after release_entry_rcu was called. That can be accomplished | ||
298 | by using kfree_rcu(entry, rhead) as done above, or by calling synchronize_rcu() | ||
299 | before using kfree, but note that synchronize_rcu() may sleep for a | ||
300 | substantial amount of time. | ||
301 | |||
302 | |||
303 | Thomas Hellstrom <thellstrom@vmware.com> | ||