#include <sys/types.h>
#include <sys/param.h>
#include <sys/thread.h>
#include <sys/sysmacros.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/atomic.h>
#include <sys/errno.h>
#include <sys/vtrace.h>
#include <sys/ftrace.h>
#include <sys/ontrap.h>
#include <sys/sdt.h>
#include <sys/strft.h>
#ifdef DEBUG
#include <sys/kmem_impl.h>
#endif
#define DBLK_MAX_CACHE 73728
#define DBLK_CACHE_ALIGN 64
#define DBLK_MIN_SIZE 8
#define DBLK_SIZE_SHIFT 3
#ifdef _BIG_ENDIAN
#define DBLK_RTFU_SHIFT(field) \
(8 * (&((dblk_t *)0)->db_struioflag - &((dblk_t *)0)->field))
#else
#define DBLK_RTFU_SHIFT(field) \
(8 * (&((dblk_t *)0)->field - &((dblk_t *)0)->db_ref))
#endif
#define DBLK_RTFU(ref, type, flags, uioflag) \
(((ref) << DBLK_RTFU_SHIFT(db_ref)) | \
((type) << DBLK_RTFU_SHIFT(db_type)) | \
(((flags) | (ref - 1)) << DBLK_RTFU_SHIFT(db_flags)) | \
((uioflag) << DBLK_RTFU_SHIFT(db_struioflag)))
#define DBLK_RTFU_REF_MASK (DBLK_REFMAX << DBLK_RTFU_SHIFT(db_ref))
#define DBLK_RTFU_WORD(dbp) (*((uint32_t *)&(dbp)->db_ref))
#define MBLK_BAND_FLAG_WORD(mp) (*((uint32_t *)&(mp)->b_band))
static size_t dblk_sizes[] = {
#ifdef _LP64
16, 80, 144, 208, 272, 336, 528, 1040, 1488, 1936, 2576, 3856,
8192, 12048, 16384, 20240, 24576, 28432, 32768, 36624,
40960, 44816, 49152, 53008, 57344, 61200, 65536, 69392,
#else
64, 128, 320, 576, 1088, 1536, 1984, 2624, 3904,
8192, 12096, 16384, 20288, 24576, 28480, 32768, 36672,
40960, 44864, 49152, 53056, 57344, 61248, 65536, 69440,
#endif
DBLK_MAX_CACHE, 0
};
static struct kmem_cache *dblk_cache[DBLK_MAX_CACHE / DBLK_MIN_SIZE];
static struct kmem_cache *mblk_cache;
static struct kmem_cache *dblk_esb_cache;
static struct kmem_cache *fthdr_cache;
static struct kmem_cache *ftblk_cache;
static void dblk_lastfree(mblk_t *mp, dblk_t *dbp);
static mblk_t *allocb_oversize(size_t size, int flags);
static int allocb_tryhard_fails;
static void frnop_func(void *arg);
frtn_t frnop = { frnop_func };
static void bcache_dblk_lastfree(mblk_t *mp, dblk_t *dbp);
static boolean_t rwnext_enter(queue_t *qp);
static void rwnext_exit(queue_t *qp);
int dblk_kmem_flags = 0;
int mblk_kmem_flags = 0;
static int
dblk_constructor(void *buf, void *cdrarg, int kmflags)
{
dblk_t *dbp = buf;
ssize_t msg_size = (ssize_t)cdrarg;
size_t index;
ASSERT(msg_size != 0);
index = (msg_size - 1) >> DBLK_SIZE_SHIFT;
ASSERT(index < (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT));
if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
return (-1);
if ((msg_size & PAGEOFFSET) == 0) {
dbp->db_base = kmem_alloc(msg_size, kmflags);
if (dbp->db_base == NULL) {
kmem_cache_free(mblk_cache, dbp->db_mblk);
return (-1);
}
} else {
dbp->db_base = (unsigned char *)&dbp[1];
}
dbp->db_mblk->b_datap = dbp;
dbp->db_cache = dblk_cache[index];
dbp->db_lim = dbp->db_base + msg_size;
dbp->db_free = dbp->db_lastfree = dblk_lastfree;
dbp->db_frtnp = NULL;
dbp->db_fthdr = NULL;
dbp->db_credp = NULL;
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
return (0);
}
static int
dblk_esb_constructor(void *buf, void *cdrarg, int kmflags)
{
dblk_t *dbp = buf;
if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
return (-1);
dbp->db_mblk->b_datap = dbp;
dbp->db_cache = dblk_esb_cache;
dbp->db_fthdr = NULL;
dbp->db_credp = NULL;
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
return (0);
}
static int
bcache_dblk_constructor(void *buf, void *cdrarg, int kmflags)
{
dblk_t *dbp = buf;
bcache_t *bcp = cdrarg;
if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
return (-1);
dbp->db_base = kmem_cache_alloc(bcp->buffer_cache, kmflags);
if (dbp->db_base == NULL) {
kmem_cache_free(mblk_cache, dbp->db_mblk);
return (-1);
}
dbp->db_mblk->b_datap = dbp;
dbp->db_cache = (void *)bcp;
dbp->db_lim = dbp->db_base + bcp->size;
dbp->db_free = dbp->db_lastfree = bcache_dblk_lastfree;
dbp->db_frtnp = NULL;
dbp->db_fthdr = NULL;
dbp->db_credp = NULL;
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
return (0);
}
static void
dblk_destructor(void *buf, void *cdrarg)
{
dblk_t *dbp = buf;
ssize_t msg_size = (ssize_t)cdrarg;
ASSERT(dbp->db_mblk->b_datap == dbp);
ASSERT(msg_size != 0);
ASSERT(dbp->db_struioflag == 0);
ASSERT(dbp->db_struioun.cksum.flags == 0);
if ((msg_size & PAGEOFFSET) == 0) {
kmem_free(dbp->db_base, msg_size);
}
kmem_cache_free(mblk_cache, dbp->db_mblk);
}
static void
bcache_dblk_destructor(void *buf, void *cdrarg)
{
dblk_t *dbp = buf;
bcache_t *bcp = cdrarg;
kmem_cache_free(bcp->buffer_cache, dbp->db_base);
ASSERT(dbp->db_mblk->b_datap == dbp);
ASSERT(dbp->db_struioflag == 0);
ASSERT(dbp->db_struioun.cksum.flags == 0);
kmem_cache_free(mblk_cache, dbp->db_mblk);
}
static int
ftblk_constructor(void *buf, void *cdrarg, int kmflags)
{
ftblk_t *fbp = buf;
int i;
bzero(fbp, sizeof (ftblk_t));
if (str_ftstack != 0) {
for (i = 0; i < FTBLK_EVNTS; i++)
fbp->ev[i].stk = kmem_alloc(sizeof (ftstk_t), kmflags);
}
return (0);
}
static void
ftblk_destructor(void *buf, void *cdrarg)
{
ftblk_t *fbp = buf;
int i;
if (str_ftstack != 0) {
for (i = 0; i < FTBLK_EVNTS; i++) {
if (fbp->ev[i].stk != NULL) {
kmem_free(fbp->ev[i].stk, sizeof (ftstk_t));
fbp->ev[i].stk = NULL;
}
}
}
}
static int
fthdr_constructor(void *buf, void *cdrarg, int kmflags)
{
fthdr_t *fhp = buf;
return (ftblk_constructor(&fhp->first, cdrarg, kmflags));
}
static void
fthdr_destructor(void *buf, void *cdrarg)
{
fthdr_t *fhp = buf;
ftblk_destructor(&fhp->first, cdrarg);
}
void
streams_msg_init(void)
{
char name[40];
size_t size;
size_t lastsize = DBLK_MIN_SIZE;
size_t *sizep;
struct kmem_cache *cp;
size_t tot_size;
int offset;
mblk_cache = kmem_cache_create("streams_mblk", sizeof (mblk_t), 32,
NULL, NULL, NULL, NULL, NULL, mblk_kmem_flags);
for (sizep = dblk_sizes; (size = *sizep) != 0; sizep++) {
if ((offset = (size & PAGEOFFSET)) != 0) {
tot_size = size + sizeof (dblk_t);
ASSERT((offset + sizeof (dblk_t) + sizeof (kmem_slab_t))
< PAGESIZE);
ASSERT((tot_size & (DBLK_CACHE_ALIGN - 1)) == 0);
} else {
ASSERT((size & (DBLK_CACHE_ALIGN - 1)) == 0);
tot_size = sizeof (dblk_t);
}
(void) sprintf(name, "streams_dblk_%ld", size);
cp = kmem_cache_create(name, tot_size, DBLK_CACHE_ALIGN,
dblk_constructor, dblk_destructor, NULL, (void *)(size),
NULL, dblk_kmem_flags);
while (lastsize <= size) {
dblk_cache[(lastsize - 1) >> DBLK_SIZE_SHIFT] = cp;
lastsize += DBLK_MIN_SIZE;
}
}
dblk_esb_cache = kmem_cache_create("streams_dblk_esb", sizeof (dblk_t),
DBLK_CACHE_ALIGN, dblk_esb_constructor, dblk_destructor, NULL,
(void *)sizeof (dblk_t), NULL, dblk_kmem_flags);
fthdr_cache = kmem_cache_create("streams_fthdr", sizeof (fthdr_t), 32,
fthdr_constructor, fthdr_destructor, NULL, NULL, NULL, 0);
ftblk_cache = kmem_cache_create("streams_ftblk", sizeof (ftblk_t), 32,
ftblk_constructor, ftblk_destructor, NULL, NULL, NULL, 0);
esballoc_queue_init();
}
mblk_t *
allocb(size_t size, uint_t pri)
{
dblk_t *dbp;
mblk_t *mp;
size_t index;
index = (size - 1) >> DBLK_SIZE_SHIFT;
if (index >= (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT)) {
if (size != 0) {
mp = allocb_oversize(size, KM_NOSLEEP);
goto out;
}
index = 0;
}
if ((dbp = kmem_cache_alloc(dblk_cache[index], KM_NOSLEEP)) == NULL) {
mp = NULL;
goto out;
}
mp = dbp->db_mblk;
DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
mp->b_next = mp->b_prev = mp->b_cont = NULL;
mp->b_rptr = mp->b_wptr = dbp->db_base;
mp->b_queue = NULL;
MBLK_BAND_FLAG_WORD(mp) = 0;
STR_FTALLOC(&dbp->db_fthdr, FTEV_ALLOCB, size);
out:
FTRACE_1("allocb(): mp=0x%p", (uintptr_t)mp);
return (mp);
}
mblk_t *
allocb_tmpl(size_t size, const mblk_t *tmpl)
{
mblk_t *mp = allocb(size, 0);
if (mp != NULL) {
dblk_t *src = tmpl->b_datap;
dblk_t *dst = mp->b_datap;
cred_t *cr;
pid_t cpid;
cr = msg_getcred(tmpl, &cpid);
if (cr != NULL)
crhold(dst->db_credp = cr);
dst->db_cpid = cpid;
dst->db_type = src->db_type;
}
return (mp);
}
mblk_t *
allocb_cred(size_t size, cred_t *cr, pid_t cpid)
{
mblk_t *mp = allocb(size, 0);
ASSERT(cr != NULL);
if (mp != NULL) {
dblk_t *dbp = mp->b_datap;
crhold(dbp->db_credp = cr);
dbp->db_cpid = cpid;
}
return (mp);
}
mblk_t *
allocb_cred_wait(size_t size, uint_t flags, int *error, cred_t *cr, pid_t cpid)
{
mblk_t *mp = allocb_wait(size, 0, flags, error);
ASSERT(cr != NULL);
if (mp != NULL) {
dblk_t *dbp = mp->b_datap;
crhold(dbp->db_credp = cr);
dbp->db_cpid = cpid;
}
return (mp);
}
cred_t *
msg_getcred(const mblk_t *mp, pid_t *cpidp)
{
cred_t *cr = NULL;
cred_t *cr2;
mblk_t *mp2;
while (mp != NULL) {
dblk_t *dbp = mp->b_datap;
cr = dbp->db_credp;
if (cr == NULL) {
mp = mp->b_cont;
continue;
}
if (cpidp != NULL)
*cpidp = dbp->db_cpid;
#ifdef DEBUG
mp2 = mp->b_cont;
while (mp2 != NULL) {
cr2 = DB_CRED(mp2);
if (cr2 != NULL) {
DTRACE_PROBE2(msg__getcred,
cred_t *, cr, cred_t *, cr2);
ASSERT(crgetzoneid(cr) == crgetzoneid(cr2) ||
crgetzone(cr) == NULL ||
crgetzone(cr2) == NULL);
}
mp2 = mp2->b_cont;
}
#endif
return (cr);
}
if (cpidp != NULL)
*cpidp = NOPID;
return (NULL);
}
cred_t *
msg_extractcred(mblk_t *mp, pid_t *cpidp)
{
cred_t *cr = NULL;
cred_t *cr2;
mblk_t *mp2;
while (mp != NULL) {
dblk_t *dbp = mp->b_datap;
cr = dbp->db_credp;
if (cr == NULL) {
mp = mp->b_cont;
continue;
}
ASSERT(dbp->db_ref == 1);
dbp->db_credp = NULL;
if (cpidp != NULL)
*cpidp = dbp->db_cpid;
#ifdef DEBUG
mp2 = mp->b_cont;
while (mp2 != NULL) {
cr2 = DB_CRED(mp2);
if (cr2 != NULL) {
DTRACE_PROBE2(msg__extractcred,
cred_t *, cr, cred_t *, cr2);
ASSERT(crgetzoneid(cr) == crgetzoneid(cr2) ||
crgetzone(cr) == NULL ||
crgetzone(cr2) == NULL);
}
mp2 = mp2->b_cont;
}
#endif
return (cr);
}
return (NULL);
}
extern struct ts_label_s *
msg_getlabel(const mblk_t *mp)
{
cred_t *cr = msg_getcred(mp, NULL);
if (cr == NULL)
return (NULL);
return (crgetlabel(cr));
}
void
freeb(mblk_t *mp)
{
dblk_t *dbp = mp->b_datap;
ASSERT(dbp->db_ref > 0);
ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
FTRACE_1("freeb(): mp=0x%lx", (uintptr_t)mp);
STR_FTEVENT_MBLK(mp, caller(), FTEV_FREEB, dbp->db_ref);
dbp->db_free(mp, dbp);
}
void
freemsg(mblk_t *mp)
{
FTRACE_1("freemsg(): mp=0x%lx", (uintptr_t)mp);
while (mp) {
dblk_t *dbp = mp->b_datap;
mblk_t *mp_cont = mp->b_cont;
ASSERT(dbp->db_ref > 0);
ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
STR_FTEVENT_MBLK(mp, caller(), FTEV_FREEB, dbp->db_ref);
dbp->db_free(mp, dbp);
mp = mp_cont;
}
}
mblk_t *
reallocb(mblk_t *mp, size_t size, uint_t copy)
{
mblk_t *mp1;
unsigned char *old_rptr;
ptrdiff_t cur_size;
if (mp == NULL)
return (allocb(size, BPRI_HI));
cur_size = mp->b_wptr - mp->b_rptr;
old_rptr = mp->b_rptr;
ASSERT(mp->b_datap->db_ref != 0);
if (mp->b_datap->db_ref == 1 && MBLKSIZE(mp) >= size) {
if (copy && mp->b_datap->db_lim - mp->b_rptr >= size)
return (mp);
mp->b_wptr = mp->b_rptr = mp->b_datap->db_base;
mp1 = mp;
} else if ((mp1 = allocb_tmpl(size, mp)) != NULL) {
mp1->b_cont = mp->b_cont;
} else {
return (NULL);
}
if (copy) {
bcopy(old_rptr, mp1->b_rptr, cur_size);
mp1->b_wptr = mp1->b_rptr + cur_size;
}
if (mp != mp1)
freeb(mp);
return (mp1);
}
static void
dblk_lastfree(mblk_t *mp, dblk_t *dbp)
{
ASSERT(dbp->db_mblk == mp);
if (dbp->db_fthdr != NULL)
str_ftfree(dbp);
if (dbp->db_credp != NULL) {
crfree(dbp->db_credp);
dbp->db_credp = NULL;
}
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
dbp->db_flags &= ~(DBLK_COOKED | DBLK_UIOA);
kmem_cache_free(dbp->db_cache, dbp);
}
static void
dblk_decref(mblk_t *mp, dblk_t *dbp)
{
if (dbp->db_ref != 1) {
uint32_t rtfu = atomic_add_32_nv(&DBLK_RTFU_WORD(dbp),
-(1 << DBLK_RTFU_SHIFT(db_ref)));
if (((rtfu >> DBLK_RTFU_SHIFT(db_ref)) & DBLK_REFMAX) !=
((rtfu >> DBLK_RTFU_SHIFT(db_flags)) & DBLK_REFMIN)) {
kmem_cache_free(mblk_cache, mp);
return;
}
}
dbp->db_mblk = mp;
dbp->db_free = dbp->db_lastfree;
dbp->db_lastfree(mp, dbp);
}
mblk_t *
dupb(mblk_t *mp)
{
dblk_t *dbp = mp->b_datap;
mblk_t *new_mp;
uint32_t oldrtfu, newrtfu;
if ((new_mp = kmem_cache_alloc(mblk_cache, KM_NOSLEEP)) == NULL)
goto out;
new_mp->b_next = new_mp->b_prev = new_mp->b_cont = NULL;
new_mp->b_rptr = mp->b_rptr;
new_mp->b_wptr = mp->b_wptr;
new_mp->b_datap = dbp;
new_mp->b_queue = NULL;
MBLK_BAND_FLAG_WORD(new_mp) = MBLK_BAND_FLAG_WORD(mp);
STR_FTEVENT_MBLK(mp, caller(), FTEV_DUPB, dbp->db_ref);
dbp->db_free = dblk_decref;
do {
ASSERT(dbp->db_ref > 0);
oldrtfu = DBLK_RTFU_WORD(dbp);
newrtfu = oldrtfu + (1 << DBLK_RTFU_SHIFT(db_ref));
if ((oldrtfu & DBLK_RTFU_REF_MASK) == DBLK_RTFU_REF_MASK) {
kmem_cache_free(mblk_cache, new_mp);
new_mp = NULL;
goto out;
}
} while (atomic_cas_32(&DBLK_RTFU_WORD(dbp), oldrtfu, newrtfu) !=
oldrtfu);
out:
FTRACE_1("dupb(): new_mp=0x%lx", (uintptr_t)new_mp);
return (new_mp);
}
static void
dblk_lastfree_desb(mblk_t *mp, dblk_t *dbp)
{
frtn_t *frp = dbp->db_frtnp;
ASSERT(dbp->db_mblk == mp);
frp->free_func(frp->free_arg);
if (dbp->db_fthdr != NULL)
str_ftfree(dbp);
if (dbp->db_credp != NULL) {
crfree(dbp->db_credp);
dbp->db_credp = NULL;
}
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
kmem_cache_free(dbp->db_cache, dbp);
}
static void
frnop_func(void *arg)
{
}
static mblk_t *
gesballoc(unsigned char *base, size_t size, uint32_t db_rtfu, frtn_t *frp,
void (*lastfree)(mblk_t *, dblk_t *), int kmflags)
{
dblk_t *dbp;
mblk_t *mp;
ASSERT(base != NULL && frp != NULL);
if ((dbp = kmem_cache_alloc(dblk_esb_cache, kmflags)) == NULL) {
mp = NULL;
goto out;
}
mp = dbp->db_mblk;
dbp->db_base = base;
dbp->db_lim = base + size;
dbp->db_free = dbp->db_lastfree = lastfree;
dbp->db_frtnp = frp;
DBLK_RTFU_WORD(dbp) = db_rtfu;
mp->b_next = mp->b_prev = mp->b_cont = NULL;
mp->b_rptr = mp->b_wptr = base;
mp->b_queue = NULL;
MBLK_BAND_FLAG_WORD(mp) = 0;
out:
FTRACE_1("gesballoc(): mp=0x%lx", (uintptr_t)mp);
return (mp);
}
mblk_t *
esballoc(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, freebs_enqueue, KM_NOSLEEP);
if (mp != NULL)
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ESBALLOC, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, freebs_enqueue, KM_NOSLEEP));
}
mblk_t *
esballoc_wait(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, freebs_enqueue, KM_SLEEP);
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ESBALLOC, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, freebs_enqueue, KM_SLEEP));
}
mblk_t *
desballoc(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, dblk_lastfree_desb, KM_NOSLEEP);
if (mp != NULL)
STR_FTALLOC(&DB_FTHDR(mp), FTEV_DESBALLOC, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(1, M_DATA, 0, 0),
frp, dblk_lastfree_desb, KM_NOSLEEP));
}
mblk_t *
esballoca(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, freebs_enqueue, KM_NOSLEEP);
if (mp != NULL)
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ESBALLOCA, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, freebs_enqueue, KM_NOSLEEP));
}
mblk_t *
esballoca_wait(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, freebs_enqueue, KM_SLEEP);
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ESBALLOCA, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, freebs_enqueue, KM_SLEEP));
}
mblk_t *
desballoca(unsigned char *base, size_t size, uint_t pri, frtn_t *frp)
{
mblk_t *mp;
if (!str_ftnever) {
mp = gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, dblk_lastfree_desb, KM_NOSLEEP);
if (mp != NULL)
STR_FTALLOC(&DB_FTHDR(mp), FTEV_DESBALLOCA, size);
return (mp);
}
return (gesballoc(base, size, DBLK_RTFU(2, M_DATA, 0, 0),
frp, dblk_lastfree_desb, KM_NOSLEEP));
}
static void
bcache_dblk_lastfree(mblk_t *mp, dblk_t *dbp)
{
bcache_t *bcp = dbp->db_cache;
ASSERT(dbp->db_mblk == mp);
if (dbp->db_fthdr != NULL)
str_ftfree(dbp);
if (dbp->db_credp != NULL) {
crfree(dbp->db_credp);
dbp->db_credp = NULL;
}
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
mutex_enter(&bcp->mutex);
kmem_cache_free(bcp->dblk_cache, dbp);
bcp->alloc--;
if (bcp->alloc == 0 && bcp->destroy != 0) {
kmem_cache_destroy(bcp->dblk_cache);
kmem_cache_destroy(bcp->buffer_cache);
mutex_exit(&bcp->mutex);
mutex_destroy(&bcp->mutex);
kmem_free(bcp, sizeof (bcache_t));
} else {
mutex_exit(&bcp->mutex);
}
}
bcache_t *
bcache_create(char *name, size_t size, uint_t align)
{
bcache_t *bcp;
char buffer[255];
ASSERT((align & (align - 1)) == 0);
if ((bcp = kmem_alloc(sizeof (bcache_t), KM_NOSLEEP)) == NULL)
return (NULL);
bcp->size = size;
bcp->align = align;
bcp->alloc = 0;
bcp->destroy = 0;
mutex_init(&bcp->mutex, NULL, MUTEX_DRIVER, NULL);
(void) sprintf(buffer, "%s_buffer_cache", name);
bcp->buffer_cache = kmem_cache_create(buffer, size, align, NULL, NULL,
NULL, NULL, NULL, 0);
(void) sprintf(buffer, "%s_dblk_cache", name);
bcp->dblk_cache = kmem_cache_create(buffer, sizeof (dblk_t),
DBLK_CACHE_ALIGN, bcache_dblk_constructor, bcache_dblk_destructor,
NULL, (void *)bcp, NULL, 0);
return (bcp);
}
void
bcache_destroy(bcache_t *bcp)
{
ASSERT(bcp != NULL);
mutex_enter(&bcp->mutex);
if (bcp->alloc == 0) {
kmem_cache_destroy(bcp->dblk_cache);
kmem_cache_destroy(bcp->buffer_cache);
mutex_exit(&bcp->mutex);
mutex_destroy(&bcp->mutex);
kmem_free(bcp, sizeof (bcache_t));
} else {
bcp->destroy++;
mutex_exit(&bcp->mutex);
}
}
mblk_t *
bcache_allocb(bcache_t *bcp, uint_t pri)
{
dblk_t *dbp;
mblk_t *mp = NULL;
ASSERT(bcp != NULL);
mutex_enter(&bcp->mutex);
if (bcp->destroy != 0) {
mutex_exit(&bcp->mutex);
goto out;
}
if ((dbp = kmem_cache_alloc(bcp->dblk_cache, KM_NOSLEEP)) == NULL) {
mutex_exit(&bcp->mutex);
goto out;
}
bcp->alloc++;
mutex_exit(&bcp->mutex);
ASSERT(((uintptr_t)(dbp->db_base) & (bcp->align - 1)) == 0);
mp = dbp->db_mblk;
DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
mp->b_next = mp->b_prev = mp->b_cont = NULL;
mp->b_rptr = mp->b_wptr = dbp->db_base;
mp->b_queue = NULL;
MBLK_BAND_FLAG_WORD(mp) = 0;
STR_FTALLOC(&dbp->db_fthdr, FTEV_BCALLOCB, bcp->size);
out:
FTRACE_1("bcache_allocb(): mp=0x%p", (uintptr_t)mp);
return (mp);
}
static void
dblk_lastfree_oversize(mblk_t *mp, dblk_t *dbp)
{
ASSERT(dbp->db_mblk == mp);
if (dbp->db_fthdr != NULL)
str_ftfree(dbp);
if (dbp->db_credp != NULL) {
crfree(dbp->db_credp);
dbp->db_credp = NULL;
}
dbp->db_cpid = -1;
dbp->db_struioflag = 0;
dbp->db_struioun.cksum.flags = 0;
kmem_free(dbp->db_base, dbp->db_lim - dbp->db_base);
kmem_cache_free(dbp->db_cache, dbp);
}
static mblk_t *
allocb_oversize(size_t size, int kmflags)
{
mblk_t *mp;
void *buf;
size = P2ROUNDUP(size, DBLK_CACHE_ALIGN);
if ((buf = kmem_alloc(size, kmflags)) == NULL)
return (NULL);
if ((mp = gesballoc(buf, size, DBLK_RTFU(1, M_DATA, 0, 0),
&frnop, dblk_lastfree_oversize, kmflags)) == NULL)
kmem_free(buf, size);
if (mp != NULL)
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ALLOCBIG, size);
return (mp);
}
mblk_t *
allocb_tryhard(size_t target_size)
{
size_t size;
mblk_t *bp;
for (size = target_size; size < target_size + 512;
size += DBLK_CACHE_ALIGN)
if ((bp = allocb(size, BPRI_HI)) != NULL)
return (bp);
allocb_tryhard_fails++;
return (NULL);
}
mblk_t *
allocb_wait(size_t size, uint_t pri, uint_t flags, int *error)
{
dblk_t *dbp;
mblk_t *mp;
size_t index;
index = (size -1) >> DBLK_SIZE_SHIFT;
if (flags & STR_NOSIG) {
if (index >= (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT)) {
if (size != 0) {
mp = allocb_oversize(size, KM_SLEEP);
FTRACE_1("allocb_wait (NOSIG): mp=0x%lx",
(uintptr_t)mp);
return (mp);
}
index = 0;
}
dbp = kmem_cache_alloc(dblk_cache[index], KM_SLEEP);
mp = dbp->db_mblk;
DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
mp->b_next = mp->b_prev = mp->b_cont = NULL;
mp->b_rptr = mp->b_wptr = dbp->db_base;
mp->b_queue = NULL;
MBLK_BAND_FLAG_WORD(mp) = 0;
STR_FTALLOC(&DB_FTHDR(mp), FTEV_ALLOCBW, size);
FTRACE_1("allocb_wait (NOSIG): mp=0x%lx", (uintptr_t)mp);
} else {
while ((mp = allocb(size, pri)) == NULL) {
if ((*error = strwaitbuf(size, BPRI_HI)) != 0)
return (NULL);
}
}
return (mp);
}
bufcall_id_t
esbbcall(uint_t pri, void (*func)(void *), void *arg)
{
return (bufcall(1, pri, func, arg));
}
mblk_t *
mkiocb(uint_t cmd)
{
struct iocblk *ioc;
mblk_t *mp;
if ((mp = allocb(sizeof (union ioctypes), BPRI_MED)) == NULL)
return (NULL);
bzero(mp->b_rptr, sizeof (union ioctypes));
mp->b_wptr += sizeof (struct iocblk);
mp->b_datap->db_type = M_IOCTL;
ioc = (struct iocblk *)mp->b_rptr;
ioc->ioc_cmd = cmd;
ioc->ioc_cr = kcred;
ioc->ioc_id = getiocseqno();
ioc->ioc_flag = IOC_NATIVE;
return (mp);
}
int
testb(size_t size, uint_t pri)
{
return ((size + sizeof (dblk_t)) <= kmem_avail());
}
bufcall_id_t
bufcall(size_t size, uint_t pri, void (*func)(void *), void *arg)
{
static long bid = 1;
bufcall_id_t bc_id;
struct strbufcall *bcp;
if ((bcp = kmem_alloc(sizeof (strbufcall_t), KM_NOSLEEP)) == NULL)
return (0);
bcp->bc_func = func;
bcp->bc_arg = arg;
bcp->bc_size = size;
bcp->bc_next = NULL;
bcp->bc_executor = NULL;
mutex_enter(&strbcall_lock);
bc_id = bcp->bc_id = (bufcall_id_t)(bid += 2);
if (strbcalls.bc_head == NULL) {
strbcalls.bc_head = strbcalls.bc_tail = bcp;
} else {
strbcalls.bc_tail->bc_next = bcp;
strbcalls.bc_tail = bcp;
}
cv_signal(&strbcall_cv);
mutex_exit(&strbcall_lock);
return (bc_id);
}
void
unbufcall(bufcall_id_t id)
{
strbufcall_t *bcp, *pbcp;
mutex_enter(&strbcall_lock);
again:
pbcp = NULL;
for (bcp = strbcalls.bc_head; bcp; bcp = bcp->bc_next) {
if (id == bcp->bc_id)
break;
pbcp = bcp;
}
if (bcp) {
if (bcp->bc_executor != NULL) {
if (bcp->bc_executor != curthread) {
cv_wait(&bcall_cv, &strbcall_lock);
goto again;
}
} else {
if (pbcp)
pbcp->bc_next = bcp->bc_next;
else
strbcalls.bc_head = bcp->bc_next;
if (bcp == strbcalls.bc_tail)
strbcalls.bc_tail = pbcp;
kmem_free(bcp, sizeof (strbufcall_t));
}
}
mutex_exit(&strbcall_lock);
}
mblk_t *
dupmsg(mblk_t *bp)
{
mblk_t *head, *nbp;
if (!bp || !(nbp = head = dupb(bp)))
return (NULL);
while (bp->b_cont) {
if (!(nbp->b_cont = dupb(bp->b_cont))) {
freemsg(head);
return (NULL);
}
nbp = nbp->b_cont;
bp = bp->b_cont;
}
return (head);
}
#define DUPB_NOLOAN(bp) \
((((bp)->b_datap->db_struioflag & STRUIO_ZC) != 0) ? \
copyb((bp)) : dupb((bp)))
mblk_t *
dupmsg_noloan(mblk_t *bp)
{
mblk_t *head, *nbp;
if (bp == NULL || DB_TYPE(bp) != M_DATA ||
((nbp = head = DUPB_NOLOAN(bp)) == NULL))
return (NULL);
while (bp->b_cont) {
if ((nbp->b_cont = DUPB_NOLOAN(bp->b_cont)) == NULL) {
freemsg(head);
return (NULL);
}
nbp = nbp->b_cont;
bp = bp->b_cont;
}
return (head);
}
mblk_t *
copyb(mblk_t *bp)
{
mblk_t *nbp;
dblk_t *dp, *ndp;
uchar_t *base;
size_t size;
size_t unaligned;
ASSERT(bp->b_wptr >= bp->b_rptr);
dp = bp->b_datap;
if (dp->db_fthdr != NULL)
STR_FTEVENT_MBLK(bp, caller(), FTEV_COPYB, 0);
size = dp->db_lim - dp->db_base;
unaligned = P2PHASE((uintptr_t)dp->db_base, sizeof (uint_t));
if ((nbp = allocb_tmpl(size + unaligned, bp)) == NULL)
return (NULL);
nbp->b_flag = bp->b_flag;
nbp->b_band = bp->b_band;
ndp = nbp->b_datap;
ndp->db_cksumstart = dp->db_cksumstart;
ndp->db_cksumend = dp->db_cksumend;
ndp->db_cksumstuff = dp->db_cksumstuff;
bcopy(dp->db_struioun.data, ndp->db_struioun.data,
sizeof (dp->db_struioun.data));
STR_FTEVENT_MBLK(nbp, caller(), FTEV_COPYB, 1);
base = ndp->db_base + unaligned;
bcopy(dp->db_base, ndp->db_base + unaligned, size);
nbp->b_rptr = base + (bp->b_rptr - dp->db_base);
nbp->b_wptr = nbp->b_rptr + MBLKL(bp);
return (nbp);
}
mblk_t *
copymsg(mblk_t *bp)
{
mblk_t *head, *nbp;
if (!bp || !(nbp = head = copyb(bp)))
return (NULL);
while (bp->b_cont) {
if (!(nbp->b_cont = copyb(bp->b_cont))) {
freemsg(head);
return (NULL);
}
nbp = nbp->b_cont;
bp = bp->b_cont;
}
return (head);
}
void
linkb(mblk_t *mp, mblk_t *bp)
{
ASSERT(mp && bp);
for (; mp->b_cont; mp = mp->b_cont)
;
mp->b_cont = bp;
}
mblk_t *
unlinkb(mblk_t *bp)
{
mblk_t *bp1;
bp1 = bp->b_cont;
bp->b_cont = NULL;
return (bp1);
}
mblk_t *
rmvb(mblk_t *mp, mblk_t *bp)
{
mblk_t *tmp;
mblk_t *lastp = NULL;
ASSERT(mp && bp);
for (tmp = mp; tmp; tmp = tmp->b_cont) {
if (tmp == bp) {
if (lastp)
lastp->b_cont = tmp->b_cont;
else
mp = tmp->b_cont;
tmp->b_cont = NULL;
return (mp);
}
lastp = tmp;
}
return ((mblk_t *)-1);
}
int
pullupmsg(mblk_t *mp, ssize_t len)
{
mblk_t *bp, *b_cont;
dblk_t *dbp;
ssize_t n;
ASSERT(mp->b_datap->db_ref > 0);
ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
if (len == -1) {
if (mp->b_cont == NULL && str_aligned(mp->b_rptr))
return (1);
len = xmsgsize(mp);
} else {
ssize_t first_mblk_len = mp->b_wptr - mp->b_rptr;
ASSERT(first_mblk_len >= 0);
if (len <= first_mblk_len) {
if (str_aligned(mp->b_rptr))
return (1);
len = first_mblk_len;
} else if (xmsgsize(mp) < len)
return (0);
}
if ((bp = allocb_tmpl(len, mp)) == NULL)
return (0);
dbp = bp->b_datap;
*bp = *mp;
mp->b_datap = dbp;
mp->b_datap->db_mblk = mp;
bp->b_datap->db_mblk = bp;
mp->b_rptr = mp->b_wptr = dbp->db_base;
do {
ASSERT(bp->b_datap->db_ref > 0);
ASSERT(bp->b_wptr >= bp->b_rptr);
n = MIN(bp->b_wptr - bp->b_rptr, len);
ASSERT(n >= 0);
if (n > 0)
bcopy(bp->b_rptr, mp->b_wptr, (size_t)n);
mp->b_wptr += n;
bp->b_rptr += n;
len -= n;
if (bp->b_rptr != bp->b_wptr)
break;
b_cont = bp->b_cont;
freeb(bp);
bp = b_cont;
} while (len && bp);
mp->b_cont = bp;
return (1);
}
mblk_t *
msgpullup(mblk_t *mp, ssize_t len)
{
return (msgpullup_pad(mp, len, 0));
}
mblk_t *
msgpullup_pad(mblk_t *mp, ssize_t len, size_t pad)
{
mblk_t *newmp;
ssize_t totlen = xmsgsize(mp);
ssize_t offset = 0;
if (len == -1)
len = totlen;
if (len < 0 || (len > 0 && len > totlen))
return (NULL);
const size_t alloc_sz = (size_t)len + pad;
if (alloc_sz < (size_t)len ||
(newmp = allocb_tmpl(alloc_sz, mp)) == NULL) {
return (NULL);
}
newmp->b_flag = mp->b_flag;
newmp->b_band = mp->b_band;
newmp->b_wptr += pad;
newmp->b_rptr = newmp->b_wptr;
while (len > 0) {
ssize_t seglen = MBLKL(mp);
ssize_t n = MIN(seglen, len);
ASSERT3P(mp, !=, NULL);
ASSERT3S(n, >=, 0);
if (n > 0)
bcopy(mp->b_rptr, newmp->b_wptr, n);
newmp->b_wptr += n;
len -= n;
if (n == seglen)
mp = mp->b_cont;
else if (len == 0)
offset = n;
}
ASSERT3S(len, ==, 0);
if (mp != NULL) {
newmp->b_cont = dupmsg(mp);
if (newmp->b_cont == NULL) {
freemsg(newmp);
return (NULL);
}
ASSERT3S(offset, >=, 0);
ASSERT3U(MBLKL(newmp->b_cont), >=, offset);
newmp->b_cont->b_rptr += offset;
}
return (newmp);
}
int
adjmsg(mblk_t *mp, ssize_t len)
{
mblk_t *bp;
mblk_t *save_bp = NULL;
mblk_t *prev_bp;
mblk_t *bcont;
unsigned char type;
ssize_t n;
int fromhead;
int first;
ASSERT(mp != NULL);
if (len < 0) {
fromhead = 0;
len = -len;
} else {
fromhead = 1;
}
if (xmsgsize(mp) < len)
return (0);
if (fromhead) {
first = 1;
while (len) {
ASSERT(mp->b_wptr >= mp->b_rptr);
n = MIN(mp->b_wptr - mp->b_rptr, len);
mp->b_rptr += n;
len -= n;
if (!first && (mp->b_wptr == mp->b_rptr)) {
bcont = mp->b_cont;
freeb(mp);
mp = save_bp->b_cont = bcont;
} else {
save_bp = mp;
mp = mp->b_cont;
}
first = 0;
}
} else {
type = mp->b_datap->db_type;
while (len) {
bp = mp;
save_bp = NULL;
while (bp && bp->b_datap->db_type == type) {
ASSERT(bp->b_wptr >= bp->b_rptr);
prev_bp = save_bp;
save_bp = bp;
bp = bp->b_cont;
}
if (save_bp == NULL)
break;
n = MIN(save_bp->b_wptr - save_bp->b_rptr, len);
save_bp->b_wptr -= n;
len -= n;
if ((save_bp != mp) &&
(save_bp->b_wptr == save_bp->b_rptr)) {
bcont = save_bp->b_cont;
freeb(save_bp);
prev_bp->b_cont = bcont;
}
}
}
return (1);
}
size_t
msgdsize(mblk_t *bp)
{
size_t count = 0;
for (; bp; bp = bp->b_cont)
if (bp->b_datap->db_type == M_DATA) {
ASSERT(bp->b_wptr >= bp->b_rptr);
count += bp->b_wptr - bp->b_rptr;
}
return (count);
}
mblk_t *
getq(queue_t *q)
{
mblk_t *bp;
uchar_t band = 0;
bp = getq_noenab(q, 0);
if (bp != NULL)
band = bp->b_band;
if (band == 0 && (q->q_flag & (QWANTW|QWANTWSYNC)) == 0)
return (bp);
qbackenable(q, band);
return (bp);
}
int
mp_cont_len(mblk_t *bp, int *mblkcnt)
{
mblk_t *mp;
int mblks = 0;
int bytes = 0;
for (mp = bp; mp != NULL; mp = mp->b_cont) {
bytes += MBLKL(mp);
mblks++;
}
if (mblkcnt != NULL)
*mblkcnt = mblks;
return (bytes);
}
mblk_t *
getq_noenab(queue_t *q, ssize_t rbytes)
{
mblk_t *bp, *mp1;
mblk_t *mp2 = NULL;
qband_t *qbp;
kthread_id_t freezer;
int bytecnt = 0, mblkcnt = 0;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if ((bp = q->q_first) == 0) {
q->q_flag |= QWANTR;
} else {
if ((DB_TYPE(bp) == M_DATA) && (rbytes > 0) &&
(q->q_count > rbytes)) {
for (mp1 = bp; mp1 != NULL; mp1 = mp1->b_cont) {
mblkcnt++;
bytecnt += MBLKL(mp1);
if (bytecnt >= rbytes)
break;
}
if (bytecnt > rbytes) {
ASSERT(mp1 != NULL);
mblkcnt--;
if ((mp2 = dupb(mp1)) == NULL &&
(mp2 = copyb(mp1)) == NULL) {
bytecnt = mblkcnt = 0;
goto dup_failed;
}
mp2->b_cont = mp1->b_cont;
mp1->b_wptr -= bytecnt - rbytes;
mp2->b_rptr += mp1->b_wptr - mp1->b_rptr;
mp1->b_cont = NULL;
bytecnt = rbytes;
} else {
if (mp1 != NULL) {
mp2 = mp1->b_cont;
mp1->b_cont = NULL;
}
}
if (mp2 != NULL) {
if ((mp2->b_next = bp->b_next) == NULL)
q->q_last = mp2;
else
bp->b_next->b_prev = mp2;
q->q_first = mp2;
} else {
if ((q->q_first = bp->b_next) == NULL)
q->q_last = NULL;
else
q->q_first->b_prev = NULL;
}
} else {
dup_failed:
bytecnt = mp_cont_len(bp, &mblkcnt);
if ((q->q_first = bp->b_next) == NULL)
q->q_last = NULL;
else
q->q_first->b_prev = NULL;
}
if (bp->b_band == 0) {
q->q_count -= bytecnt;
q->q_mblkcnt -= mblkcnt;
if (q->q_mblkcnt == 0 || ((q->q_count < q->q_hiwat) &&
(q->q_mblkcnt < q->q_hiwat))) {
q->q_flag &= ~QFULL;
}
} else {
int i;
ASSERT(bp->b_band <= q->q_nband);
ASSERT(q->q_bandp != NULL);
ASSERT(MUTEX_HELD(QLOCK(q)));
qbp = q->q_bandp;
i = bp->b_band;
while (--i > 0)
qbp = qbp->qb_next;
if (qbp->qb_first == qbp->qb_last) {
qbp->qb_first = NULL;
qbp->qb_last = NULL;
} else {
qbp->qb_first = bp->b_next;
}
qbp->qb_count -= bytecnt;
qbp->qb_mblkcnt -= mblkcnt;
if (qbp->qb_mblkcnt == 0 ||
((qbp->qb_count < qbp->qb_hiwat) &&
(qbp->qb_mblkcnt < qbp->qb_hiwat))) {
qbp->qb_flag &= ~QB_FULL;
}
}
q->q_flag &= ~QWANTR;
bp->b_next = NULL;
bp->b_prev = NULL;
}
if (freezer != curthread)
mutex_exit(QLOCK(q));
STR_FTEVENT_MSG(bp, q, FTEV_GETQ, 0);
return (bp);
}
void
qbackenable(queue_t *q, uchar_t band)
{
int backenab = 0;
qband_t *qbp;
kthread_id_t freezer;
ASSERT(q);
ASSERT((q->q_flag & QREADR) || MUTEX_NOT_HELD(&STREAM(q)->sd_lock));
if (band == 0 && (q->q_flag & (QWANTW|QWANTWSYNC)) == 0)
return;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if (band == 0) {
if (q->q_lowat == 0 || (q->q_count < q->q_lowat &&
q->q_mblkcnt < q->q_lowat)) {
backenab = q->q_flag & (QWANTW|QWANTWSYNC);
}
} else {
int i;
ASSERT((unsigned)band <= q->q_nband);
ASSERT(q->q_bandp != NULL);
qbp = q->q_bandp;
i = band;
while (--i > 0)
qbp = qbp->qb_next;
if (qbp->qb_lowat == 0 || (qbp->qb_count < qbp->qb_lowat &&
qbp->qb_mblkcnt < qbp->qb_lowat)) {
backenab = qbp->qb_flag & QB_WANTW;
}
}
if (backenab == 0) {
if (freezer != curthread)
mutex_exit(QLOCK(q));
return;
}
if (backenab & QWANTWSYNC)
q->q_flag &= ~QWANTWSYNC;
if (backenab & (QWANTW|QB_WANTW)) {
if (band != 0)
qbp->qb_flag &= ~QB_WANTW;
else {
q->q_flag &= ~QWANTW;
}
}
if (freezer != curthread)
mutex_exit(QLOCK(q));
if (backenab & QWANTWSYNC)
strwakeq(q, QWANTWSYNC);
if (backenab & (QWANTW|QB_WANTW))
backenable(q, band);
}
void
rmvq(queue_t *q, mblk_t *mp)
{
ASSERT(mp != NULL);
rmvq_noenab(q, mp);
if (curthread != STREAM(q)->sd_freezer && MUTEX_HELD(QLOCK(q))) {
mutex_exit(QLOCK(q));
qbackenable(q, mp->b_band);
mutex_enter(QLOCK(q));
} else {
qbackenable(q, mp->b_band);
}
}
void
rmvq_noenab(queue_t *q, mblk_t *mp)
{
int i;
qband_t *qbp = NULL;
kthread_id_t freezer;
int bytecnt = 0, mblkcnt = 0;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else if (MUTEX_HELD(QLOCK(q))) {
freezer = curthread;
} else
mutex_enter(QLOCK(q));
ASSERT(mp->b_band <= q->q_nband);
if (mp->b_band != 0) {
ASSERT(q->q_bandp != NULL);
qbp = q->q_bandp;
i = mp->b_band;
while (--i > 0)
qbp = qbp->qb_next;
if (mp == qbp->qb_first) {
if (mp->b_next && mp->b_band == mp->b_next->b_band)
qbp->qb_first = mp->b_next;
else
qbp->qb_first = NULL;
}
if (mp == qbp->qb_last) {
if (mp->b_prev && mp->b_band == mp->b_prev->b_band)
qbp->qb_last = mp->b_prev;
else
qbp->qb_last = NULL;
}
}
if (mp->b_prev)
mp->b_prev->b_next = mp->b_next;
else
q->q_first = mp->b_next;
if (mp->b_next)
mp->b_next->b_prev = mp->b_prev;
else
q->q_last = mp->b_prev;
mp->b_next = NULL;
mp->b_prev = NULL;
bytecnt = mp_cont_len(mp, &mblkcnt);
if (mp->b_band == 0) {
q->q_count -= bytecnt;
q->q_mblkcnt -= mblkcnt;
if (q->q_mblkcnt == 0 || ((q->q_count < q->q_hiwat) &&
(q->q_mblkcnt < q->q_hiwat))) {
q->q_flag &= ~QFULL;
}
} else {
qbp->qb_count -= bytecnt;
qbp->qb_mblkcnt -= mblkcnt;
if (qbp->qb_mblkcnt == 0 || ((qbp->qb_count < qbp->qb_hiwat) &&
(qbp->qb_mblkcnt < qbp->qb_hiwat))) {
qbp->qb_flag &= ~QB_FULL;
}
}
if (freezer != curthread)
mutex_exit(QLOCK(q));
STR_FTEVENT_MSG(mp, q, FTEV_RMVQ, 0);
}
void
flushq_common(queue_t *q, int flag, int pcproto_flag)
{
mblk_t *mp, *nmp;
qband_t *qbp;
int backenab = 0;
unsigned char bpri;
unsigned char qbf[NBAND];
if (q->q_first == NULL)
return;
mutex_enter(QLOCK(q));
mp = q->q_first;
q->q_first = NULL;
q->q_last = NULL;
q->q_count = 0;
q->q_mblkcnt = 0;
for (qbp = q->q_bandp; qbp; qbp = qbp->qb_next) {
qbp->qb_first = NULL;
qbp->qb_last = NULL;
qbp->qb_count = 0;
qbp->qb_mblkcnt = 0;
qbp->qb_flag &= ~QB_FULL;
}
q->q_flag &= ~QFULL;
mutex_exit(QLOCK(q));
while (mp) {
nmp = mp->b_next;
mp->b_next = mp->b_prev = NULL;
STR_FTEVENT_MBLK(mp, q, FTEV_FLUSHQ, 0);
if (pcproto_flag && (mp->b_datap->db_type == M_PCPROTO))
(void) putq(q, mp);
else if (flag || datamsg(mp->b_datap->db_type))
freemsg(mp);
else
(void) putq(q, mp);
mp = nmp;
}
bpri = 1;
mutex_enter(QLOCK(q));
for (qbp = q->q_bandp; qbp; qbp = qbp->qb_next) {
if ((qbp->qb_flag & QB_WANTW) &&
(((qbp->qb_count < qbp->qb_lowat) &&
(qbp->qb_mblkcnt < qbp->qb_lowat)) ||
qbp->qb_lowat == 0)) {
qbp->qb_flag &= ~QB_WANTW;
backenab = 1;
qbf[bpri] = 1;
} else
qbf[bpri] = 0;
bpri++;
}
ASSERT(bpri == (unsigned char)(q->q_nband + 1));
if ((q->q_flag & QWANTW) &&
(((q->q_count < q->q_lowat) &&
(q->q_mblkcnt < q->q_lowat)) || q->q_lowat == 0)) {
q->q_flag &= ~QWANTW;
backenab = 1;
qbf[0] = 1;
} else
qbf[0] = 0;
if (backenab) {
mutex_exit(QLOCK(q));
for (bpri = q->q_nband; bpri != 0; bpri--)
if (qbf[bpri])
backenable(q, bpri);
if (qbf[0])
backenable(q, 0);
} else
mutex_exit(QLOCK(q));
}
void
flushq(queue_t *q, int flag)
{
flushq_common(q, flag, 0);
}
void
flushband(queue_t *q, unsigned char pri, int flag)
{
mblk_t *mp;
mblk_t *nmp;
mblk_t *last;
qband_t *qbp;
int band;
ASSERT((flag == FLUSHDATA) || (flag == FLUSHALL));
if (pri > q->q_nband) {
return;
}
mutex_enter(QLOCK(q));
if (pri == 0) {
mp = q->q_first;
q->q_first = NULL;
q->q_last = NULL;
q->q_count = 0;
q->q_mblkcnt = 0;
for (qbp = q->q_bandp; qbp; qbp = qbp->qb_next) {
qbp->qb_first = NULL;
qbp->qb_last = NULL;
qbp->qb_count = 0;
qbp->qb_mblkcnt = 0;
qbp->qb_flag &= ~QB_FULL;
}
q->q_flag &= ~QFULL;
mutex_exit(QLOCK(q));
while (mp) {
nmp = mp->b_next;
mp->b_next = mp->b_prev = NULL;
if ((mp->b_band == 0) &&
((flag == FLUSHALL) ||
datamsg(mp->b_datap->db_type)))
freemsg(mp);
else
(void) putq(q, mp);
mp = nmp;
}
mutex_enter(QLOCK(q));
if ((q->q_flag & QWANTW) &&
(((q->q_count < q->q_lowat) &&
(q->q_mblkcnt < q->q_lowat)) || q->q_lowat == 0)) {
q->q_flag &= ~QWANTW;
mutex_exit(QLOCK(q));
backenable(q, pri);
} else
mutex_exit(QLOCK(q));
} else {
boolean_t flushed = B_FALSE;
band = pri;
ASSERT(MUTEX_HELD(QLOCK(q)));
qbp = q->q_bandp;
while (--band > 0)
qbp = qbp->qb_next;
mp = qbp->qb_first;
if (mp == NULL) {
mutex_exit(QLOCK(q));
return;
}
last = qbp->qb_last->b_next;
while (mp != last) {
ASSERT(mp->b_band == pri);
nmp = mp->b_next;
if (flag == FLUSHALL || datamsg(mp->b_datap->db_type)) {
rmvq_noenab(q, mp);
freemsg(mp);
flushed = B_TRUE;
}
mp = nmp;
}
mutex_exit(QLOCK(q));
if (flushed)
qbackenable(q, pri);
}
}
int
canput(queue_t *q)
{
TRACE_1(TR_FAC_STREAMS_FR, TR_CANPUT_IN, "canput:%p", q);
ASSERT(STRMATED(q->q_stream) || STREAM(q) == STREAM(q->q_nfsrv));
q = q->q_nfsrv;
if (!(q->q_flag & QFULL)) {
TRACE_2(TR_FAC_STREAMS_FR, TR_CANPUT_OUT, "canput:%p %d", q, 1);
return (1);
}
mutex_enter(QLOCK(q));
if (q->q_flag & QFULL) {
q->q_flag |= QWANTW;
mutex_exit(QLOCK(q));
TRACE_2(TR_FAC_STREAMS_FR, TR_CANPUT_OUT, "canput:%p %d", q, 0);
return (0);
}
mutex_exit(QLOCK(q));
TRACE_2(TR_FAC_STREAMS_FR, TR_CANPUT_OUT, "canput:%p %d", q, 1);
return (1);
}
int
bcanput(queue_t *q, unsigned char pri)
{
qband_t *qbp;
TRACE_2(TR_FAC_STREAMS_FR, TR_BCANPUT_IN, "bcanput:%p %p", q, pri);
if (!q)
return (0);
q = q->q_nfsrv;
mutex_enter(QLOCK(q));
if (pri == 0) {
if (q->q_flag & QFULL) {
q->q_flag |= QWANTW;
mutex_exit(QLOCK(q));
TRACE_3(TR_FAC_STREAMS_FR, TR_BCANPUT_OUT,
"bcanput:%p %X %d", q, pri, 0);
return (0);
}
} else {
if (pri > q->q_nband) {
mutex_exit(QLOCK(q));
TRACE_3(TR_FAC_STREAMS_FR, TR_BCANPUT_OUT,
"bcanput:%p %X %d", q, pri, 1);
return (1);
}
qbp = q->q_bandp;
while (--pri)
qbp = qbp->qb_next;
if (qbp->qb_flag & QB_FULL) {
qbp->qb_flag |= QB_WANTW;
mutex_exit(QLOCK(q));
TRACE_3(TR_FAC_STREAMS_FR, TR_BCANPUT_OUT,
"bcanput:%p %X %d", q, pri, 0);
return (0);
}
}
mutex_exit(QLOCK(q));
TRACE_3(TR_FAC_STREAMS_FR, TR_BCANPUT_OUT,
"bcanput:%p %X %d", q, pri, 1);
return (1);
}
int
putq(queue_t *q, mblk_t *bp)
{
mblk_t *tmp;
qband_t *qbp = NULL;
int mcls = (int)queclass(bp);
kthread_id_t freezer;
int bytecnt = 0, mblkcnt = 0;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if (mcls == QPCTL) {
if (bp->b_band != 0)
bp->b_band = 0;
} else if (bp->b_band != 0) {
int i;
qband_t **qbpp;
if (bp->b_band > q->q_nband) {
qbpp = &q->q_bandp;
while (*qbpp)
qbpp = &(*qbpp)->qb_next;
while (bp->b_band > q->q_nband) {
if ((*qbpp = allocband()) == NULL) {
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (0);
}
(*qbpp)->qb_hiwat = q->q_hiwat;
(*qbpp)->qb_lowat = q->q_lowat;
q->q_nband++;
qbpp = &(*qbpp)->qb_next;
}
}
ASSERT(MUTEX_HELD(QLOCK(q)));
qbp = q->q_bandp;
i = bp->b_band;
while (--i)
qbp = qbp->qb_next;
}
if (!q->q_first) {
bp->b_next = NULL;
bp->b_prev = NULL;
q->q_first = bp;
q->q_last = bp;
if (qbp) {
qbp->qb_first = bp;
qbp->qb_last = bp;
}
} else if (!qbp) {
tmp = q->q_last;
if (mcls <= (int)queclass(tmp)) {
bp->b_next = NULL;
bp->b_prev = tmp;
tmp->b_next = bp;
q->q_last = bp;
} else {
tmp = q->q_first;
while ((int)queclass(tmp) >= mcls)
tmp = tmp->b_next;
bp->b_next = tmp;
bp->b_prev = tmp->b_prev;
if (tmp->b_prev)
tmp->b_prev->b_next = bp;
else
q->q_first = bp;
tmp->b_prev = bp;
}
} else {
if (qbp->qb_first) {
tmp = qbp->qb_last;
bp->b_next = tmp->b_next;
if (tmp->b_next)
tmp->b_next->b_prev = bp;
else
q->q_last = bp;
bp->b_prev = tmp;
tmp->b_next = bp;
} else {
tmp = q->q_last;
if ((mcls < (int)queclass(tmp)) ||
(bp->b_band <= tmp->b_band)) {
bp->b_next = NULL;
bp->b_prev = tmp;
tmp->b_next = bp;
q->q_last = bp;
} else {
tmp = q->q_first;
while (tmp->b_datap->db_type >= QPCTL)
tmp = tmp->b_next;
while (tmp->b_band >= bp->b_band)
tmp = tmp->b_next;
bp->b_next = tmp;
bp->b_prev = tmp->b_prev;
if (tmp->b_prev)
tmp->b_prev->b_next = bp;
else
q->q_first = bp;
tmp->b_prev = bp;
}
qbp->qb_first = bp;
}
qbp->qb_last = bp;
}
bytecnt = mp_cont_len(bp, &mblkcnt);
if (qbp) {
qbp->qb_count += bytecnt;
qbp->qb_mblkcnt += mblkcnt;
if ((qbp->qb_count >= qbp->qb_hiwat) ||
(qbp->qb_mblkcnt >= qbp->qb_hiwat)) {
qbp->qb_flag |= QB_FULL;
}
} else {
q->q_count += bytecnt;
q->q_mblkcnt += mblkcnt;
if ((q->q_count >= q->q_hiwat) ||
(q->q_mblkcnt >= q->q_hiwat)) {
q->q_flag |= QFULL;
}
}
STR_FTEVENT_MSG(bp, q, FTEV_PUTQ, 0);
if ((mcls > QNORM) ||
(canenable(q) && (q->q_flag & QWANTR || bp->b_band)))
qenable_locked(q);
ASSERT(MUTEX_HELD(QLOCK(q)));
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (1);
}
int
putbq(queue_t *q, mblk_t *bp)
{
mblk_t *tmp;
qband_t *qbp = NULL;
int mcls = (int)queclass(bp);
kthread_id_t freezer;
int bytecnt = 0, mblkcnt = 0;
ASSERT(q && bp);
ASSERT(bp->b_next == NULL);
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if (mcls == QPCTL) {
if (bp->b_band != 0)
bp->b_band = 0;
} else if (bp->b_band != 0) {
int i;
qband_t **qbpp;
if (bp->b_band > q->q_nband) {
qbpp = &q->q_bandp;
while (*qbpp)
qbpp = &(*qbpp)->qb_next;
while (bp->b_band > q->q_nband) {
if ((*qbpp = allocband()) == NULL) {
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (0);
}
(*qbpp)->qb_hiwat = q->q_hiwat;
(*qbpp)->qb_lowat = q->q_lowat;
q->q_nband++;
qbpp = &(*qbpp)->qb_next;
}
}
qbp = q->q_bandp;
i = bp->b_band;
while (--i)
qbp = qbp->qb_next;
}
tmp = q->q_first;
if ((!tmp) || (mcls == QPCTL)) {
bp->b_next = tmp;
if (tmp)
tmp->b_prev = bp;
else
q->q_last = bp;
q->q_first = bp;
bp->b_prev = NULL;
if (qbp) {
qbp->qb_first = bp;
qbp->qb_last = bp;
}
} else if (qbp) {
tmp = qbp->qb_first;
if (tmp) {
bp->b_next = tmp;
bp->b_prev = tmp->b_prev;
if (tmp->b_prev)
tmp->b_prev->b_next = bp;
else
q->q_first = bp;
tmp->b_prev = bp;
} else {
tmp = q->q_last;
if ((mcls < (int)queclass(tmp)) ||
(bp->b_band < tmp->b_band)) {
bp->b_next = NULL;
bp->b_prev = tmp;
tmp->b_next = bp;
q->q_last = bp;
} else {
tmp = q->q_first;
while (tmp->b_datap->db_type >= QPCTL)
tmp = tmp->b_next;
while (tmp->b_band > bp->b_band)
tmp = tmp->b_next;
bp->b_next = tmp;
bp->b_prev = tmp->b_prev;
if (tmp->b_prev)
tmp->b_prev->b_next = bp;
else
q->q_first = bp;
tmp->b_prev = bp;
}
qbp->qb_last = bp;
}
qbp->qb_first = bp;
} else {
tmp = q->q_last;
if ((mcls < (int)queclass(tmp)) || (bp->b_band < tmp->b_band)) {
bp->b_next = NULL;
bp->b_prev = tmp;
tmp->b_next = bp;
q->q_last = bp;
} else {
tmp = q->q_first;
while (tmp->b_datap->db_type >= QPCTL)
tmp = tmp->b_next;
while (tmp->b_band > bp->b_band)
tmp = tmp->b_next;
bp->b_next = tmp;
bp->b_prev = tmp->b_prev;
if (tmp->b_prev)
tmp->b_prev->b_next = bp;
else
q->q_first = bp;
tmp->b_prev = bp;
}
}
bytecnt = mp_cont_len(bp, &mblkcnt);
if (qbp) {
qbp->qb_count += bytecnt;
qbp->qb_mblkcnt += mblkcnt;
if ((qbp->qb_count >= qbp->qb_hiwat) ||
(qbp->qb_mblkcnt >= qbp->qb_hiwat)) {
qbp->qb_flag |= QB_FULL;
}
} else {
q->q_count += bytecnt;
q->q_mblkcnt += mblkcnt;
if ((q->q_count >= q->q_hiwat) ||
(q->q_mblkcnt >= q->q_hiwat)) {
q->q_flag |= QFULL;
}
}
STR_FTEVENT_MSG(bp, q, FTEV_PUTBQ, 0);
if ((mcls > QNORM) || (canenable(q) && (q->q_flag & QWANTR)))
qenable_locked(q);
ASSERT(MUTEX_HELD(QLOCK(q)));
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (1);
}
int
insq(queue_t *q, mblk_t *emp, mblk_t *mp)
{
mblk_t *tmp;
qband_t *qbp = NULL;
int mcls = (int)queclass(mp);
kthread_id_t freezer;
int bytecnt = 0, mblkcnt = 0;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else if (MUTEX_HELD(QLOCK(q))) {
freezer = curthread;
} else
mutex_enter(QLOCK(q));
if (mcls == QPCTL) {
if (mp->b_band != 0)
mp->b_band = 0;
if (emp && emp->b_prev &&
(emp->b_prev->b_datap->db_type < QPCTL))
goto badord;
}
if (emp) {
if (((mcls == QNORM) && (mp->b_band < emp->b_band)) ||
(emp->b_prev && (emp->b_prev->b_datap->db_type < QPCTL) &&
(emp->b_prev->b_band < mp->b_band))) {
goto badord;
}
} else {
tmp = q->q_last;
if (tmp && (mcls == QNORM) && (mp->b_band > tmp->b_band)) {
badord:
cmn_err(CE_WARN,
"insq: attempt to insert message out of order "
"on q %p", (void *)q);
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (0);
}
}
if (mp->b_band != 0) {
int i;
qband_t **qbpp;
if (mp->b_band > q->q_nband) {
qbpp = &q->q_bandp;
while (*qbpp)
qbpp = &(*qbpp)->qb_next;
while (mp->b_band > q->q_nband) {
if ((*qbpp = allocband()) == NULL) {
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (0);
}
(*qbpp)->qb_hiwat = q->q_hiwat;
(*qbpp)->qb_lowat = q->q_lowat;
q->q_nband++;
qbpp = &(*qbpp)->qb_next;
}
}
qbp = q->q_bandp;
i = mp->b_band;
while (--i)
qbp = qbp->qb_next;
}
if ((mp->b_next = emp) != NULL) {
if ((mp->b_prev = emp->b_prev) != NULL)
emp->b_prev->b_next = mp;
else
q->q_first = mp;
emp->b_prev = mp;
} else {
if ((mp->b_prev = q->q_last) != NULL)
q->q_last->b_next = mp;
else
q->q_first = mp;
q->q_last = mp;
}
bytecnt = mp_cont_len(mp, &mblkcnt);
if (qbp) {
if (!qbp->qb_first) {
qbp->qb_first = mp;
qbp->qb_last = mp;
} else {
if (mp->b_prev == NULL || (mp->b_prev != NULL &&
(mp->b_prev->b_band != mp->b_band)))
qbp->qb_first = mp;
else if (mp->b_next == NULL || (mp->b_next != NULL &&
(mp->b_next->b_band != mp->b_band)))
qbp->qb_last = mp;
}
qbp->qb_count += bytecnt;
qbp->qb_mblkcnt += mblkcnt;
if ((qbp->qb_count >= qbp->qb_hiwat) ||
(qbp->qb_mblkcnt >= qbp->qb_hiwat)) {
qbp->qb_flag |= QB_FULL;
}
} else {
q->q_count += bytecnt;
q->q_mblkcnt += mblkcnt;
if ((q->q_count >= q->q_hiwat) ||
(q->q_mblkcnt >= q->q_hiwat)) {
q->q_flag |= QFULL;
}
}
STR_FTEVENT_MSG(mp, q, FTEV_INSQ, 0);
if (canenable(q) && (q->q_flag & QWANTR))
qenable_locked(q);
ASSERT(MUTEX_HELD(QLOCK(q)));
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (1);
}
int
putctl(queue_t *q, int type)
{
mblk_t *bp;
if ((datamsg(type) && (type != M_DELAY)) ||
(bp = allocb_tryhard(0)) == NULL)
return (0);
bp->b_datap->db_type = (unsigned char) type;
put(q, bp);
return (1);
}
int
putctl1(queue_t *q, int type, int param)
{
mblk_t *bp;
if ((datamsg(type) && (type != M_DELAY)) ||
(bp = allocb_tryhard(1)) == NULL)
return (0);
bp->b_datap->db_type = (unsigned char)type;
*bp->b_wptr++ = (unsigned char)param;
put(q, bp);
return (1);
}
int
putnextctl1(queue_t *q, int type, int param)
{
mblk_t *bp;
if ((datamsg(type) && (type != M_DELAY)) ||
((bp = allocb_tryhard(1)) == NULL))
return (0);
bp->b_datap->db_type = (unsigned char)type;
*bp->b_wptr++ = (unsigned char)param;
putnext(q, bp);
return (1);
}
int
putnextctl(queue_t *q, int type)
{
mblk_t *bp;
if ((datamsg(type) && (type != M_DELAY)) ||
((bp = allocb_tryhard(0)) == NULL))
return (0);
bp->b_datap->db_type = (unsigned char)type;
putnext(q, bp);
return (1);
}
queue_t *
backq(queue_t *q)
{
q = _OTHERQ(q);
if (q->q_next) {
q = q->q_next;
return (_OTHERQ(q));
}
return (NULL);
}
void
qreply(queue_t *q, mblk_t *bp)
{
ASSERT(q && bp);
putnext(_OTHERQ(q), bp);
}
void
qenable(queue_t *q)
{
mutex_enter(QLOCK(q));
qenable_locked(q);
mutex_exit(QLOCK(q));
}
int
qsize(queue_t *qp)
{
int count = 0;
mblk_t *mp;
mutex_enter(QLOCK(qp));
for (mp = qp->q_first; mp; mp = mp->b_next)
count++;
mutex_exit(QLOCK(qp));
return (count);
}
void
noenable(queue_t *q)
{
mutex_enter(QLOCK(q));
q->q_flag |= QNOENB;
mutex_exit(QLOCK(q));
}
void
enableok(queue_t *q)
{
mutex_enter(QLOCK(q));
q->q_flag &= ~QNOENB;
mutex_exit(QLOCK(q));
}
int
strqset(queue_t *q, qfields_t what, unsigned char pri, intptr_t val)
{
qband_t *qbp = NULL;
queue_t *wrq;
int error = 0;
kthread_id_t freezer;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if (what >= QBAD) {
error = EINVAL;
goto done;
}
if (pri != 0) {
int i;
qband_t **qbpp;
if (pri > q->q_nband) {
qbpp = &q->q_bandp;
while (*qbpp)
qbpp = &(*qbpp)->qb_next;
while (pri > q->q_nband) {
if ((*qbpp = allocband()) == NULL) {
error = EAGAIN;
goto done;
}
(*qbpp)->qb_hiwat = q->q_hiwat;
(*qbpp)->qb_lowat = q->q_lowat;
q->q_nband++;
qbpp = &(*qbpp)->qb_next;
}
}
qbp = q->q_bandp;
i = pri;
while (--i)
qbp = qbp->qb_next;
}
switch (what) {
case QHIWAT:
if (qbp)
qbp->qb_hiwat = (size_t)val;
else
q->q_hiwat = (size_t)val;
break;
case QLOWAT:
if (qbp)
qbp->qb_lowat = (size_t)val;
else
q->q_lowat = (size_t)val;
break;
case QMAXPSZ:
if (qbp)
error = EINVAL;
else
q->q_maxpsz = (ssize_t)val;
wrq = STREAM(q)->sd_wrq;
if (q != wrq->q_next)
break;
if (freezer != curthread) {
mutex_exit(QLOCK(q));
mutex_enter(QLOCK(wrq));
}
ASSERT(MUTEX_HELD(QLOCK(wrq)));
if (strmsgsz != 0) {
if (val == INFPSZ)
val = strmsgsz;
else {
if (STREAM(q)->sd_vnode->v_type == VFIFO)
val = MIN(PIPE_BUF, val);
else
val = MIN(strmsgsz, val);
}
}
STREAM(q)->sd_qn_maxpsz = val;
if (freezer != curthread) {
mutex_exit(QLOCK(wrq));
mutex_enter(QLOCK(q));
}
break;
case QMINPSZ:
if (qbp)
error = EINVAL;
else
q->q_minpsz = (ssize_t)val;
wrq = STREAM(q)->sd_wrq;
if (q != wrq->q_next)
break;
if (freezer != curthread) {
mutex_exit(QLOCK(q));
mutex_enter(QLOCK(wrq));
}
STREAM(q)->sd_qn_minpsz = (ssize_t)val;
if (freezer != curthread) {
mutex_exit(QLOCK(wrq));
mutex_enter(QLOCK(q));
}
break;
case QSTRUIOT:
if (qbp)
error = EINVAL;
else
q->q_struiot = (ushort_t)val;
break;
case QCOUNT:
case QFIRST:
case QLAST:
case QFLAG:
error = EPERM;
break;
default:
error = EINVAL;
break;
}
done:
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (error);
}
int
strqget(queue_t *q, qfields_t what, unsigned char pri, void *valp)
{
qband_t *qbp = NULL;
int error = 0;
kthread_id_t freezer;
freezer = STREAM(q)->sd_freezer;
if (freezer == curthread) {
ASSERT(frozenstr(q));
ASSERT(MUTEX_HELD(QLOCK(q)));
} else
mutex_enter(QLOCK(q));
if (what >= QBAD) {
error = EINVAL;
goto done;
}
if (pri != 0) {
int i;
qband_t **qbpp;
if (pri > q->q_nband) {
qbpp = &q->q_bandp;
while (*qbpp)
qbpp = &(*qbpp)->qb_next;
while (pri > q->q_nband) {
if ((*qbpp = allocband()) == NULL) {
error = EAGAIN;
goto done;
}
(*qbpp)->qb_hiwat = q->q_hiwat;
(*qbpp)->qb_lowat = q->q_lowat;
q->q_nband++;
qbpp = &(*qbpp)->qb_next;
}
}
qbp = q->q_bandp;
i = pri;
while (--i)
qbp = qbp->qb_next;
}
switch (what) {
case QHIWAT:
if (qbp)
*(size_t *)valp = qbp->qb_hiwat;
else
*(size_t *)valp = q->q_hiwat;
break;
case QLOWAT:
if (qbp)
*(size_t *)valp = qbp->qb_lowat;
else
*(size_t *)valp = q->q_lowat;
break;
case QMAXPSZ:
if (qbp)
error = EINVAL;
else
*(ssize_t *)valp = q->q_maxpsz;
break;
case QMINPSZ:
if (qbp)
error = EINVAL;
else
*(ssize_t *)valp = q->q_minpsz;
break;
case QCOUNT:
if (qbp)
*(size_t *)valp = qbp->qb_count;
else
*(size_t *)valp = q->q_count;
break;
case QFIRST:
if (qbp)
*(mblk_t **)valp = qbp->qb_first;
else
*(mblk_t **)valp = q->q_first;
break;
case QLAST:
if (qbp)
*(mblk_t **)valp = qbp->qb_last;
else
*(mblk_t **)valp = q->q_last;
break;
case QFLAG:
if (qbp)
*(uint_t *)valp = qbp->qb_flag;
else
*(uint_t *)valp = q->q_flag;
break;
case QSTRUIOT:
if (qbp)
error = EINVAL;
else
*(short *)valp = q->q_struiot;
break;
default:
error = EINVAL;
break;
}
done:
if (freezer != curthread)
mutex_exit(QLOCK(q));
return (error);
}
void
strwakeq(queue_t *q, int flag)
{
stdata_t *stp = STREAM(q);
pollhead_t *pl;
mutex_enter(&stp->sd_lock);
pl = &stp->sd_pollist;
if (flag & QWANTWSYNC) {
ASSERT(!(q->q_flag & QREADR));
if (stp->sd_flag & WSLEEP) {
stp->sd_flag &= ~WSLEEP;
cv_broadcast(&stp->sd_wrq->q_wait);
} else {
stp->sd_wakeq |= WSLEEP;
}
mutex_exit(&stp->sd_lock);
pollwakeup(pl, POLLWRNORM);
mutex_enter(&stp->sd_lock);
if (stp->sd_sigflags & S_WRNORM)
strsendsig(stp->sd_siglist, S_WRNORM, 0, 0);
} else if (flag & QWANTR) {
if (stp->sd_flag & RSLEEP) {
stp->sd_flag &= ~RSLEEP;
cv_broadcast(&_RD(stp->sd_wrq)->q_wait);
} else {
stp->sd_wakeq |= RSLEEP;
}
mutex_exit(&stp->sd_lock);
pollwakeup(pl, POLLIN | POLLRDNORM);
mutex_enter(&stp->sd_lock);
{
int events = stp->sd_sigflags & (S_INPUT | S_RDNORM);
if (events)
strsendsig(stp->sd_siglist, events, 0, 0);
}
} else {
if (stp->sd_flag & WSLEEP) {
stp->sd_flag &= ~WSLEEP;
cv_broadcast(&stp->sd_wrq->q_wait);
}
mutex_exit(&stp->sd_lock);
pollwakeup(pl, POLLWRNORM);
mutex_enter(&stp->sd_lock);
if (stp->sd_sigflags & S_WRNORM)
strsendsig(stp->sd_siglist, S_WRNORM, 0, 0);
}
mutex_exit(&stp->sd_lock);
}
int
struioget(queue_t *q, mblk_t *mp, struiod_t *dp, int noblock)
{
stdata_t *stp = STREAM(q);
int typ = STRUIOT_STANDARD;
uio_t *uiop = &dp->d_uio;
dblk_t *dbp;
ssize_t uiocnt;
ssize_t cnt;
unsigned char *ptr;
ssize_t resid;
int error = 0;
on_trap_data_t otd;
queue_t *stwrq;
stwrq = stp->sd_struiowrq;
if (stwrq)
typ = stwrq->q_struiot;
for (; (resid = uiop->uio_resid) > 0 && mp; mp = mp->b_cont) {
dbp = mp->b_datap;
ptr = (uchar_t *)(mp->b_rptr + dbp->db_cksumstuff);
uiocnt = dbp->db_cksumend - dbp->db_cksumstuff;
cnt = MIN(uiocnt, uiop->uio_resid);
if (!(dbp->db_struioflag & STRUIO_SPEC) ||
(dbp->db_struioflag & STRUIO_DONE) || cnt == 0) {
continue;
}
switch (typ) {
case STRUIOT_STANDARD:
if (noblock) {
if (on_trap(&otd, OT_DATA_ACCESS)) {
no_trap();
error = EWOULDBLOCK;
goto out;
}
}
if (error = uiomove(ptr, cnt, UIO_WRITE, uiop)) {
if (noblock)
no_trap();
goto out;
}
if (noblock)
no_trap();
break;
default:
error = EIO;
goto out;
}
dbp->db_struioflag |= STRUIO_DONE;
dbp->db_cksumstuff += cnt;
}
out:
if (error == EWOULDBLOCK && (resid -= uiop->uio_resid) > 0) {
uiocnt = dbp->db_cksumend - dbp->db_cksumstuff;
if (uiocnt >= resid)
dbp->db_cksumstuff += resid;
}
return (error);
}
static boolean_t
rwnext_enter(queue_t *qp)
{
mutex_enter(QLOCK(qp));
if (qp->q_flag & QWCLOSE) {
mutex_exit(QLOCK(qp));
return (B_FALSE);
}
qp->q_rwcnt++;
ASSERT(qp->q_rwcnt != 0);
mutex_exit(QLOCK(qp));
return (B_TRUE);
}
static void
rwnext_exit(queue_t *qp)
{
mutex_enter(QLOCK(qp));
qp->q_rwcnt--;
if (qp->q_flag & QWANTRMQSYNC) {
qp->q_flag &= ~QWANTRMQSYNC;
cv_broadcast(&qp->q_wait);
}
mutex_exit(QLOCK(qp));
}
int
rwnext(queue_t *qp, struiod_t *dp)
{
queue_t *nqp;
syncq_t *sq;
uint16_t count;
uint16_t flags;
struct qinit *qi;
int (*proc)();
struct stdata *stp;
int isread;
int rval;
stp = STREAM(qp);
if ((nqp = _WR(qp)) == qp) {
isread = 0;
mutex_enter(&stp->sd_lock);
qp = nqp->q_next;
} else {
isread = 1;
if (nqp != stp->sd_wrq)
mutex_enter(&stp->sd_lock);
qp = _RD(nqp->q_next);
}
qi = qp->q_qinfo;
if (qp->q_struiot == STRUIOT_NONE || ! (proc = qi->qi_rwp)) {
mutex_exit(&stp->sd_lock);
return (EINVAL);
}
if (rwnext_enter(qp) == B_FALSE) {
mutex_exit(&stp->sd_lock);
return (EINVAL);
}
sq = qp->q_syncq;
mutex_enter(SQLOCK(sq));
mutex_exit(&stp->sd_lock);
count = sq->sq_count;
flags = sq->sq_flags;
ASSERT(sq->sq_ciputctrl == NULL || (flags & SQ_CIPUT));
while ((flags & SQ_GOAWAY) || (!(flags & SQ_CIPUT) && count != 0)) {
if (qp->q_flag & QWCLOSE) {
mutex_exit(SQLOCK(sq));
rwnext_exit(qp);
return (EINVAL);
}
sq->sq_flags = flags | SQ_WANTWAKEUP;
cv_wait(&sq->sq_wait, SQLOCK(sq));
count = sq->sq_count;
flags = sq->sq_flags;
}
if (isread == 0 && stp->sd_struiowrq == NULL ||
isread == 1 && stp->sd_struiordq == NULL) {
mutex_exit(SQLOCK(sq));
rwnext_exit(qp);
return (EINVAL);
}
if (!(flags & SQ_CIPUT))
sq->sq_flags = flags | SQ_EXCL;
sq->sq_count = count + 1;
ASSERT(sq->sq_count != 0);
mutex_exit(SQLOCK(sq));
if (! isread && (qp->q_flag & QFULL)) {
mutex_enter(QLOCK(qp));
if (qp->q_flag & QFULL) {
qp->q_flag |= QWANTWSYNC;
mutex_exit(QLOCK(qp));
rval = EWOULDBLOCK;
goto out;
}
mutex_exit(QLOCK(qp));
}
if (! isread && dp->d_mp)
STR_FTEVENT_MSG(dp->d_mp, nqp, FTEV_RWNEXT, dp->d_mp->b_rptr -
dp->d_mp->b_datap->db_base);
rval = (*proc)(qp, dp);
if (isread && dp->d_mp)
STR_FTEVENT_MSG(dp->d_mp, _RD(nqp), FTEV_RWNEXT,
dp->d_mp->b_rptr - dp->d_mp->b_datap->db_base);
out:
rwnext_exit(qp);
mutex_enter(SQLOCK(sq));
flags = sq->sq_flags;
ASSERT(sq->sq_count != 0);
sq->sq_count--;
if (flags & SQ_TAIL) {
putnext_tail(sq, qp, flags);
ASSERT(flags & SQ_TAIL);
return (rval);
}
ASSERT(flags & (SQ_EXCL|SQ_CIPUT));
sq->sq_flags = flags & ~SQ_EXCL;
if (sq->sq_flags & SQ_WANTWAKEUP) {
sq->sq_flags &= ~SQ_WANTWAKEUP;
cv_broadcast(&sq->sq_wait);
}
mutex_exit(SQLOCK(sq));
return (rval);
}
int
infonext(queue_t *qp, infod_t *idp)
{
queue_t *nqp;
syncq_t *sq;
uint16_t count;
uint16_t flags;
struct qinit *qi;
int (*proc)();
struct stdata *stp;
int rval;
stp = STREAM(qp);
mutex_enter(&stp->sd_lock);
if ((nqp = _WR(qp)) == qp) {
qp = nqp->q_next;
} else {
qp = _RD(nqp->q_next);
}
qi = qp->q_qinfo;
if (qp->q_struiot == STRUIOT_NONE || ! (proc = qi->qi_infop)) {
mutex_exit(&stp->sd_lock);
return (EINVAL);
}
sq = qp->q_syncq;
mutex_enter(SQLOCK(sq));
mutex_exit(&stp->sd_lock);
count = sq->sq_count;
flags = sq->sq_flags;
ASSERT(sq->sq_ciputctrl == NULL || (flags & SQ_CIPUT));
while ((flags & SQ_GOAWAY) || (!(flags & SQ_CIPUT) && count != 0)) {
sq->sq_flags = flags | SQ_WANTWAKEUP;
cv_wait(&sq->sq_wait, SQLOCK(sq));
count = sq->sq_count;
flags = sq->sq_flags;
}
if (! (flags & SQ_CIPUT))
sq->sq_flags = flags | SQ_EXCL;
sq->sq_count = count + 1;
ASSERT(sq->sq_count != 0);
mutex_exit(SQLOCK(sq));
rval = (*proc)(qp, idp);
mutex_enter(SQLOCK(sq));
flags = sq->sq_flags;
ASSERT(sq->sq_count != 0);
sq->sq_count--;
if (flags & SQ_TAIL) {
putnext_tail(sq, qp, flags);
ASSERT(flags & SQ_TAIL);
return (rval);
}
ASSERT(flags & (SQ_EXCL|SQ_CIPUT));
sq->sq_flags = flags & ~SQ_EXCL;
mutex_exit(SQLOCK(sq));
return (rval);
}
int
isuioq(queue_t *q)
{
if (q->q_flag & QREADR)
return (STREAM(q)->sd_struiordq == q);
else
return (STREAM(q)->sd_struiowrq == q);
}
#if defined(__sparc)
int disable_putlocks = 0;
#else
int disable_putlocks = 1;
#endif
static void
create_syncq_putlocks(queue_t *q)
{
syncq_t *sq = q->q_syncq;
ciputctrl_t *cip;
int i;
ASSERT(sq != NULL);
ASSERT(disable_putlocks == 0);
ASSERT(n_ciputctrl >= min_n_ciputctrl);
ASSERT(ciputctrl_cache != NULL);
if (!(sq->sq_type & SQ_CIPUT))
return;
for (i = 0; i <= 1; i++) {
if (sq->sq_ciputctrl == NULL) {
cip = kmem_cache_alloc(ciputctrl_cache, KM_SLEEP);
SUMCHECK_CIPUTCTRL_COUNTS(cip, n_ciputctrl - 1, 0);
mutex_enter(SQLOCK(sq));
if (sq->sq_ciputctrl != NULL) {
mutex_exit(SQLOCK(sq));
kmem_cache_free(ciputctrl_cache, cip);
} else {
ASSERT(sq->sq_nciputctrl == 0);
sq->sq_nciputctrl = n_ciputctrl - 1;
membar_producer();
sq->sq_ciputctrl = cip;
mutex_exit(SQLOCK(sq));
}
}
ASSERT(sq->sq_nciputctrl == n_ciputctrl - 1);
if (i == 1)
break;
q = _OTHERQ(q);
if (!(q->q_flag & QPERQ)) {
ASSERT(sq == q->q_syncq);
break;
}
ASSERT(q->q_syncq != NULL);
ASSERT(sq != q->q_syncq);
sq = q->q_syncq;
ASSERT(sq->sq_type & SQ_CIPUT);
}
}
void
create_putlocks(queue_t *q, int stream)
{
ciputctrl_t *cip;
struct stdata *stp = STREAM(q);
q = _WR(q);
ASSERT(stp != NULL);
if (disable_putlocks != 0)
return;
if (n_ciputctrl < min_n_ciputctrl)
return;
ASSERT(ciputctrl_cache != NULL);
if (stream != 0 && stp->sd_ciputctrl == NULL) {
cip = kmem_cache_alloc(ciputctrl_cache, KM_SLEEP);
SUMCHECK_CIPUTCTRL_COUNTS(cip, n_ciputctrl - 1, 0);
mutex_enter(&stp->sd_lock);
if (stp->sd_ciputctrl != NULL) {
mutex_exit(&stp->sd_lock);
kmem_cache_free(ciputctrl_cache, cip);
} else {
ASSERT(stp->sd_nciputctrl == 0);
stp->sd_nciputctrl = n_ciputctrl - 1;
membar_producer();
stp->sd_ciputctrl = cip;
mutex_exit(&stp->sd_lock);
}
}
ASSERT(stream == 0 || stp->sd_nciputctrl == n_ciputctrl - 1);
while (_SAMESTR(q)) {
create_syncq_putlocks(q);
if (stream == 0)
return;
q = q->q_next;
}
ASSERT(q != NULL);
create_syncq_putlocks(q);
}
int str_ftnever = 1;
int str_ftstack = 0;
void
str_ftevent(fthdr_t *hp, void *p, ushort_t evnt, ushort_t data)
{
ftblk_t *bp = hp->tail;
ftblk_t *nbp;
ftevnt_t *ep;
int ix, nix;
ASSERT(hp != NULL);
for (;;) {
if ((ix = bp->ix) == FTBLK_EVNTS) {
nbp = kmem_cache_alloc(ftblk_cache, KM_NOSLEEP);
if (nbp == NULL) {
str_ftnever++;
return;
}
nbp->nxt = NULL;
nbp->ix = 1;
membar_producer();
if (atomic_cas_ptr(&hp->tail, bp, nbp) == bp) {
bp->nxt = nbp;
membar_producer();
bp = nbp;
ix = 0;
goto cas_good;
} else {
kmem_cache_free(ftblk_cache, nbp);
bp = hp->tail;
continue;
}
}
nix = ix + 1;
if (atomic_cas_32((uint32_t *)&bp->ix, ix, nix) == ix) {
cas_good:
if (curthread != hp->thread) {
hp->thread = curthread;
evnt |= FTEV_CS;
}
if (CPU->cpu_seqid != hp->cpu_seqid) {
hp->cpu_seqid = CPU->cpu_seqid;
evnt |= FTEV_PS;
}
ep = &bp->ev[ix];
break;
}
}
if (evnt & FTEV_QMASK) {
queue_t *qp = p;
if (!(qp->q_flag & QREADR))
evnt |= FTEV_ISWR;
ep->mid = Q2NAME(qp);
if ((evnt & FTEV_MASK) == FTEV_PUTNEXT && qp->q_next != NULL)
ep->midnext = Q2NAME(qp->q_next);
else
ep->midnext = NULL;
} else {
ep->mid = p;
ep->midnext = NULL;
}
if (ep->stk != NULL)
ep->stk->fs_depth = getpcstack(ep->stk->fs_stk, FTSTK_DEPTH);
ep->ts = gethrtime();
ep->evnt = evnt;
ep->data = data;
hp->hash = (hp->hash << 9) + hp->hash;
hp->hash += (evnt << 16) | data;
hp->hash += (uintptr_t)ep->mid;
}
void
str_ftfree(dblk_t *dbp)
{
fthdr_t *hp = dbp->db_fthdr;
ftblk_t *bp = &hp->first;
ftblk_t *nbp;
if (bp != hp->tail || bp->ix != 0) {
bp = hp->first.nxt;
hp->tail = &hp->first;
hp->hash = 0;
hp->first.nxt = NULL;
hp->first.ix = 0;
while (bp != NULL) {
nbp = bp->nxt;
kmem_cache_free(ftblk_cache, bp);
bp = nbp;
}
}
kmem_cache_free(fthdr_cache, hp);
dbp->db_fthdr = NULL;
}