#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/slaballoc.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/vmmeter.h>
#include <sys/lock.h>
#include <sys/thread.h>
#include <sys/globaldata.h>
#include <sys/mpipe.h>
#include <sys/kthread.h>
struct mpipe_callback {
STAILQ_ENTRY(mpipe_callback) entry;
void (*func)(void *arg1, void *arg2);
void *arg1;
void *arg2;
};
static MALLOC_DEFINE(M_MPIPEARY, "MPipe Array", "Auxiliary MPIPE structure");
static void mpipe_thread(void *arg);
void
mpipe_init(malloc_pipe_t mpipe, malloc_type_t type, int bytes,
int nnom, int nmax,
int mpflags,
void (*construct)(void *, void *),
void (*deconstruct)(void *, void *),
void *priv)
{
int n;
if (nnom < 1)
nnom = 1;
if (nmax < 0)
nmax = 0x7FFF0000;
if (nmax < nnom)
nmax = nnom;
bzero(mpipe, sizeof(struct malloc_pipe));
mpipe->type = type;
mpipe->bytes = bytes;
mpipe->mpflags = mpflags;
mpipe->construct = construct;
mpipe->deconstruct = deconstruct;
mpipe->priv = priv;
if ((mpflags & MPF_NOZERO) == 0)
mpipe->mflags |= M_ZERO;
if (mpflags & MPF_INT)
mpipe->mflags |= M_USE_RESERVE | M_USE_INTERRUPT_RESERVE;
mpipe->ary_count = nnom;
mpipe->max_count = nmax;
mpipe->array = kmalloc(nnom * sizeof(mpipe->array[0]), M_MPIPEARY,
M_WAITOK | M_ZERO);
while (mpipe->free_count < nnom) {
n = mpipe->free_count;
mpipe->array[n] = kmalloc(bytes, mpipe->type, M_WAITOK | mpipe->mflags);
if (construct)
construct(mpipe->array[n], priv);
++mpipe->free_count;
++mpipe->total_count;
}
STAILQ_INIT(&mpipe->queue);
lwkt_token_init(&mpipe->token, "mpipe token");
if (mpflags & MPF_CALLBACK) {
kthread_create(mpipe_thread, mpipe, &mpipe->thread,
"mpipe_%s", type->ks_shortdesc);
}
}
void
mpipe_done(malloc_pipe_t mpipe)
{
void *buf;
int n;
KKASSERT(mpipe->free_count == mpipe->total_count);
lwkt_gettoken(&mpipe->token);
mpipe->mpflags |= MPF_EXITING;
while (mpipe->thread) {
wakeup(&mpipe->queue);
tsleep(mpipe, 0, "mpipex", 1);
}
for (n = mpipe->free_count - 1; n >= 0; --n) {
buf = mpipe->array[n];
mpipe->array[n] = NULL;
KKASSERT(buf != NULL);
if (mpipe->deconstruct)
mpipe->deconstruct(buf, mpipe->priv);
kfree(buf, mpipe->type);
}
mpipe->free_count = 0;
mpipe->total_count = 0;
if (mpipe->array) {
kfree(mpipe->array, M_MPIPEARY);
mpipe->array = NULL;
}
lwkt_reltoken(&mpipe->token);
lwkt_token_uninit(&mpipe->token);
}
static void
mpipe_thread(void *arg)
{
malloc_pipe_t mpipe = arg;
struct mpipe_callback *mcb;
lwkt_gettoken(&mpipe->token);
while ((mpipe->mpflags & MPF_EXITING) == 0) {
while (mpipe->free_count &&
(mcb = STAILQ_FIRST(&mpipe->queue)) != NULL) {
STAILQ_REMOVE(&mpipe->queue, mcb, mpipe_callback, entry);
mcb->func(mcb->arg1, mcb->arg2);
kfree(mcb, M_MPIPEARY);
}
if (STAILQ_FIRST(&mpipe->queue))
mpipe->mpflags |= MPF_QUEUEWAIT;
tsleep(&mpipe->queue, 0, "wait", 0);
}
mpipe->thread = NULL;
lwkt_reltoken(&mpipe->token);
wakeup(mpipe);
}
static __inline
void *
_mpipe_alloc_locked(malloc_pipe_t mpipe, int mfailed)
{
void *buf;
int n;
if ((n = mpipe->free_count) != 0) {
--n;
buf = mpipe->array[n];
mpipe->array[n] = NULL;
mpipe->free_count = n;
} else if (mpipe->total_count >= mpipe->max_count || mfailed) {
buf = NULL;
} else {
buf = kmalloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags);
if (buf) {
++mpipe->total_count;
if (mpipe->construct)
mpipe->construct(buf, mpipe->priv);
}
}
return(buf);
}
void *
mpipe_alloc_nowait(malloc_pipe_t mpipe)
{
void *buf;
lwkt_gettoken(&mpipe->token);
buf = _mpipe_alloc_locked(mpipe, 0);
lwkt_reltoken(&mpipe->token);
return(buf);
}
void *
mpipe_alloc_callback(malloc_pipe_t mpipe, void (*func)(void *arg1, void *arg2),
void *arg1, void *arg2)
{
struct mpipe_callback *mcb;
void *buf;
lwkt_gettoken(&mpipe->token);
buf = _mpipe_alloc_locked(mpipe, 0);
if (buf == NULL) {
mcb = kmalloc(sizeof(*mcb), M_MPIPEARY, M_INTWAIT);
buf = _mpipe_alloc_locked(mpipe, 0);
if (buf == NULL) {
mcb->func = func;
mcb->arg1 = arg1;
mcb->arg2 = arg2;
STAILQ_INSERT_TAIL(&mpipe->queue, mcb, entry);
mpipe->mpflags |= MPF_QUEUEWAIT;
} else {
kfree(mcb, M_MPIPEARY);
}
}
lwkt_reltoken(&mpipe->token);
return(buf);
}
void
mpipe_wait(malloc_pipe_t mpipe)
{
if (mpipe->free_count == 0) {
lwkt_gettoken(&mpipe->token);
while ((mpipe->mpflags & MPF_EXITING) == 0) {
if (mpipe->free_count)
break;
mpipe->mpflags |= MPF_QUEUEWAIT;
tsleep(&mpipe->queue, 0, "wait", 0);
}
lwkt_reltoken(&mpipe->token);
}
}
void *
mpipe_alloc_waitok(malloc_pipe_t mpipe)
{
void *buf;
int mfailed;
lwkt_gettoken(&mpipe->token);
mfailed = 0;
while ((buf = _mpipe_alloc_locked(mpipe, mfailed)) == NULL) {
mpipe->pending = 1;
tsleep(mpipe, 0, "mpipe1", 0);
mfailed = 1;
}
lwkt_reltoken(&mpipe->token);
return(buf);
}
void
mpipe_free(malloc_pipe_t mpipe, void *buf)
{
int n;
if (buf == NULL)
return;
lwkt_gettoken(&mpipe->token);
if ((n = mpipe->free_count) < mpipe->ary_count) {
mpipe->array[n] = buf;
++mpipe->free_count;
if ((mpipe->mpflags & (MPF_CACHEDATA|MPF_NOZERO)) == 0)
bzero(buf, mpipe->bytes);
if (mpipe->mpflags & MPF_QUEUEWAIT) {
mpipe->mpflags &= ~MPF_QUEUEWAIT;
lwkt_reltoken(&mpipe->token);
wakeup(&mpipe->queue);
} else {
lwkt_reltoken(&mpipe->token);
}
if (mpipe->pending) {
mpipe->pending = 0;
wakeup(mpipe);
}
} else {
--mpipe->total_count;
KKASSERT(mpipe->total_count >= mpipe->free_count);
if (mpipe->deconstruct)
mpipe->deconstruct(buf, mpipe->priv);
lwkt_reltoken(&mpipe->token);
kfree(buf, mpipe->type);
}
}