Files
Arquivotheca.AIX-4.1.3/bos/kernext/pse/str_memory.c
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

655 lines
14 KiB
C

static char sccsid[] = "@(#)23 1.18 src/bos/kernext/pse/str_memory.c, sysxpse, bos41J, 9516A_all 4/18/95 11:33:43";
/*
* COMPONENT_NAME: SYXPSE - STREAMS framework
*
* FUNCTIONS: he_alloc
* he_free
* he_realloc
* bufcall_configure
* bufcall_rsrv
* bufcall
* pse_bufcall
* unbufcall
* bufcall_init
* bufcall_term
* allocb
* freeb
* ext_freeb
* bufcall_funnel_rsrv
* bufcall_funnel_init
* bufcall_funnel_term
*
* ORIGINS: 63, 71, 83
*
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
/*
* (c) Copyright 1990, 1991, 1992 OPEN SOFTWARE FOUNDATION, INC.
* ALL RIGHTS RESERVED
*/
/*
* OSF/1 1.1
*/
/** Copyright (c) 1988 Mentat Inc.
**/
#include <sys/errno.h>
#if SEC_BASE
#include <kern/assert.h>
#endif
#include <pse/str_stream.h>
#include <pse/str_config.h>
#include <pse/str_proto.h>
#include <sys/strstat.h>
#include <net/netisr.h>
/*
* Storage allocation for everything but messages
*
* CONTEXT These routines are called only in process context
* (data structures other than messages are allocated
* only by stream head code, and that is always forced
* into process context) - thus, we may use blocking
* memory allocation services.
*
* PRIORITES are passed to us for historical reasons. This module
* used to allocate any dynamically needed memory.
*
* EXPORTED: he_alloc(size, pri)
* he_free(data)
* he_realloc(data, old_size, new_size);
*/
caddr_t
he_alloc (size, pri)
long size;
int pri;
{
register caddr_t addr;
ENTER_FUNC(he_alloc, size, pri, 0, 0, 0, 0);
NET_MALLOC(addr, caddr_t, size, M_STREAMS, M_WAITOK);
LEAVE_FUNC(he_alloc, addr);
return addr;
}
void
he_free (addr)
caddr_t addr;
{
ENTER_FUNC(he_free, addr, 0, 0, 0, 0, 0);
NET_FREE(addr, M_STREAMS);
LEAVE_FUNC(he_free, 0);
}
caddr_t
he_realloc (old_addr, old_size, new_size)
caddr_t old_addr;
long old_size;
long new_size;
{
register caddr_t new_addr;
ENTER_FUNC(he_realloc, old_addr, old_size, new_size, 0, 0, 0);
if (new_size <= old_size) {
LEAVE_FUNC(he_realloc, old_addr);
return(old_addr);
}
if ((new_addr = he_alloc(new_size, BPRI_LO)) != NULL) {
bcopy(old_addr, new_addr, old_size);
bzero(new_addr + old_size, new_size - old_size);
he_free(old_addr);
}
LEAVE_FUNC(he_realloc, new_addr);
return(new_addr);
}
/*
* Storage allocation for messages
*/
static simple_lock_data_t bufc_lock = {SIMPLE_LOCK_AVAIL};
simple_lock_data_t mblk_lock = {SIMPLE_LOCK_AVAIL};
int NSTREVENT = 20;
typedef struct bufcall_s {
struct bufcall_s * bc_next; /* Must be first */
struct bufcall_s * bc_prev;
uint bc_size;
int bc_pri;
bufcall_fcn_t bc_fcn;
bufcall_arg_t bc_arg;
int bc_id;
queue_t * bc_queue;
} BUFCALL, * BUFCALLP, ** BUFCALLPP;
static struct module_info bminfo = {
0, "bufcall", 0, INFPSZ, 2048, 128
};
static struct qinit brinit = {
NULL, bufcall_rsrv, NULL, NULL, NULL, &bminfo
};
struct streamtab bufcallinfo = { &brinit, &brinit };
int
bufcall_configure (op, indata, indatalen, outdata, outdatalen)
uint op;
str_config_t * indata;
size_t indatalen;
str_config_t * outdata;
size_t outdatalen;
{
struct streamadm sa;
dev_t devno;
int error;
extern int bufcall_init();
extern void bufcall_term();
extern void ext_freeb();
ENTER_FUNC(bufcall_configure, op, outdata, outdatalen, 0, 0, 0);
error = 0;
sa.sa_flags = STR_IS_MODULE | STR_IS_MPSAFE | STR_NOTTOSPEC;
sa.sa_ttys = nil(caddr_t);
sa.sa_sync_level = SQLVL_MODULE;
sa.sa_sync_info = nil(caddr_t);
strcpy(sa.sa_name, "bufcall");
switch(op) {
case CFG_INIT:
if ((devno = strmod_add(NODEV, &bufcallinfo, &sa)) == NODEV) {
LEAVE_FUNC(bufcall_configure, ENODEV);
return ENODEV;
}
(void)bufcall_init();
netisr_add(NETISR_MBLK, ext_freeb,
(struct ifqueue *)0, (struct domain *)0);
break;
case CFG_TERM:
error = strmod_del(devno, &bufcallinfo, &sa);
if (error) {
LEAVE_FUNC(bufcall_configure, error);
return(error);
}
bufcall_term ();
break;
default:
LEAVE_FUNC(bufcall_configure, EINVAL);
return EINVAL;
}
if (outdata != NULL && outdatalen == sizeof(str_config_t)) {
outdata->sc_devnum = NODEV;
outdata->sc_sa_flags = sa.sa_flags;
strcpy(outdata->sc_sa_name, sa.sa_name);
}
LEAVE_FUNC(bufcall_configure, 0);
return 0;
}
staticf struct { BUFCALLP next, prev; } bc_q;
staticf BUFCALLP bc_ref, bc_free;
staticf queue_t * bufcallq;
staticf int bufcall_needed = 0;
staticf int bufcall_happened = 0;
int
bufcall_rsrv (q)
queue_t * q;
{
reg BUFCALLP bc, nextbc;
int threshold;
DISABLE_LOCK_DECL
ENTER_FUNC(bufcall_rsrv, q, 0, 0, 0, 0, 0);
DISABLE_LOCK(&bufc_lock);
again:
bufcall_happened = 0;
threshold = (STRMSGSZ > 0) ? STRMSGSZ+1 : INT_MAX;
for ( nextbc = bc_q.next; nextbc != (BUFCALLP)&bc_q; ) {
bc = nextbc;
nextbc = nextbc->bc_next;
if (bc->bc_size >= threshold)
continue;
if (testb(bc->bc_size, bc->bc_pri) == 0) {
threshold = bc->bc_size;
continue;
}
remque(bc);
DISABLE_UNLOCK(&bufc_lock);
(void) csq_protect( bc->bc_queue,
nil(queue_t *),
(csq_protect_fcn_t)bc->bc_fcn,
(csq_protect_arg_t)bc->bc_arg,
q->q_runq_sq, TRUE);
DISABLE_LOCK(&bufc_lock);
bc->bc_next = bc_free;
bc_free = bc;
/*
* If bufcall() was called while we didn't
* hold the bufc_lock, we have to restart
* the loop.
*/
if ( bufcall_happened )
goto again;
nextbc = bc_q.next;
}
if (bc_q.next == (BUFCALLP)&bc_q)
bufcall_needed = 0;
DISABLE_UNLOCK(&bufc_lock);
LEAVE_FUNC(bufcall_rsrv, 0);
return 0;
}
int
(bufcall)(s, p, f, a)
uint s; int p; int (*f)(); long a;
{
ENTER_FUNC(bufcall, s, p, f, a, 0, 0);
LEAVE_FUNC(bufcall, 0);
return bufcall(s, p, (bufcall_fcn_t)f, (bufcall_arg_t)a);
}
/** Recover from failure of allocb */
int
pse_bufcall (size, pri, func, arg)
uint size;
int pri;
bufcall_fcn_t func;
bufcall_arg_t arg;
{
static int bufcall_id;
reg BUFCALLP bc;
int timeid = 0;
DISABLE_LOCK_DECL
ENTER_FUNC(bufcall, size, pri, func, arg, 0, 0);
DISABLE_LOCK(&bufc_lock);
if (!(bc = bc_free)) {
DISABLE_UNLOCK(&bufc_lock);
STR_DEBUG(printf("WARNING: bufcall: could not allocate stream event\n"));
LEAVE_FUNC(bufcall, 0);
return 0;
}
bc_free = bc->bc_next;
bc->bc_size = size;
bc->bc_pri = pri;
bc->bc_fcn = func;
bc->bc_arg = arg;
if ((bc->bc_id = ++bufcall_id) == 0)
bc->bc_id = ++bufcall_id;
if (func != (bufcall_fcn_t)qenable &&
func != (bufcall_fcn_t)osr_bufcall_wakeup)
bc->bc_queue = csq_which_q();
else
bc->bc_queue = nil(queue_t *);
insque(bc, bc_q.prev);
if (bufcall_needed++ == 0)
timeid = str_timeout_trb(qenable, bufcallq, hz);
bufcall_happened = 1;
DISABLE_UNLOCK(&bufc_lock);
LEAVE_FUNC(bufcall, 1);
return bc->bc_id;
}
void
unbufcall (id)
int id;
{
reg BUFCALLP bc;
DISABLE_LOCK_DECL
ENTER_FUNC(unbufcall, id, 0, 0, 0, 0, 0);
DISABLE_LOCK(&bufc_lock);
for (bc = bc_q.next; bc != (BUFCALLP)&bc_q; bc = bc->bc_next) {
if (bc->bc_id == id) {
remque(bc);
bc->bc_next = bc_free;
bc_free = bc;
break;
}
}
DISABLE_UNLOCK(&bufc_lock);
LEAVE_FUNC(unbufcall, 0);
}
/* Used to initialize mblks in allocb. */
static MH mh_template;
/* "Attached" mblks held here for safe freeing */
MHP mh_freelater;
MHP mh_cache_128 = NULL;
int mh_cache_num_128 = 0;
MHP mh_cache_256 = NULL;
int mh_cache_num_256 = 0;
#define MAX_CACHE_SIZE 128
int
bufcall_init ()
{
reg BUFCALL * bc;
ENTER_FUNC(bufcall_init, 0, 0, 0, 0, 0, 0);
bc_q.next = bc_q.prev = (BUFCALLP)&bc_q;
lock_alloc((&bufc_lock), LOCK_ALLOC_PIN, PSE_BUFC_LOCK, -1);
simple_lock_init(&bufc_lock);
lock_alloc((&mblk_lock), LOCK_ALLOC_PIN, PSE_MBLK_LOCK, -1);
simple_lock_init(&mblk_lock);
mh_template.mh_dblk.db_ref = 1;
mh_template.mh_dblk.db_type = M_DATA;
/* Others are 0 or initialized on demand */
NET_MALLOC(bc, BUFCALL *, NSTREVENT * sizeof *bc, M_STREAMS, M_WAITOK);
if (bufcallq = q_alloc()) {
sth_set_queue( bufcallq,
bufcallinfo.st_rdinit,
bufcallinfo.st_wrinit);
bc_ref = bc;
for (bc_free = bc; bc < &bc_free[NSTREVENT-1]; bc++)
bc->bc_next = &bc[1];
bc->bc_next = nilp(BUFCALL);
LEAVE_FUNC(bufcall_init, 0);
return ;
}
NET_FREE(bc, M_STREAMS);
panic("bufcall_init");
LEAVE_FUNC(bufcall_init, 0);
}
void
bufcall_term ()
{
lock_free(&bufc_lock);
lock_free(&mblk_lock);
NET_FREE(bc_ref, M_STREAMS);
}
/*
* allocb - allocate a message block.
*/
MBLKP
allocb (size, pri)
reg int size;
uint pri;
{
reg MHP mh;
uchar * mem;
extern int thewall, allocated, throttle;
DISABLE_LOCK_DECL
ENTER_FUNC(allocb, size, pri, 0, 0, 0, 0);
if (size < 0 || (STRMSGSZ > 0 && size > STRMSGSZ))
goto bad;
pri = (pri == BPRI_WAITOK) ? M_WAITOK : M_NOWAIT;
if (size <= 128 - sizeof (MH)) {
DISABLE_LOCK(&mblk_lock);
if (mh_cache_128) {
mh = mh_cache_128;
mh_cache_128 = (MHP)mh_cache_128->mh_mblk.b_next;
mh_cache_num_128--;
DISABLE_UNLOCK(&mblk_lock);
} else {
DISABLE_UNLOCK(&mblk_lock);
if (allocated > throttle) goto bad;
NET_MALLOC(mh, MHP, 128, M_MBLK, pri);
}
mem = (uchar *)(mh + 1);
if (size)
size = 128 - sizeof (MH);
} else if (size <= 256 - sizeof (MH)) {
DISABLE_LOCK(&mblk_lock);
if (mh_cache_256) {
mh = mh_cache_256;
mh_cache_256 = (MHP)mh_cache_256->mh_mblk.b_next;
mh_cache_num_256--;
DISABLE_UNLOCK(&mblk_lock);
} else {
DISABLE_UNLOCK(&mblk_lock);
if (allocated > throttle) goto bad;
NET_MALLOC(mh, MHP, 256, M_MBLK, pri);
}
mem = (uchar *)(mh + 1);
if (size)
size = 256 - sizeof (MH);
} else {
if (allocated > throttle) goto bad;
NET_MALLOC(mh, MHP, sizeof (MH), M_MBLK, pri);
if (mh) {
/*
* Round up size to net_malloc()'s quantum.
* XXX we know too much.
*/
if (size > MAXALLOCSAVE)
size = round_page(size);
else if (size & (size - 1))
size = (1 << BUCKETINDX(size));
NET_MALLOC(mem, uchar *, size, M_MBDATA, pri);
}
}
if (mh == 0 || mem == 0) {
if (mh) NET_FREE(mh, M_MBLK);
bad:
throttle = 922 * thewall; /* Update throttle in case user has changed thewall. */
LEAVE_FUNC(allocb, 0);
return nil(MBLKP);
}
*mh = mh_template;
sq_init(&mh->mh_mblk.b_sq);
mh->mh_mblk.b_datap = &mh->mh_dblk;
mh->mh_mblk.b_rptr = mh->mh_mblk.b_wptr = mh->mh_dblk.db_base = mem;
mh->mh_dblk.db_lim = &mem[size];
mh->mh_dblk.db_size = size;
mh->mh_frtn.free_func = 0;
LEAVE_FUNC(allocb, &mh->mh_mblk);
return &mh->mh_mblk;
}
/*
* freeb - free a message block
*/
void
freeb (mp)
MBLKP mp;
{
reg MHP mh;
dblk_t * db;
uchar *md1, *md2;
MH *mh1, *mh2;
DISABLE_LOCK_DECL
if (mp->b_datap == 0 || mp->b_datap->db_ref == 0)
panic("freeing free mblk");
ENTER_FUNC(freeb, mp, 0, 0, 0, 0, 0);
#if SEC_BASE
/*
* If there are security attributes associated with this buffer,
* release them first.
*/
if (mp->b_attr) {
ASSERT( !mp->b_attr->b_attr && \
!mp->b_attr->b_cont && \
!mp->b_attr->b_next);
freeb(mp->b_attr);
mp->b_attr = nil(MBLKP);
}
#endif
md1 = md2 = NULL;
mh1 = mh2 = NULL;
DISABLE_LOCK(&mblk_lock);
db = mp->b_datap;
mp->b_datap = nilp(dblk_t);
db->db_ref--;
mh = (MHP)(&((MBLKP)db)[-1]);
/*
* Following loop executed at most twice - once for the
* mp->b_datap and its associated mblk, and a second
* time if that mblk isn't mp. Set mh* and md* as we go
* in order to take mblk_lock and splstr only once.
*/
for (;;) {
if ( db->db_ref == 0 ) {
uchar *p = db->db_base;
db->db_base = NULL;
/*
* If free_func, defer free to memory isr thread when
* _both_ the mblk and dblk are unreferenced. (We need
* the mblk and the mh_frtn to queue it)
*/
if ( mh->mh_frtn.free_func ) {
assert(mh->mh_mblk.b_flag & MSGEXT);
if ( mh->mh_mblk.b_datap == NULL
&& mh->mh_dblk.db_base == NULL ) {
mh->mh_mblk.b_prev =(MBLKP)p;
mh->mh_mblk.b_next =(MBLKP)mh_freelater;
mh_freelater = mh;
} else /* later... */
db->db_base = p;
/*
* Else free any mbdata and unused mblk.
*/
} else {
if ( p != (uchar *)(mh + 1) )
if (md1) md2 = p; else md1 = p;
if ( mh->mh_mblk.b_datap == NULL
&& mh->mh_dblk.db_base == NULL ) {
if (mh_cache_num_128 < MAX_CACHE_SIZE
&& mh == mp
&& (mh->mh_dblk.db_size <=
128 - sizeof (MH))) {
mh->mh_mblk.b_next =(MBLKP)mh_cache_128;
mh_cache_128 = mh;
mh_cache_num_128++;
} else if (mh_cache_num_256 < MAX_CACHE_SIZE
&& mh == mp
&& ((mh->mh_dblk.db_size >
128 - sizeof (MH)) &&
(mh->mh_dblk.db_size <=
256 - sizeof (MH)))) {
mh->mh_mblk.b_next =(MBLKP)mh_cache_256;
mh_cache_256 = mh;
mh_cache_num_256++;
} else {
if (mh1) mh2 = mh; else mh1 = mh;
}
} /* if */
}
}
if ( mh == (MHP)mp )
break;
mh = (MHP)mp;
db = &mh->mh_dblk;
}
mh = mh_freelater;
DISABLE_UNLOCK(&mblk_lock);
if (mh1) NET_FREE(mh1, M_MBLK);
if (mh2) NET_FREE(mh2, M_MBLK);
if (md1) NET_FREE(md1, M_MBDATA);
if (md2) NET_FREE(md2, M_MBDATA);
if (mh) schednetisr(NETISR_MBLK);
if (bufcall_needed)
qenable(bufcallq);
LEAVE_FUNC(freeb, 0);
}
/*
* Called from NETISR_MBLK for interrupt-safe freeing.
*/
void
ext_freeb()
{
reg MHP mh, mh_next;
char * p;
DISABLE_LOCK_DECL
ENTER_FUNC(ext_freeb, 0, 0, 0, 0, 0, 0);
DISABLE_LOCK(&mblk_lock);
mh = mh_freelater;
mh_freelater = 0;
DISABLE_UNLOCK(&mblk_lock);
while (mh) {
if (p = (char *)mh->mh_mblk.b_prev)
(*mh->mh_frtn.free_func)(mh->mh_frtn.free_arg, p);
mh->mh_mblk.b_flag &= ~MSGEXT;
mh_next = (MHP)mh->mh_mblk.b_next;
NET_FREE(mh, M_MBLK);
mh = mh_next;
}
LEAVE_FUNC(ext_freeb, 0);
}
#include <pse/str_funnel.h>
void
bufcall_funnel_init()
{
DISABLE_LOCK_DECL
DISABLE_LOCK(&bufcallq->q_qlock);
bufcallq->q_runq_sq->sq_entry = funnel_sq_wrapper;
DISABLE_UNLOCK(&bufcallq->q_qlock);
DISABLE_LOCK(&WR(bufcallq)->q_qlock);
WR(bufcallq)->q_runq_sq->sq_entry = funnel_sq_wrapper;
DISABLE_UNLOCK(&WR(bufcallq)->q_qlock);
}
void
bufcall_funnel_term()
{
extern void sq_wrapper();
DISABLE_LOCK_DECL
DISABLE_LOCK(&bufcallq->q_qlock);
bufcallq->q_runq_sq->sq_entry = sq_wrapper;
DISABLE_UNLOCK(&bufcallq->q_qlock);
DISABLE_LOCK(&WR(bufcallq)->q_qlock);
WR(bufcallq)->q_runq_sq->sq_entry = sq_wrapper;
DISABLE_UNLOCK(&WR(bufcallq)->q_qlock);
}