#include <sys/types.h>
#include <sys/ksynch.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
#include <sys/debug.h>
struct available {
uint32_t nodeid;
uint32_t count;
struct available *next;
struct available *prev;
};
#define OUR_NODEID_MIN ((uint32_t)1)
#define OUR_NODEID_MAX ((uint32_t)0x10000000)
#define OUR_NODEID_COUNT ((uint32_t)(OUR_NODEID_MAX - OUR_NODEID_MIN))
static struct available seed = {
OUR_NODEID_MIN, OUR_NODEID_COUNT, NULL, NULL
};
static struct available *nhead;
static kmutex_t nodeid_lock;
static struct available *
np_alloc(int kmflag)
{
return (kmem_zalloc(sizeof (struct available), kmflag));
}
static void
np_free(struct available *np)
{
kmem_free(np, sizeof (struct available));
}
static void
np_unlink(struct available *np)
{
if (np->prev)
np->prev->next = np->next;
else
nhead = np->next;
if (np->next)
np->next->prev = np->prev;
}
static void
np_insert(struct available *fp, struct available *np)
{
fp->prev = np->prev;
fp->next = np;
if (np->prev)
np->prev->next = fp;
else
nhead = fp;
np->prev = fp;
}
static void
np_add(struct available *fp)
{
struct available *np;
if (nhead == NULL) {
nhead = fp;
return;
}
for (np = nhead; np->next != NULL; np = np->next)
;
np->next = fp;
fp->prev = np;
}
static void
np_coalesce(struct available *np)
{
struct available *xp;
xp = np->next;
if (xp == NULL)
return;
if ((np->nodeid + np->count) == xp->nodeid) {
np->count += xp->count;
np_unlink(xp);
np_free(xp);
}
}
void
impl_ddi_init_nodeid(void)
{
struct available *np;
mutex_init(&nodeid_lock, NULL, MUTEX_DEFAULT, NULL);
np = np_alloc(KM_SLEEP);
*np = seed;
nhead = np;
}
int
impl_ddi_alloc_nodeid(int *nodeid)
{
struct available *np;
int x;
int unlinked = 0;
mutex_enter(&nodeid_lock);
if (nhead == NULL) {
mutex_exit(&nodeid_lock);
*nodeid = 0;
return (DDI_FAILURE);
}
np = nhead;
x = (int)((unsigned int)np->nodeid);
++np->nodeid;
--np->count;
if (np->count == 0) {
np_unlink(np);
unlinked = 1;
}
mutex_exit(&nodeid_lock);
if (unlinked)
np_free(np);
ASSERT(x != 0);
ASSERT(x != DEVI_PSEUDO_NODEID);
ASSERT(x != DEVI_SID_NODEID);
*nodeid = x;
return (DDI_SUCCESS);
}
void
impl_ddi_free_nodeid(int n)
{
uint32_t nodeid = (uint32_t)n;
struct available *np, *fp;
ASSERT(n != 0);
ASSERT(n != DEVI_PSEUDO_NODEID);
ASSERT(n != DEVI_SID_NODEID);
fp = np_alloc(KM_SLEEP);
mutex_enter(&nodeid_lock);
for (np = nhead; np != NULL; np = np->next) {
if ((nodeid + 1) == np->nodeid) {
np->nodeid = nodeid;
++np->count;
mutex_exit(&nodeid_lock);
np_free(fp);
return;
}
if (nodeid == (np->nodeid + np->count)) {
++np->count;
np_coalesce(np);
mutex_exit(&nodeid_lock);
np_free(fp);
return;
}
if (nodeid < np->nodeid) {
fp->nodeid = nodeid;
fp->count = 1;
np_insert(fp, np);
mutex_exit(&nodeid_lock);
return;
}
if (nodeid < (np->nodeid + np->count))
cmn_err(CE_PANIC, "impl_ddi_free_nodeid: "
"nodeid %x already free", n);
}
fp->nodeid = nodeid;
fp->count = 1;
np_add(fp);
mutex_exit(&nodeid_lock);
}
int
impl_ddi_take_nodeid(int n, int kmflag)
{
uint32_t nodeid = (uint32_t)n;
struct available *np, *fp;
int unlinked = 0;
ASSERT(n != 0);
ASSERT(n != DEVI_PSEUDO_NODEID);
ASSERT(n != DEVI_SID_NODEID);
if ((nodeid < OUR_NODEID_MIN) || (nodeid > OUR_NODEID_MAX))
return (0);
fp = np_alloc(kmflag);
mutex_enter(&nodeid_lock);
for (np = nhead; np != NULL; np = np->next) {
if (nodeid < np->nodeid)
break;
if ((nodeid) == np->nodeid) {
++np->nodeid;
--np->count;
if (np->count == 0) {
np_unlink(np);
++unlinked;
}
mutex_exit(&nodeid_lock);
if (fp)
np_free(fp);
if (unlinked)
np_free(np);
return (0);
}
if (nodeid == (np->nodeid + np->count - 1)) {
--np->count;
ASSERT(np->count != 0);
mutex_exit(&nodeid_lock);
if (fp)
np_free(fp);
return (0);
}
if (nodeid < (np->nodeid + np->count - 1)) {
if (fp == NULL) {
mutex_exit(&nodeid_lock);
return (-1);
}
fp->nodeid = np->nodeid;
fp->count = nodeid - np->nodeid;
np->nodeid = nodeid + 1;
np->count = np->count - fp->count - 1;
ASSERT((fp->count != 0) && (np->count != 0));
ASSERT(np->nodeid == (fp->nodeid + fp->count + 1));
np_insert(fp, np);
mutex_exit(&nodeid_lock);
return (0);
}
}
mutex_exit(&nodeid_lock);
if (fp)
np_free(fp);
cmn_err(CE_CONT, "?impl_ddi_take_nodeid: nodeid %x may not "
"be unique\n", nodeid);
return (0);
}