blob: 7888b7f12806b467dd76c075987253ddd7e21a03 [file] [log] [blame]
/* Copyright (c) 2010 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Kernel reference counting, based on Linux's kref:
* - http://www.kroah.com/linux/talks/ols_2004_kref_paper/
* Reprint-Kroah-Hartman-OLS2004.pdf
* - http://lwn.net/Articles/336224/
* - Linux's Documentation/kref.txt
*
* See our Documentation/kref.txt for more info. */
#pragma once
#include <atomic.h>
#include <assert.h>
/* Current versions of Linux pass in 'release' on the kref_put() callsite to
* save on space in whatever struct these are embedded in. We don't, since it's
* a little more complicated and we will probably change the release function a
* lot for subsystems in development. */
struct kref {
atomic_t refcount;
void (*release)(struct kref *kref);
};
/* Helper for some debugging situations */
static long kref_refcnt(struct kref *kref)
{
return atomic_read(&kref->refcount);
}
static void kref_init(struct kref *kref, void (*release)(struct kref *kref),
unsigned int init)
{
assert(release);
atomic_init(&kref->refcount, init);
kref->release = release;
}
/* Will blindly incref */
static struct kref *__kref_get(struct kref *kref, unsigned int inc)
{
atomic_add(&kref->refcount, inc);
return kref;
}
/* Returns the kref ptr on success, 0 on failure */
static struct kref *kref_get_not_zero(struct kref *kref, unsigned int inc)
{
if (atomic_add_not_zero(&kref->refcount, inc))
return kref;
else
return 0;
}
/* Will panic on zero */
static struct kref *kref_get(struct kref *kref, unsigned int inc)
{
kref = kref_get_not_zero(kref, inc);
assert(kref);
return kref;
}
/* Returns True if we hit 0 and executed 'release', False otherwise */
static bool kref_put(struct kref *kref)
{
assert(kref_refcnt(kref) > 0); /* catch some bugs */
if (atomic_sub_and_test(&kref->refcount, 1)) {
kref->release(kref);
return TRUE;
}
return FALSE;
}
/* Dev / debugging function to catch the attempted freeing of objects we don't
* know how to free yet. */
static void fake_release(struct kref *kref)
{
panic("Cleaning up this object is not supported!\n");
}