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>
This commit is contained in:
parent
384cc2f968
commit
a82b8db02f
1 changed files with 88 additions and 0 deletions
|
@ -213,3 +213,91 @@ presentation on krefs, which can be found at:
|
||||||
and:
|
and:
|
||||||
http://www.kroah.com/linux/talks/ols_2004_kref_talk/
|
http://www.kroah.com/linux/talks/ols_2004_kref_talk/
|
||||||
|
|
||||||
|
|
||||||
|
The above example could also be optimized using kref_get_unless_zero() in
|
||||||
|
the following way:
|
||||||
|
|
||||||
|
static struct my_data *get_entry()
|
||||||
|
{
|
||||||
|
struct my_data *entry = NULL;
|
||||||
|
mutex_lock(&mutex);
|
||||||
|
if (!list_empty(&q)) {
|
||||||
|
entry = container_of(q.next, struct my_data, link);
|
||||||
|
if (!kref_get_unless_zero(&entry->refcount))
|
||||||
|
entry = NULL;
|
||||||
|
}
|
||||||
|
mutex_unlock(&mutex);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_entry(struct kref *ref)
|
||||||
|
{
|
||||||
|
struct my_data *entry = container_of(ref, struct my_data, refcount);
|
||||||
|
|
||||||
|
mutex_lock(&mutex);
|
||||||
|
list_del(&entry->link);
|
||||||
|
mutex_unlock(&mutex);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_entry(struct my_data *entry)
|
||||||
|
{
|
||||||
|
kref_put(&entry->refcount, release_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Which is useful to remove the mutex lock around kref_put() in put_entry(), but
|
||||||
|
it's important that kref_get_unless_zero is enclosed in the same critical
|
||||||
|
section that finds the entry in the lookup table,
|
||||||
|
otherwise kref_get_unless_zero may reference already freed memory.
|
||||||
|
Note that it is illegal to use kref_get_unless_zero without checking its
|
||||||
|
return value. If you are sure (by already having a valid pointer) that
|
||||||
|
kref_get_unless_zero() will return true, then use kref_get() instead.
|
||||||
|
|
||||||
|
The function kref_get_unless_zero also makes it possible to use rcu
|
||||||
|
locking for lookups in the above example:
|
||||||
|
|
||||||
|
struct my_data
|
||||||
|
{
|
||||||
|
struct rcu_head rhead;
|
||||||
|
.
|
||||||
|
struct kref refcount;
|
||||||
|
.
|
||||||
|
.
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct my_data *get_entry_rcu()
|
||||||
|
{
|
||||||
|
struct my_data *entry = NULL;
|
||||||
|
rcu_read_lock();
|
||||||
|
if (!list_empty(&q)) {
|
||||||
|
entry = container_of(q.next, struct my_data, link);
|
||||||
|
if (!kref_get_unless_zero(&entry->refcount))
|
||||||
|
entry = NULL;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_entry_rcu(struct kref *ref)
|
||||||
|
{
|
||||||
|
struct my_data *entry = container_of(ref, struct my_data, refcount);
|
||||||
|
|
||||||
|
mutex_lock(&mutex);
|
||||||
|
list_del_rcu(&entry->link);
|
||||||
|
mutex_unlock(&mutex);
|
||||||
|
kfree_rcu(entry, rhead);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_entry(struct my_data *entry)
|
||||||
|
{
|
||||||
|
kref_put(&entry->refcount, release_entry_rcu);
|
||||||
|
}
|
||||||
|
|
||||||
|
But note that the struct kref member needs to remain in valid memory for a
|
||||||
|
rcu grace period after release_entry_rcu was called. That can be accomplished
|
||||||
|
by using kfree_rcu(entry, rhead) as done above, or by calling synchronize_rcu()
|
||||||
|
before using kfree, but note that synchronize_rcu() may sleep for a
|
||||||
|
substantial amount of time.
|
||||||
|
|
||||||
|
|
||||||
|
Thomas Hellstrom <thellstrom@vmware.com>
|
||||||
|
|
Loading…
Reference in a new issue