bond/kernel/rf/ref.c

240 lines
4.6 KiB
C

#include "rf/ref.h"
#include "ke/assert.h"
#include "ke/spin_lock.h"
#include "ke/atomic.h"
#include "ke/intr.h"
#include "clib.h"
#define K_IDENT_BASE (0x80000000ul)
static struct atree ident_tree;
static struct spin_lock ident_tree_lock;
static uint32 ident_base;
static int32
handle_compare(struct atree_node *tree_node, struct atree_node *my_node)
{
struct ident_node *tcb = OBTAIN_STRUCT_ADDR(tree_node, struct ident_node, tree_node);
struct ident_node *my_tcb = OBTAIN_STRUCT_ADDR(my_node, struct ident_node, tree_node);
if ((uintptr) tcb->ident > (uintptr) my_tcb->ident)
{
return -1;
}
else
{
if ((uintptr) tcb->ident == (uintptr) my_tcb->ident)
{
return 0;
}
else
{
return 1;
}
}
}
static struct ident_node *
search_handle_node(k_ident ident)
{
struct atree_node *result;
struct ident_node temp;
temp.ident = ident;
result = lb_atree_search(&ident_tree, &temp.tree_node);
return result == NULL ? NULL : OBTAIN_STRUCT_ADDR(result, struct ident_node, tree_node);
}
k_status
rf_ref_init(void)
{
lb_atree_init(&ident_tree, handle_compare);
ke_spin_init(&ident_tree_lock);
ident_base = K_IDENT_BASE;
return STATUS_SUCCESS;
}
k_status
rf_ref_node_init(struct ref_node *rf_node, ref_free_fp free_func)
{
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
rf_node->f_free = free_func;
rf_node->ref_count = 1;
return STATUS_SUCCESS;
}
k_status
rf_ref_obj(struct ref_node *ref_node)
{
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
if (ref_node == NULL)
{
return STATUS_INVALID_ARGS;
}
int32 old_ref_count = ke_atomic_inc_32(&ref_node->ref_count, 1);
KE_ASSERT(old_ref_count >= 1);
return STATUS_SUCCESS;
}
k_status
rf_deref_obj(struct ref_node *rf_node)
{
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
if (rf_node == NULL)
{
return STATUS_INVALID_ARGS;
}
k_status result = STATUS_SUCCESS;
int32 old_ref_count = ke_atomic_inc_32(&rf_node->ref_count, -1);
KE_ASSERT(old_ref_count >= 1);
if (old_ref_count == 1)
{
rf_node->f_free(rf_node);
}
return result;
}
k_status
rf_open_obj_by_ident(k_ident id, struct ref_node **out)
{
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
uint32 irql;
k_status status = STATUS_SUCCESS;
struct ref_node *ref = NULL;
irql = ke_raise_irql(IRQL_DPC);
ke_spin_lock(&ident_tree_lock);
struct ident_node *ident_node = search_handle_node(id);
if (ident_node == NULL)
{
status = STATUS_INVALID_ARGS;
}
else
{
ref = ident_node->obj;
}
// PREREQUISITE: Having a handle -> having a reference
// MUST GUARANTEE that handle exists while we reference
if (SX_SUCCESS(status))
{
// reference the object then return the reference
rf_ref_obj(ref);
*out = ref;
}
ke_spin_unlock(&ident_tree_lock);
ke_lower_irql(irql);
return status;
}
k_status
rf_ident_create(struct ref_node *rf_node, struct ident_node *id_node, k_ident *out)
{
k_status result;
uint32 irql;
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
result = STATUS_SUCCESS;
// TODO: CHECK OVERFLOW
id_node->ident = (k_ident) ke_atomic_inc_32((int32 *) &ident_base, 1);
id_node->obj = rf_node;
irql = ke_raise_irql(IRQL_DPC);
ke_spin_lock(&ident_tree_lock);
struct ident_node *existing_node = search_handle_node(id_node->ident);
if (existing_node == NULL)
{
lb_atree_insert(&ident_tree, &id_node->tree_node);
}
else
{
/**
* Shouldn't get to here
*/
result = STATUS_DUPLICATE;
}
ke_spin_unlock(&ident_tree_lock);
ke_lower_irql(irql);
if (SX_SUCCESS(result))
{
rf_ref_obj(rf_node);
*out = id_node->ident;
}
return result;
}
k_status
rf_ident_close(k_ident id)
{
uint32 irql;
k_status status;
struct ref_node *ref;
KE_ASSERT(ke_get_irql() <= IRQL_DPC);
status = STATUS_SUCCESS;
ref = NULL;
irql = ke_raise_irql(IRQL_DPC);
ke_spin_lock(&ident_tree_lock);
struct ident_node *id_node = search_handle_node(id);
if (id_node == NULL)
{
status = STATUS_INVALID_ARGS;
}
else
{
ref = id_node->obj;
lb_atree_delete(&ident_tree, &id_node->tree_node);
}
ke_spin_unlock(&ident_tree_lock);
ke_lower_irql(irql);
if (SX_SUCCESS(status))
{
/**
* Success means already freed
*/
id_node->free_routine(id_node);
/**
* Dereference the object
*/
rf_deref_obj(ref);
}
return status;
}