18329232eSGordon Ross /*
28329232eSGordon Ross  * CDDL HEADER START
38329232eSGordon Ross  *
48329232eSGordon Ross  * The contents of this file are subject to the terms of the
58329232eSGordon Ross  * Common Development and Distribution License (the "License").
68329232eSGordon Ross  * You may not use this file except in compliance with the License.
78329232eSGordon Ross  *
88329232eSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98329232eSGordon Ross  * or http://www.opensolaris.org/os/licensing.
108329232eSGordon Ross  * See the License for the specific language governing permissions
118329232eSGordon Ross  * and limitations under the License.
128329232eSGordon Ross  *
138329232eSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
148329232eSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158329232eSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
168329232eSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
178329232eSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
188329232eSGordon Ross  *
198329232eSGordon Ross  * CDDL HEADER END
208329232eSGordon Ross  */
218329232eSGordon Ross /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
228329232eSGordon Ross /*	  All Rights Reserved	*/
238329232eSGordon Ross 
248329232eSGordon Ross /*
258329232eSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
268329232eSGordon Ross  * Use is subject to license terms.
278329232eSGordon Ross  *
288329232eSGordon Ross  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
29*9b664393SGarrett D'Amore  * Copyright 2022 Garrett D'Amore
308329232eSGordon Ross  */
318329232eSGordon Ross 
328329232eSGordon Ross #include <sys/types.h>
338329232eSGordon Ross #include <sys/param.h>
348329232eSGordon Ross #include <sys/thread.h>
358329232eSGordon Ross #include <sys/sysmacros.h>
368329232eSGordon Ross #include <sys/stropts.h>
378329232eSGordon Ross #include <sys/stream.h>
388329232eSGordon Ross #include <sys/strsubr.h>
398329232eSGordon Ross #include <sys/strsun.h>
408329232eSGordon Ross #include <sys/conf.h>
418329232eSGordon Ross #include <sys/debug.h>
428329232eSGordon Ross #include <sys/cmn_err.h>
438329232eSGordon Ross #include <sys/kmem.h>
448329232eSGordon Ross #include <sys/atomic.h>
458329232eSGordon Ross #include <sys/errno.h>
468329232eSGordon Ross #include <sys/vtrace.h>
478329232eSGordon Ross #include <sys/ftrace.h>
488329232eSGordon Ross #include <sys/ontrap.h>
498329232eSGordon Ross #include <sys/sdt.h>
508329232eSGordon Ross #include <sys/strft.h>
518329232eSGordon Ross 
528329232eSGordon Ross /*
538329232eSGordon Ross  * This file contains selected functions from io/stream.c
548329232eSGordon Ross  * needed by this library, mostly unmodified.
558329232eSGordon Ross  */
568329232eSGordon Ross 
578329232eSGordon Ross /*
588329232eSGordon Ross  * STREAMS message allocator: principles of operation
598329232eSGordon Ross  * (See usr/src/uts/common/io/stream.c)
608329232eSGordon Ross  */
618329232eSGordon Ross #define	DBLK_MAX_CACHE		73728
628329232eSGordon Ross #define	DBLK_CACHE_ALIGN	64
638329232eSGordon Ross #define	DBLK_MIN_SIZE		8
648329232eSGordon Ross #define	DBLK_SIZE_SHIFT		3
658329232eSGordon Ross 
668329232eSGordon Ross #ifdef _BIG_ENDIAN
678329232eSGordon Ross #define	DBLK_RTFU_SHIFT(field)	\
688329232eSGordon Ross 	(8 * (&((dblk_t *)0)->db_struioflag - &((dblk_t *)0)->field))
698329232eSGordon Ross #else
708329232eSGordon Ross #define	DBLK_RTFU_SHIFT(field)	\
718329232eSGordon Ross 	(8 * (&((dblk_t *)0)->field - &((dblk_t *)0)->db_ref))
728329232eSGordon Ross #endif
738329232eSGordon Ross 
748329232eSGordon Ross #define	DBLK_RTFU(ref, type, flags, uioflag)	\
758329232eSGordon Ross 	(((ref) << DBLK_RTFU_SHIFT(db_ref)) | \
768329232eSGordon Ross 	((type) << DBLK_RTFU_SHIFT(db_type)) | \
778329232eSGordon Ross 	(((flags) | (ref - 1)) << DBLK_RTFU_SHIFT(db_flags)) | \
788329232eSGordon Ross 	((uioflag) << DBLK_RTFU_SHIFT(db_struioflag)))
798329232eSGordon Ross #define	DBLK_RTFU_REF_MASK	(DBLK_REFMAX << DBLK_RTFU_SHIFT(db_ref))
808329232eSGordon Ross #define	DBLK_RTFU_WORD(dbp)	(*((uint32_t *)&(dbp)->db_ref))
818329232eSGordon Ross #define	MBLK_BAND_FLAG_WORD(mp)	(*((uint32_t *)&(mp)->b_band))
828329232eSGordon Ross 
838329232eSGordon Ross static size_t dblk_sizes[] = {
848329232eSGordon Ross #ifdef _LP64
858329232eSGordon Ross 	16, 80, 144, 208, 272, 336, 528, 1040, 1488, 1936, 2576, 3856,
868329232eSGordon Ross 	8192, 12048, 16384, 20240, 24576, 28432, 32768, 36624,
878329232eSGordon Ross 	40960, 44816, 49152, 53008, 57344, 61200, 65536, 69392,
888329232eSGordon Ross #else
898329232eSGordon Ross 	64, 128, 320, 576, 1088, 1536, 1984, 2624, 3904,
908329232eSGordon Ross 	8192, 12096, 16384, 20288, 24576, 28480, 32768, 36672,
918329232eSGordon Ross 	40960, 44864, 49152, 53056, 57344, 61248, 65536, 69440,
928329232eSGordon Ross #endif
938329232eSGordon Ross 	DBLK_MAX_CACHE, 0
948329232eSGordon Ross };
958329232eSGordon Ross 
968329232eSGordon Ross static struct kmem_cache *dblk_cache[DBLK_MAX_CACHE / DBLK_MIN_SIZE];
978329232eSGordon Ross static struct kmem_cache *mblk_cache;
988329232eSGordon Ross static struct kmem_cache *dblk_esb_cache;
998329232eSGordon Ross 
1008329232eSGordon Ross static void dblk_lastfree(mblk_t *mp, dblk_t *dbp);
1018329232eSGordon Ross static mblk_t *allocb_oversize(size_t size, int flags);
1028329232eSGordon Ross static int allocb_tryhard_fails;
1038329232eSGordon Ross static void frnop_func(void *arg);
1048329232eSGordon Ross frtn_t frnop = { frnop_func };
1058329232eSGordon Ross static void bcache_dblk_lastfree(mblk_t *mp, dblk_t *dbp);
1068329232eSGordon Ross 
1078329232eSGordon Ross /*
1088329232eSGordon Ross  * Patchable mblk/dblk kmem_cache flags.
1098329232eSGordon Ross  */
1108329232eSGordon Ross int dblk_kmem_flags = 0;
1118329232eSGordon Ross int mblk_kmem_flags = 0;
1128329232eSGordon Ross 
1138329232eSGordon Ross static int
dblk_constructor(void * buf,void * cdrarg,int kmflags)1148329232eSGordon Ross dblk_constructor(void *buf, void *cdrarg, int kmflags)
1158329232eSGordon Ross {
1168329232eSGordon Ross 	dblk_t *dbp = buf;
1178329232eSGordon Ross 	ssize_t msg_size = (ssize_t)cdrarg;
1188329232eSGordon Ross 	size_t index;
1198329232eSGordon Ross 
1208329232eSGordon Ross 	ASSERT(msg_size != 0);
1218329232eSGordon Ross 
1228329232eSGordon Ross 	index = (msg_size - 1) >> DBLK_SIZE_SHIFT;
1238329232eSGordon Ross 
1248329232eSGordon Ross 	ASSERT(index < (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT));
1258329232eSGordon Ross 
1268329232eSGordon Ross 	if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
1278329232eSGordon Ross 		return (-1);
1288329232eSGordon Ross 	if ((msg_size & PAGEOFFSET) == 0) {
1298329232eSGordon Ross 		dbp->db_base = kmem_alloc(msg_size, kmflags);
1308329232eSGordon Ross 		if (dbp->db_base == NULL) {
1318329232eSGordon Ross 			kmem_cache_free(mblk_cache, dbp->db_mblk);
1328329232eSGordon Ross 			return (-1);
1338329232eSGordon Ross 		}
1348329232eSGordon Ross 	} else {
1358329232eSGordon Ross 		dbp->db_base = (unsigned char *)&dbp[1];
1368329232eSGordon Ross 	}
1378329232eSGordon Ross 
1388329232eSGordon Ross 	dbp->db_mblk->b_datap = dbp;
1398329232eSGordon Ross 	dbp->db_cache = dblk_cache[index];
1408329232eSGordon Ross 	dbp->db_lim = dbp->db_base + msg_size;
1418329232eSGordon Ross 	dbp->db_free = dbp->db_lastfree = dblk_lastfree;
1428329232eSGordon Ross 	dbp->db_frtnp = NULL;
1438329232eSGordon Ross 	dbp->db_fthdr = NULL;
1448329232eSGordon Ross 	dbp->db_credp = NULL;
1458329232eSGordon Ross 	dbp->db_cpid = -1;
1468329232eSGordon Ross 	dbp->db_struioflag = 0;
1478329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
1488329232eSGordon Ross 	return (0);
1498329232eSGordon Ross }
1508329232eSGordon Ross 
1518329232eSGordon Ross /*ARGSUSED*/
1528329232eSGordon Ross static int
dblk_esb_constructor(void * buf,void * cdrarg,int kmflags)1538329232eSGordon Ross dblk_esb_constructor(void *buf, void *cdrarg, int kmflags)
1548329232eSGordon Ross {
1558329232eSGordon Ross 	dblk_t *dbp = buf;
1568329232eSGordon Ross 
1578329232eSGordon Ross 	if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
1588329232eSGordon Ross 		return (-1);
1598329232eSGordon Ross 	dbp->db_mblk->b_datap = dbp;
1608329232eSGordon Ross 	dbp->db_cache = dblk_esb_cache;
1618329232eSGordon Ross 	dbp->db_fthdr = NULL;
1628329232eSGordon Ross 	dbp->db_credp = NULL;
1638329232eSGordon Ross 	dbp->db_cpid = -1;
1648329232eSGordon Ross 	dbp->db_struioflag = 0;
1658329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
1668329232eSGordon Ross 	return (0);
1678329232eSGordon Ross }
1688329232eSGordon Ross 
1698329232eSGordon Ross static int
bcache_dblk_constructor(void * buf,void * cdrarg,int kmflags)1708329232eSGordon Ross bcache_dblk_constructor(void *buf, void *cdrarg, int kmflags)
1718329232eSGordon Ross {
1728329232eSGordon Ross 	dblk_t *dbp = buf;
1738329232eSGordon Ross 	bcache_t *bcp = cdrarg;
1748329232eSGordon Ross 
1758329232eSGordon Ross 	if ((dbp->db_mblk = kmem_cache_alloc(mblk_cache, kmflags)) == NULL)
1768329232eSGordon Ross 		return (-1);
1778329232eSGordon Ross 
1788329232eSGordon Ross 	dbp->db_base = kmem_cache_alloc(bcp->buffer_cache, kmflags);
1798329232eSGordon Ross 	if (dbp->db_base == NULL) {
1808329232eSGordon Ross 		kmem_cache_free(mblk_cache, dbp->db_mblk);
1818329232eSGordon Ross 		return (-1);
1828329232eSGordon Ross 	}
1838329232eSGordon Ross 
1848329232eSGordon Ross 	dbp->db_mblk->b_datap = dbp;
1858329232eSGordon Ross 	dbp->db_cache = (void *)bcp;
1868329232eSGordon Ross 	dbp->db_lim = dbp->db_base + bcp->size;
1878329232eSGordon Ross 	dbp->db_free = dbp->db_lastfree = bcache_dblk_lastfree;
1888329232eSGordon Ross 	dbp->db_frtnp = NULL;
1898329232eSGordon Ross 	dbp->db_fthdr = NULL;
1908329232eSGordon Ross 	dbp->db_credp = NULL;
1918329232eSGordon Ross 	dbp->db_cpid = -1;
1928329232eSGordon Ross 	dbp->db_struioflag = 0;
1938329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
1948329232eSGordon Ross 	return (0);
1958329232eSGordon Ross }
1968329232eSGordon Ross 
1978329232eSGordon Ross /*ARGSUSED*/
1988329232eSGordon Ross static void
dblk_destructor(void * buf,void * cdrarg)1998329232eSGordon Ross dblk_destructor(void *buf, void *cdrarg)
2008329232eSGordon Ross {
2018329232eSGordon Ross 	dblk_t *dbp = buf;
2028329232eSGordon Ross 	ssize_t msg_size = (ssize_t)cdrarg;
2038329232eSGordon Ross 
2048329232eSGordon Ross 	ASSERT(dbp->db_mblk->b_datap == dbp);
2058329232eSGordon Ross 	ASSERT(msg_size != 0);
2068329232eSGordon Ross 	ASSERT(dbp->db_struioflag == 0);
2078329232eSGordon Ross 	ASSERT(dbp->db_struioun.cksum.flags == 0);
2088329232eSGordon Ross 
2098329232eSGordon Ross 	if ((msg_size & PAGEOFFSET) == 0) {
2108329232eSGordon Ross 		kmem_free(dbp->db_base, msg_size);
2118329232eSGordon Ross 	}
2128329232eSGordon Ross 
2138329232eSGordon Ross 	kmem_cache_free(mblk_cache, dbp->db_mblk);
2148329232eSGordon Ross }
2158329232eSGordon Ross 
2168329232eSGordon Ross static void
bcache_dblk_destructor(void * buf,void * cdrarg)2178329232eSGordon Ross bcache_dblk_destructor(void *buf, void *cdrarg)
2188329232eSGordon Ross {
2198329232eSGordon Ross 	dblk_t *dbp = buf;
2208329232eSGordon Ross 	bcache_t *bcp = cdrarg;
2218329232eSGordon Ross 
2228329232eSGordon Ross 	kmem_cache_free(bcp->buffer_cache, dbp->db_base);
2238329232eSGordon Ross 
2248329232eSGordon Ross 	ASSERT(dbp->db_mblk->b_datap == dbp);
2258329232eSGordon Ross 	ASSERT(dbp->db_struioflag == 0);
2268329232eSGordon Ross 	ASSERT(dbp->db_struioun.cksum.flags == 0);
2278329232eSGordon Ross 
2288329232eSGordon Ross 	kmem_cache_free(mblk_cache, dbp->db_mblk);
2298329232eSGordon Ross }
2308329232eSGordon Ross 
2318329232eSGordon Ross /* Needed in the ASSERT below */
2328329232eSGordon Ross #ifdef	DEBUG
2338329232eSGordon Ross #ifdef	_KERNEL
2348329232eSGordon Ross #define	KMEM_SLAB_T_SZ	sizeof (kmem_slab_t)
2358329232eSGordon Ross #else	/* _KERNEL */
2368329232eSGordon Ross #define	KMEM_SLAB_T_SZ	64	/* fakekernel */
2378329232eSGordon Ross #endif	/* _KERNEL */
2388329232eSGordon Ross #endif	/* DEBUG */
2398329232eSGordon Ross 
2408329232eSGordon Ross void
streams_msg_init(void)2418329232eSGordon Ross streams_msg_init(void)
2428329232eSGordon Ross {
2438329232eSGordon Ross 	char name[40];
2448329232eSGordon Ross 	size_t size;
2458329232eSGordon Ross 	size_t lastsize = DBLK_MIN_SIZE;
2468329232eSGordon Ross 	size_t *sizep;
2478329232eSGordon Ross 	struct kmem_cache *cp;
2488329232eSGordon Ross 	size_t tot_size;
2498329232eSGordon Ross 	int offset;
2508329232eSGordon Ross 
2518329232eSGordon Ross 	mblk_cache = kmem_cache_create("streams_mblk", sizeof (mblk_t), 32,
2528329232eSGordon Ross 	    NULL, NULL, NULL, NULL, NULL, mblk_kmem_flags);
2538329232eSGordon Ross 
2548329232eSGordon Ross 	for (sizep = dblk_sizes; (size = *sizep) != 0; sizep++) {
2558329232eSGordon Ross 
2568329232eSGordon Ross 		if ((offset = (size & PAGEOFFSET)) != 0) {
2578329232eSGordon Ross 			/*
2588329232eSGordon Ross 			 * We are in the middle of a page, dblk should
2598329232eSGordon Ross 			 * be allocated on the same page
2608329232eSGordon Ross 			 */
2618329232eSGordon Ross 			tot_size = size + sizeof (dblk_t);
2628329232eSGordon Ross 			ASSERT((offset + sizeof (dblk_t) + KMEM_SLAB_T_SZ)
2638329232eSGordon Ross 			    < PAGESIZE);
2648329232eSGordon Ross 			ASSERT((tot_size & (DBLK_CACHE_ALIGN - 1)) == 0);
2658329232eSGordon Ross 
2668329232eSGordon Ross 		} else {
2678329232eSGordon Ross 
2688329232eSGordon Ross 			/*
2698329232eSGordon Ross 			 * buf size is multiple of page size, dblk and
2708329232eSGordon Ross 			 * buffer are allocated separately.
2718329232eSGordon Ross 			 */
2728329232eSGordon Ross 
2738329232eSGordon Ross 			ASSERT((size & (DBLK_CACHE_ALIGN - 1)) == 0);
2748329232eSGordon Ross 			tot_size = sizeof (dblk_t);
2758329232eSGordon Ross 		}
2768329232eSGordon Ross 
2778329232eSGordon Ross 		(void) sprintf(name, "streams_dblk_%ld", (long)size);
2788329232eSGordon Ross 		cp = kmem_cache_create(name, tot_size, DBLK_CACHE_ALIGN,
2798329232eSGordon Ross 		    dblk_constructor, dblk_destructor, NULL, (void *)(size),
2808329232eSGordon Ross 		    NULL, dblk_kmem_flags);
2818329232eSGordon Ross 
2828329232eSGordon Ross 		while (lastsize <= size) {
2838329232eSGordon Ross 			dblk_cache[(lastsize - 1) >> DBLK_SIZE_SHIFT] = cp;
2848329232eSGordon Ross 			lastsize += DBLK_MIN_SIZE;
2858329232eSGordon Ross 		}
2868329232eSGordon Ross 	}
2878329232eSGordon Ross 
2888329232eSGordon Ross 	dblk_esb_cache = kmem_cache_create("streams_dblk_esb", sizeof (dblk_t),
2898329232eSGordon Ross 	    DBLK_CACHE_ALIGN, dblk_esb_constructor, dblk_destructor, NULL,
2908329232eSGordon Ross 	    (void *)sizeof (dblk_t), NULL, dblk_kmem_flags);
2918329232eSGordon Ross 
292*9b664393SGarrett D'Amore 	/* fthdr_cache, ftblk_cache, ... */
2938329232eSGordon Ross }
2948329232eSGordon Ross 
2958329232eSGordon Ross /*ARGSUSED*/
2968329232eSGordon Ross mblk_t *
allocb(size_t size,uint_t pri)2978329232eSGordon Ross allocb(size_t size, uint_t pri)
2988329232eSGordon Ross {
2998329232eSGordon Ross 	dblk_t *dbp;
3008329232eSGordon Ross 	mblk_t *mp;
3018329232eSGordon Ross 	size_t index;
3028329232eSGordon Ross 
3038329232eSGordon Ross 	index =  (size - 1)  >> DBLK_SIZE_SHIFT;
3048329232eSGordon Ross 
3058329232eSGordon Ross 	if (index >= (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT)) {
3068329232eSGordon Ross 		if (size != 0) {
3078329232eSGordon Ross 			mp = allocb_oversize(size, KM_NOSLEEP);
3088329232eSGordon Ross 			goto out;
3098329232eSGordon Ross 		}
3108329232eSGordon Ross 		index = 0;
3118329232eSGordon Ross 	}
3128329232eSGordon Ross 
3138329232eSGordon Ross 	if ((dbp = kmem_cache_alloc(dblk_cache[index], KM_NOSLEEP)) == NULL) {
3148329232eSGordon Ross 		mp = NULL;
3158329232eSGordon Ross 		goto out;
3168329232eSGordon Ross 	}
3178329232eSGordon Ross 
3188329232eSGordon Ross 	mp = dbp->db_mblk;
3198329232eSGordon Ross 	DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
3208329232eSGordon Ross 	mp->b_next = mp->b_prev = mp->b_cont = NULL;
3218329232eSGordon Ross 	mp->b_rptr = mp->b_wptr = dbp->db_base;
3228329232eSGordon Ross 	mp->b_queue = NULL;
3238329232eSGordon Ross 	MBLK_BAND_FLAG_WORD(mp) = 0;
3248329232eSGordon Ross 	STR_FTALLOC(&dbp->db_fthdr, FTEV_ALLOCB, size);
3258329232eSGordon Ross out:
3268329232eSGordon Ross 	FTRACE_1("allocb(): mp=0x%p", (uintptr_t)mp);
3278329232eSGordon Ross 
3288329232eSGordon Ross 	return (mp);
3298329232eSGordon Ross }
3308329232eSGordon Ross 
3318329232eSGordon Ross /*
3328329232eSGordon Ross  * Allocate an mblk taking db_credp and db_cpid from the template.
3338329232eSGordon Ross  * Allow the cred to be NULL.
3348329232eSGordon Ross  */
3358329232eSGordon Ross mblk_t *
allocb_tmpl(size_t size,const mblk_t * tmpl)3368329232eSGordon Ross allocb_tmpl(size_t size, const mblk_t *tmpl)
3378329232eSGordon Ross {
3388329232eSGordon Ross 	mblk_t *mp = allocb(size, 0);
3398329232eSGordon Ross 
3408329232eSGordon Ross 	if (mp != NULL) {
3418329232eSGordon Ross 		dblk_t *src = tmpl->b_datap;
3428329232eSGordon Ross 		dblk_t *dst = mp->b_datap;
3438329232eSGordon Ross 		cred_t *cr;
3448329232eSGordon Ross 		pid_t cpid;
3458329232eSGordon Ross 
3468329232eSGordon Ross 		cr = msg_getcred(tmpl, &cpid);
3478329232eSGordon Ross 		if (cr != NULL)
3488329232eSGordon Ross 			crhold(dst->db_credp = cr);
3498329232eSGordon Ross 		dst->db_cpid = cpid;
3508329232eSGordon Ross 		dst->db_type = src->db_type;
3518329232eSGordon Ross 	}
3528329232eSGordon Ross 	return (mp);
3538329232eSGordon Ross }
3548329232eSGordon Ross 
3558329232eSGordon Ross mblk_t *
allocb_cred(size_t size,cred_t * cr,pid_t cpid)3568329232eSGordon Ross allocb_cred(size_t size, cred_t *cr, pid_t cpid)
3578329232eSGordon Ross {
3588329232eSGordon Ross 	mblk_t *mp = allocb(size, 0);
3598329232eSGordon Ross 
3608329232eSGordon Ross 	ASSERT(cr != NULL);
3618329232eSGordon Ross 	if (mp != NULL) {
3628329232eSGordon Ross 		dblk_t *dbp = mp->b_datap;
3638329232eSGordon Ross 
3648329232eSGordon Ross 		crhold(dbp->db_credp = cr);
3658329232eSGordon Ross 		dbp->db_cpid = cpid;
3668329232eSGordon Ross 	}
3678329232eSGordon Ross 	return (mp);
3688329232eSGordon Ross }
3698329232eSGordon Ross 
3708329232eSGordon Ross mblk_t *
allocb_cred_wait(size_t size,uint_t flags,int * error,cred_t * cr,pid_t cpid)3718329232eSGordon Ross allocb_cred_wait(size_t size, uint_t flags, int *error, cred_t *cr, pid_t cpid)
3728329232eSGordon Ross {
3738329232eSGordon Ross 	mblk_t *mp = allocb_wait(size, 0, flags, error);
3748329232eSGordon Ross 
3758329232eSGordon Ross 	ASSERT(cr != NULL);
3768329232eSGordon Ross 	if (mp != NULL) {
3778329232eSGordon Ross 		dblk_t *dbp = mp->b_datap;
3788329232eSGordon Ross 
3798329232eSGordon Ross 		crhold(dbp->db_credp = cr);
3808329232eSGordon Ross 		dbp->db_cpid = cpid;
3818329232eSGordon Ross 	}
3828329232eSGordon Ross 
3838329232eSGordon Ross 	return (mp);
3848329232eSGordon Ross }
3858329232eSGordon Ross 
3868329232eSGordon Ross /*
3878329232eSGordon Ross  * Extract the db_cred (and optionally db_cpid) from a message.
3888329232eSGordon Ross  * We find the first mblk which has a non-NULL db_cred and use that.
3898329232eSGordon Ross  * If none found we return NULL.
3908329232eSGordon Ross  * Does NOT get a hold on the cred.
3918329232eSGordon Ross  */
3928329232eSGordon Ross cred_t *
msg_getcred(const mblk_t * mp,pid_t * cpidp)3938329232eSGordon Ross msg_getcred(const mblk_t *mp, pid_t *cpidp)
3948329232eSGordon Ross {
3958329232eSGordon Ross 	cred_t *cr = NULL;
3968329232eSGordon Ross 
3978329232eSGordon Ross 	while (mp != NULL) {
3988329232eSGordon Ross 		dblk_t *dbp = mp->b_datap;
3998329232eSGordon Ross 
4008329232eSGordon Ross 		cr = dbp->db_credp;
4018329232eSGordon Ross 		if (cr == NULL) {
4028329232eSGordon Ross 			mp = mp->b_cont;
4038329232eSGordon Ross 			continue;
4048329232eSGordon Ross 		}
4058329232eSGordon Ross 		if (cpidp != NULL)
4068329232eSGordon Ross 			*cpidp = dbp->db_cpid;
4078329232eSGordon Ross 
4088329232eSGordon Ross 		/* DEBUG check for only one db_credp */
4098329232eSGordon Ross 		return (cr);
4108329232eSGordon Ross 	}
4118329232eSGordon Ross 	if (cpidp != NULL)
4128329232eSGordon Ross 		*cpidp = NOPID;
4138329232eSGordon Ross 	return (NULL);
4148329232eSGordon Ross }
4158329232eSGordon Ross 
4168329232eSGordon Ross /*
4178329232eSGordon Ross  * Variant of msg_getcred which, when a cred is found
4188329232eSGordon Ross  * 1. Returns with a hold on the cred
4198329232eSGordon Ross  * 2. Clears the first cred in the mblk.
4208329232eSGordon Ross  * This is more efficient to use than a msg_getcred() + crhold() when
4218329232eSGordon Ross  * the message is freed after the cred has been extracted.
4228329232eSGordon Ross  *
4238329232eSGordon Ross  * The caller is responsible for ensuring that there is no other reference
4248329232eSGordon Ross  * on the message since db_credp can not be cleared when there are other
4258329232eSGordon Ross  * references.
4268329232eSGordon Ross  */
4278329232eSGordon Ross cred_t *
msg_extractcred(mblk_t * mp,pid_t * cpidp)4288329232eSGordon Ross msg_extractcred(mblk_t *mp, pid_t *cpidp)
4298329232eSGordon Ross {
4308329232eSGordon Ross 	cred_t *cr = NULL;
4318329232eSGordon Ross 
4328329232eSGordon Ross 	while (mp != NULL) {
4338329232eSGordon Ross 		dblk_t *dbp = mp->b_datap;
4348329232eSGordon Ross 
4358329232eSGordon Ross 		cr = dbp->db_credp;
4368329232eSGordon Ross 		if (cr == NULL) {
4378329232eSGordon Ross 			mp = mp->b_cont;
4388329232eSGordon Ross 			continue;
4398329232eSGordon Ross 		}
4408329232eSGordon Ross 		ASSERT(dbp->db_ref == 1);
4418329232eSGordon Ross 		dbp->db_credp = NULL;
4428329232eSGordon Ross 		if (cpidp != NULL)
4438329232eSGordon Ross 			*cpidp = dbp->db_cpid;
4448329232eSGordon Ross 
4458329232eSGordon Ross 		/* DEBUG check for only one db_credp */
4468329232eSGordon Ross 		return (cr);
4478329232eSGordon Ross 	}
4488329232eSGordon Ross 	return (NULL);
4498329232eSGordon Ross }
4508329232eSGordon Ross 
4518329232eSGordon Ross /* _KERNEL msg_getlabel() */
4528329232eSGordon Ross 
4538329232eSGordon Ross void
freeb(mblk_t * mp)4548329232eSGordon Ross freeb(mblk_t *mp)
4558329232eSGordon Ross {
4568329232eSGordon Ross 	dblk_t *dbp = mp->b_datap;
4578329232eSGordon Ross 
4588329232eSGordon Ross 	ASSERT(dbp->db_ref > 0);
4598329232eSGordon Ross 	ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
4608329232eSGordon Ross 	FTRACE_1("freeb(): mp=0x%lx", (uintptr_t)mp);
4618329232eSGordon Ross 
4628329232eSGordon Ross 	STR_FTEVENT_MBLK(mp, caller(), FTEV_FREEB, dbp->db_ref);
4638329232eSGordon Ross 
4648329232eSGordon Ross 	dbp->db_free(mp, dbp);
4658329232eSGordon Ross }
4668329232eSGordon Ross 
4678329232eSGordon Ross void
freemsg(mblk_t * mp)4688329232eSGordon Ross freemsg(mblk_t *mp)
4698329232eSGordon Ross {
4708329232eSGordon Ross 	FTRACE_1("freemsg(): mp=0x%lx", (uintptr_t)mp);
4718329232eSGordon Ross 	while (mp) {
4728329232eSGordon Ross 		dblk_t *dbp = mp->b_datap;
4738329232eSGordon Ross 		mblk_t *mp_cont = mp->b_cont;
4748329232eSGordon Ross 
4758329232eSGordon Ross 		ASSERT(dbp->db_ref > 0);
4768329232eSGordon Ross 		ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
4778329232eSGordon Ross 
4788329232eSGordon Ross 		STR_FTEVENT_MBLK(mp, caller(), FTEV_FREEB, dbp->db_ref);
4798329232eSGordon Ross 
4808329232eSGordon Ross 		dbp->db_free(mp, dbp);
4818329232eSGordon Ross 		mp = mp_cont;
4828329232eSGordon Ross 	}
4838329232eSGordon Ross }
4848329232eSGordon Ross 
4858329232eSGordon Ross /*
4868329232eSGordon Ross  * Reallocate a block for another use.  Try hard to use the old block.
4878329232eSGordon Ross  * If the old data is wanted (copy), leave b_wptr at the end of the data,
4888329232eSGordon Ross  * otherwise return b_wptr = b_rptr.
4898329232eSGordon Ross  *
4908329232eSGordon Ross  * This routine is private and unstable.
4918329232eSGordon Ross  */
4928329232eSGordon Ross mblk_t	*
reallocb(mblk_t * mp,size_t size,uint_t copy)4938329232eSGordon Ross reallocb(mblk_t *mp, size_t size, uint_t copy)
4948329232eSGordon Ross {
4958329232eSGordon Ross 	mblk_t		*mp1;
4968329232eSGordon Ross 	unsigned char	*old_rptr;
4978329232eSGordon Ross 	ptrdiff_t	cur_size;
4988329232eSGordon Ross 
4998329232eSGordon Ross 	if (mp == NULL)
5008329232eSGordon Ross 		return (allocb(size, BPRI_HI));
5018329232eSGordon Ross 
5028329232eSGordon Ross 	cur_size = mp->b_wptr - mp->b_rptr;
5038329232eSGordon Ross 	old_rptr = mp->b_rptr;
5048329232eSGordon Ross 
5058329232eSGordon Ross 	ASSERT(mp->b_datap->db_ref != 0);
5068329232eSGordon Ross 
5078329232eSGordon Ross 	if (mp->b_datap->db_ref == 1 && MBLKSIZE(mp) >= size) {
5088329232eSGordon Ross 		/*
5098329232eSGordon Ross 		 * If the data is wanted and it will fit where it is, no
5108329232eSGordon Ross 		 * work is required.
5118329232eSGordon Ross 		 */
5128329232eSGordon Ross 		if (copy && mp->b_datap->db_lim - mp->b_rptr >= size)
5138329232eSGordon Ross 			return (mp);
5148329232eSGordon Ross 
5158329232eSGordon Ross 		mp->b_wptr = mp->b_rptr = mp->b_datap->db_base;
5168329232eSGordon Ross 		mp1 = mp;
5178329232eSGordon Ross 	} else if ((mp1 = allocb_tmpl(size, mp)) != NULL) {
5188329232eSGordon Ross 		/* XXX other mp state could be copied too, db_flags ... ? */
5198329232eSGordon Ross 		mp1->b_cont = mp->b_cont;
5208329232eSGordon Ross 	} else {
5218329232eSGordon Ross 		return (NULL);
5228329232eSGordon Ross 	}
5238329232eSGordon Ross 
5248329232eSGordon Ross 	if (copy) {
5258329232eSGordon Ross 		bcopy(old_rptr, mp1->b_rptr, cur_size);
5268329232eSGordon Ross 		mp1->b_wptr = mp1->b_rptr + cur_size;
5278329232eSGordon Ross 	}
5288329232eSGordon Ross 
5298329232eSGordon Ross 	if (mp != mp1)
5308329232eSGordon Ross 		freeb(mp);
5318329232eSGordon Ross 
5328329232eSGordon Ross 	return (mp1);
5338329232eSGordon Ross }
5348329232eSGordon Ross 
5358329232eSGordon Ross static void
dblk_lastfree(mblk_t * mp,dblk_t * dbp)5368329232eSGordon Ross dblk_lastfree(mblk_t *mp, dblk_t *dbp)
5378329232eSGordon Ross {
5388329232eSGordon Ross 	ASSERT(dbp->db_mblk == mp);
5398329232eSGordon Ross 	if (dbp->db_fthdr != NULL)
5408329232eSGordon Ross 		str_ftfree(dbp);
5418329232eSGordon Ross 
5428329232eSGordon Ross 	/* set credp and projid to be 'unspecified' before returning to cache */
5438329232eSGordon Ross 	if (dbp->db_credp != NULL) {
5448329232eSGordon Ross 		crfree(dbp->db_credp);
5458329232eSGordon Ross 		dbp->db_credp = NULL;
5468329232eSGordon Ross 	}
5478329232eSGordon Ross 	dbp->db_cpid = -1;
5488329232eSGordon Ross 
5498329232eSGordon Ross 	/* Reset the struioflag and the checksum flag fields */
5508329232eSGordon Ross 	dbp->db_struioflag = 0;
5518329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
5528329232eSGordon Ross 
5538329232eSGordon Ross 	/* and the COOKED and/or UIOA flag(s) */
5548329232eSGordon Ross 	dbp->db_flags &= ~(DBLK_COOKED | DBLK_UIOA);
5558329232eSGordon Ross 
5568329232eSGordon Ross 	kmem_cache_free(dbp->db_cache, dbp);
5578329232eSGordon Ross }
5588329232eSGordon Ross 
5598329232eSGordon Ross static void
dblk_decref(mblk_t * mp,dblk_t * dbp)5608329232eSGordon Ross dblk_decref(mblk_t *mp, dblk_t *dbp)
5618329232eSGordon Ross {
5628329232eSGordon Ross 	if (dbp->db_ref != 1) {
5638329232eSGordon Ross 		uint32_t rtfu = atomic_add_32_nv(&DBLK_RTFU_WORD(dbp),
5648329232eSGordon Ross 		    -(1 << DBLK_RTFU_SHIFT(db_ref)));
5658329232eSGordon Ross 		/*
5668329232eSGordon Ross 		 * atomic_add_32_nv() just decremented db_ref, so we no longer
5678329232eSGordon Ross 		 * have a reference to the dblk, which means another thread
5688329232eSGordon Ross 		 * could free it.  Therefore we cannot examine the dblk to
5698329232eSGordon Ross 		 * determine whether ours was the last reference.  Instead,
5708329232eSGordon Ross 		 * we extract the new and minimum reference counts from rtfu.
5718329232eSGordon Ross 		 * Note that all we're really saying is "if (ref != refmin)".
5728329232eSGordon Ross 		 */
5738329232eSGordon Ross 		if (((rtfu >> DBLK_RTFU_SHIFT(db_ref)) & DBLK_REFMAX) !=
5748329232eSGordon Ross 		    ((rtfu >> DBLK_RTFU_SHIFT(db_flags)) & DBLK_REFMIN)) {
5758329232eSGordon Ross 			kmem_cache_free(mblk_cache, mp);
5768329232eSGordon Ross 			return;
5778329232eSGordon Ross 		}
5788329232eSGordon Ross 	}
5798329232eSGordon Ross 	dbp->db_mblk = mp;
5808329232eSGordon Ross 	dbp->db_free = dbp->db_lastfree;
5818329232eSGordon Ross 	dbp->db_lastfree(mp, dbp);
5828329232eSGordon Ross }
5838329232eSGordon Ross 
5848329232eSGordon Ross mblk_t *
dupb(mblk_t * mp)5858329232eSGordon Ross dupb(mblk_t *mp)
5868329232eSGordon Ross {
5878329232eSGordon Ross 	dblk_t *dbp = mp->b_datap;
5888329232eSGordon Ross 	mblk_t *new_mp;
5898329232eSGordon Ross 	uint32_t oldrtfu, newrtfu;
5908329232eSGordon Ross 
5918329232eSGordon Ross 	if ((new_mp = kmem_cache_alloc(mblk_cache, KM_NOSLEEP)) == NULL)
5928329232eSGordon Ross 		goto out;
5938329232eSGordon Ross 
5948329232eSGordon Ross 	new_mp->b_next = new_mp->b_prev = new_mp->b_cont = NULL;
5958329232eSGordon Ross 	new_mp->b_rptr = mp->b_rptr;
5968329232eSGordon Ross 	new_mp->b_wptr = mp->b_wptr;
5978329232eSGordon Ross 	new_mp->b_datap = dbp;
5988329232eSGordon Ross 	new_mp->b_queue = NULL;
5998329232eSGordon Ross 	MBLK_BAND_FLAG_WORD(new_mp) = MBLK_BAND_FLAG_WORD(mp);
6008329232eSGordon Ross 
6018329232eSGordon Ross 	STR_FTEVENT_MBLK(mp, caller(), FTEV_DUPB, dbp->db_ref);
6028329232eSGordon Ross 
6038329232eSGordon Ross 	dbp->db_free = dblk_decref;
6048329232eSGordon Ross 	do {
6058329232eSGordon Ross 		ASSERT(dbp->db_ref > 0);
6068329232eSGordon Ross 		oldrtfu = DBLK_RTFU_WORD(dbp);
6078329232eSGordon Ross 		newrtfu = oldrtfu + (1 << DBLK_RTFU_SHIFT(db_ref));
6088329232eSGordon Ross 		/*
6098329232eSGordon Ross 		 * If db_ref is maxed out we can't dup this message anymore.
6108329232eSGordon Ross 		 */
6118329232eSGordon Ross 		if ((oldrtfu & DBLK_RTFU_REF_MASK) == DBLK_RTFU_REF_MASK) {
6128329232eSGordon Ross 			kmem_cache_free(mblk_cache, new_mp);
6138329232eSGordon Ross 			new_mp = NULL;
6148329232eSGordon Ross 			goto out;
6158329232eSGordon Ross 		}
6168329232eSGordon Ross 	} while (atomic_cas_32(&DBLK_RTFU_WORD(dbp), oldrtfu, newrtfu) !=
6178329232eSGordon Ross 	    oldrtfu);
6188329232eSGordon Ross 
6198329232eSGordon Ross out:
6208329232eSGordon Ross 	FTRACE_1("dupb(): new_mp=0x%lx", (uintptr_t)new_mp);
6218329232eSGordon Ross 	return (new_mp);
6228329232eSGordon Ross }
6238329232eSGordon Ross 
6248329232eSGordon Ross /*ARGSUSED*/
6258329232eSGordon Ross static void
frnop_func(void * arg)6268329232eSGordon Ross frnop_func(void *arg)
6278329232eSGordon Ross {
6288329232eSGordon Ross }
6298329232eSGordon Ross 
6308329232eSGordon Ross /*
6318329232eSGordon Ross  * Generic esballoc used to implement the four flavors: [d]esballoc[a].
6328329232eSGordon Ross  * and allocb_oversize
6338329232eSGordon Ross  */
6348329232eSGordon Ross 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)6358329232eSGordon Ross gesballoc(unsigned char *base, size_t size, uint32_t db_rtfu, frtn_t *frp,
636*9b664393SGarrett D'Amore     void (*lastfree)(mblk_t *, dblk_t *), int kmflags)
6378329232eSGordon Ross {
6388329232eSGordon Ross 	dblk_t *dbp;
6398329232eSGordon Ross 	mblk_t *mp;
6408329232eSGordon Ross 
6418329232eSGordon Ross 	ASSERT(base != NULL && frp != NULL);
6428329232eSGordon Ross 
6438329232eSGordon Ross 	if ((dbp = kmem_cache_alloc(dblk_esb_cache, kmflags)) == NULL) {
6448329232eSGordon Ross 		mp = NULL;
6458329232eSGordon Ross 		goto out;
6468329232eSGordon Ross 	}
6478329232eSGordon Ross 
6488329232eSGordon Ross 	mp = dbp->db_mblk;
6498329232eSGordon Ross 	dbp->db_base = base;
6508329232eSGordon Ross 	dbp->db_lim = base + size;
6518329232eSGordon Ross 	dbp->db_free = dbp->db_lastfree = lastfree;
6528329232eSGordon Ross 	dbp->db_frtnp = frp;
6538329232eSGordon Ross 	DBLK_RTFU_WORD(dbp) = db_rtfu;
6548329232eSGordon Ross 	mp->b_next = mp->b_prev = mp->b_cont = NULL;
6558329232eSGordon Ross 	mp->b_rptr = mp->b_wptr = base;
6568329232eSGordon Ross 	mp->b_queue = NULL;
6578329232eSGordon Ross 	MBLK_BAND_FLAG_WORD(mp) = 0;
6588329232eSGordon Ross 
6598329232eSGordon Ross out:
6608329232eSGordon Ross 	FTRACE_1("gesballoc(): mp=0x%lx", (uintptr_t)mp);
6618329232eSGordon Ross 	return (mp);
6628329232eSGordon Ross }
6638329232eSGordon Ross 
6648329232eSGordon Ross static void
bcache_dblk_lastfree(mblk_t * mp,dblk_t * dbp)6658329232eSGordon Ross bcache_dblk_lastfree(mblk_t *mp, dblk_t *dbp)
6668329232eSGordon Ross {
6678329232eSGordon Ross 	bcache_t *bcp = dbp->db_cache;
6688329232eSGordon Ross 
6698329232eSGordon Ross 	ASSERT(dbp->db_mblk == mp);
6708329232eSGordon Ross 	if (dbp->db_fthdr != NULL)
6718329232eSGordon Ross 		str_ftfree(dbp);
6728329232eSGordon Ross 
6738329232eSGordon Ross 	/* set credp and projid to be 'unspecified' before returning to cache */
6748329232eSGordon Ross 	if (dbp->db_credp != NULL) {
6758329232eSGordon Ross 		crfree(dbp->db_credp);
6768329232eSGordon Ross 		dbp->db_credp = NULL;
6778329232eSGordon Ross 	}
6788329232eSGordon Ross 	dbp->db_cpid = -1;
6798329232eSGordon Ross 	dbp->db_struioflag = 0;
6808329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
6818329232eSGordon Ross 
6828329232eSGordon Ross 	mutex_enter(&bcp->mutex);
6838329232eSGordon Ross 	kmem_cache_free(bcp->dblk_cache, dbp);
6848329232eSGordon Ross 	bcp->alloc--;
6858329232eSGordon Ross 
6868329232eSGordon Ross 	if (bcp->alloc == 0 && bcp->destroy != 0) {
6878329232eSGordon Ross 		kmem_cache_destroy(bcp->dblk_cache);
6888329232eSGordon Ross 		kmem_cache_destroy(bcp->buffer_cache);
6898329232eSGordon Ross 		mutex_exit(&bcp->mutex);
6908329232eSGordon Ross 		mutex_destroy(&bcp->mutex);
6918329232eSGordon Ross 		kmem_free(bcp, sizeof (bcache_t));
6928329232eSGordon Ross 	} else {
6938329232eSGordon Ross 		mutex_exit(&bcp->mutex);
6948329232eSGordon Ross 	}
6958329232eSGordon Ross }
6968329232eSGordon Ross 
6978329232eSGordon Ross bcache_t *
bcache_create(char * name,size_t size,uint_t align)6988329232eSGordon Ross bcache_create(char *name, size_t size, uint_t align)
6998329232eSGordon Ross {
7008329232eSGordon Ross 	bcache_t *bcp;
7018329232eSGordon Ross 	char buffer[255];
7028329232eSGordon Ross 
7038329232eSGordon Ross 	ASSERT((align & (align - 1)) == 0);
7048329232eSGordon Ross 
7058329232eSGordon Ross 	if ((bcp = kmem_alloc(sizeof (bcache_t), KM_NOSLEEP)) == NULL)
7068329232eSGordon Ross 		return (NULL);
7078329232eSGordon Ross 
7088329232eSGordon Ross 	bcp->size = size;
7098329232eSGordon Ross 	bcp->align = align;
7108329232eSGordon Ross 	bcp->alloc = 0;
7118329232eSGordon Ross 	bcp->destroy = 0;
7128329232eSGordon Ross 
7138329232eSGordon Ross 	mutex_init(&bcp->mutex, NULL, MUTEX_DRIVER, NULL);
7148329232eSGordon Ross 
7158329232eSGordon Ross 	(void) sprintf(buffer, "%s_buffer_cache", name);
7168329232eSGordon Ross 	bcp->buffer_cache = kmem_cache_create(buffer, size, align, NULL, NULL,
7178329232eSGordon Ross 	    NULL, NULL, NULL, 0);
7188329232eSGordon Ross 	(void) sprintf(buffer, "%s_dblk_cache", name);
7198329232eSGordon Ross 	bcp->dblk_cache = kmem_cache_create(buffer, sizeof (dblk_t),
7208329232eSGordon Ross 	    DBLK_CACHE_ALIGN, bcache_dblk_constructor, bcache_dblk_destructor,
7218329232eSGordon Ross 	    NULL, (void *)bcp, NULL, 0);
7228329232eSGordon Ross 
7238329232eSGordon Ross 	return (bcp);
7248329232eSGordon Ross }
7258329232eSGordon Ross 
7268329232eSGordon Ross void
bcache_destroy(bcache_t * bcp)7278329232eSGordon Ross bcache_destroy(bcache_t *bcp)
7288329232eSGordon Ross {
7298329232eSGordon Ross 	ASSERT(bcp != NULL);
7308329232eSGordon Ross 
7318329232eSGordon Ross 	mutex_enter(&bcp->mutex);
7328329232eSGordon Ross 	if (bcp->alloc == 0) {
7338329232eSGordon Ross 		kmem_cache_destroy(bcp->dblk_cache);
7348329232eSGordon Ross 		kmem_cache_destroy(bcp->buffer_cache);
7358329232eSGordon Ross 		mutex_exit(&bcp->mutex);
7368329232eSGordon Ross 		mutex_destroy(&bcp->mutex);
7378329232eSGordon Ross 		kmem_free(bcp, sizeof (bcache_t));
7388329232eSGordon Ross 	} else {
7398329232eSGordon Ross 		bcp->destroy++;
7408329232eSGordon Ross 		mutex_exit(&bcp->mutex);
7418329232eSGordon Ross 	}
7428329232eSGordon Ross }
7438329232eSGordon Ross 
7448329232eSGordon Ross /*ARGSUSED*/
7458329232eSGordon Ross mblk_t *
bcache_allocb(bcache_t * bcp,uint_t pri)7468329232eSGordon Ross bcache_allocb(bcache_t *bcp, uint_t pri)
7478329232eSGordon Ross {
7488329232eSGordon Ross 	dblk_t *dbp;
7498329232eSGordon Ross 	mblk_t *mp = NULL;
7508329232eSGordon Ross 
7518329232eSGordon Ross 	ASSERT(bcp != NULL);
7528329232eSGordon Ross 
7538329232eSGordon Ross 	mutex_enter(&bcp->mutex);
7548329232eSGordon Ross 	if (bcp->destroy != 0) {
7558329232eSGordon Ross 		mutex_exit(&bcp->mutex);
7568329232eSGordon Ross 		goto out;
7578329232eSGordon Ross 	}
7588329232eSGordon Ross 
7598329232eSGordon Ross 	if ((dbp = kmem_cache_alloc(bcp->dblk_cache, KM_NOSLEEP)) == NULL) {
7608329232eSGordon Ross 		mutex_exit(&bcp->mutex);
7618329232eSGordon Ross 		goto out;
7628329232eSGordon Ross 	}
7638329232eSGordon Ross 	bcp->alloc++;
7648329232eSGordon Ross 	mutex_exit(&bcp->mutex);
7658329232eSGordon Ross 
7668329232eSGordon Ross 	ASSERT(((uintptr_t)(dbp->db_base) & (bcp->align - 1)) == 0);
7678329232eSGordon Ross 
7688329232eSGordon Ross 	mp = dbp->db_mblk;
7698329232eSGordon Ross 	DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
7708329232eSGordon Ross 	mp->b_next = mp->b_prev = mp->b_cont = NULL;
7718329232eSGordon Ross 	mp->b_rptr = mp->b_wptr = dbp->db_base;
7728329232eSGordon Ross 	mp->b_queue = NULL;
7738329232eSGordon Ross 	MBLK_BAND_FLAG_WORD(mp) = 0;
7748329232eSGordon Ross 	STR_FTALLOC(&dbp->db_fthdr, FTEV_BCALLOCB, bcp->size);
7758329232eSGordon Ross out:
7768329232eSGordon Ross 	FTRACE_1("bcache_allocb(): mp=0x%p", (uintptr_t)mp);
7778329232eSGordon Ross 
7788329232eSGordon Ross 	return (mp);
7798329232eSGordon Ross }
7808329232eSGordon Ross 
7818329232eSGordon Ross static void
dblk_lastfree_oversize(mblk_t * mp,dblk_t * dbp)7828329232eSGordon Ross dblk_lastfree_oversize(mblk_t *mp, dblk_t *dbp)
7838329232eSGordon Ross {
7848329232eSGordon Ross 	ASSERT(dbp->db_mblk == mp);
7858329232eSGordon Ross 	if (dbp->db_fthdr != NULL)
7868329232eSGordon Ross 		str_ftfree(dbp);
7878329232eSGordon Ross 
7888329232eSGordon Ross 	/* set credp and projid to be 'unspecified' before returning to cache */
7898329232eSGordon Ross 	if (dbp->db_credp != NULL) {
7908329232eSGordon Ross 		crfree(dbp->db_credp);
7918329232eSGordon Ross 		dbp->db_credp = NULL;
7928329232eSGordon Ross 	}
7938329232eSGordon Ross 	dbp->db_cpid = -1;
7948329232eSGordon Ross 	dbp->db_struioflag = 0;
7958329232eSGordon Ross 	dbp->db_struioun.cksum.flags = 0;
7968329232eSGordon Ross 
7978329232eSGordon Ross 	kmem_free(dbp->db_base, dbp->db_lim - dbp->db_base);
7988329232eSGordon Ross 	kmem_cache_free(dbp->db_cache, dbp);
7998329232eSGordon Ross }
8008329232eSGordon Ross 
8018329232eSGordon Ross static mblk_t *
allocb_oversize(size_t size,int kmflags)8028329232eSGordon Ross allocb_oversize(size_t size, int kmflags)
8038329232eSGordon Ross {
8048329232eSGordon Ross 	mblk_t *mp;
8058329232eSGordon Ross 	void *buf;
8068329232eSGordon Ross 
8078329232eSGordon Ross 	size = P2ROUNDUP(size, DBLK_CACHE_ALIGN);
8088329232eSGordon Ross 	if ((buf = kmem_alloc(size, kmflags)) == NULL)
8098329232eSGordon Ross 		return (NULL);
8108329232eSGordon Ross 	if ((mp = gesballoc(buf, size, DBLK_RTFU(1, M_DATA, 0, 0),
8118329232eSGordon Ross 	    &frnop, dblk_lastfree_oversize, kmflags)) == NULL)
8128329232eSGordon Ross 		kmem_free(buf, size);
8138329232eSGordon Ross 
8148329232eSGordon Ross 	if (mp != NULL)
8158329232eSGordon Ross 		STR_FTALLOC(&DB_FTHDR(mp), FTEV_ALLOCBIG, size);
8168329232eSGordon Ross 
8178329232eSGordon Ross 	return (mp);
8188329232eSGordon Ross }
8198329232eSGordon Ross 
8208329232eSGordon Ross mblk_t *
allocb_tryhard(size_t target_size)8218329232eSGordon Ross allocb_tryhard(size_t target_size)
8228329232eSGordon Ross {
8238329232eSGordon Ross 	size_t size;
8248329232eSGordon Ross 	mblk_t *bp;
8258329232eSGordon Ross 
8268329232eSGordon Ross 	for (size = target_size; size < target_size + 512;
8278329232eSGordon Ross 	    size += DBLK_CACHE_ALIGN)
8288329232eSGordon Ross 		if ((bp = allocb(size, BPRI_HI)) != NULL)
8298329232eSGordon Ross 			return (bp);
8308329232eSGordon Ross 	allocb_tryhard_fails++;
8318329232eSGordon Ross 	return (NULL);
8328329232eSGordon Ross }
8338329232eSGordon Ross 
8348329232eSGordon Ross /*
8358329232eSGordon Ross  * This routine is consolidation private for STREAMS internal use
8368329232eSGordon Ross  * This routine may only be called from sync routines (i.e., not
8378329232eSGordon Ross  * from put or service procedures).  It is located here (rather
8388329232eSGordon Ross  * than strsubr.c) so that we don't have to expose all of the
8398329232eSGordon Ross  * allocb() implementation details in header files.
8408329232eSGordon Ross  */
8418329232eSGordon Ross mblk_t *
allocb_wait(size_t size,uint_t pri,uint_t flags,int * error)8428329232eSGordon Ross allocb_wait(size_t size, uint_t pri, uint_t flags, int *error)
8438329232eSGordon Ross {
8448329232eSGordon Ross 	dblk_t *dbp;
8458329232eSGordon Ross 	mblk_t *mp;
8468329232eSGordon Ross 	size_t index;
8478329232eSGordon Ross 
8488329232eSGordon Ross 	index = (size -1) >> DBLK_SIZE_SHIFT;
8498329232eSGordon Ross 
8508329232eSGordon Ross 	if (flags & STR_NOSIG) {
8518329232eSGordon Ross 		if (index >= (DBLK_MAX_CACHE >> DBLK_SIZE_SHIFT)) {
8528329232eSGordon Ross 			if (size != 0) {
8538329232eSGordon Ross 				mp = allocb_oversize(size, KM_SLEEP);
8548329232eSGordon Ross 				FTRACE_1("allocb_wait (NOSIG): mp=0x%lx",
8558329232eSGordon Ross 				    (uintptr_t)mp);
8568329232eSGordon Ross 				return (mp);
8578329232eSGordon Ross 			}
8588329232eSGordon Ross 			index = 0;
8598329232eSGordon Ross 		}
8608329232eSGordon Ross 
8618329232eSGordon Ross 		dbp = kmem_cache_alloc(dblk_cache[index], KM_SLEEP);
8628329232eSGordon Ross 		mp = dbp->db_mblk;
8638329232eSGordon Ross 		DBLK_RTFU_WORD(dbp) = DBLK_RTFU(1, M_DATA, 0, 0);
8648329232eSGordon Ross 		mp->b_next = mp->b_prev = mp->b_cont = NULL;
8658329232eSGordon Ross 		mp->b_rptr = mp->b_wptr = dbp->db_base;
8668329232eSGordon Ross 		mp->b_queue = NULL;
8678329232eSGordon Ross 		MBLK_BAND_FLAG_WORD(mp) = 0;
8688329232eSGordon Ross 		STR_FTALLOC(&DB_FTHDR(mp), FTEV_ALLOCBW, size);
8698329232eSGordon Ross 
8708329232eSGordon Ross 		FTRACE_1("allocb_wait (NOSIG): mp=0x%lx", (uintptr_t)mp);
8718329232eSGordon Ross 
8728329232eSGordon Ross 	} else {
8738329232eSGordon Ross 		while ((mp = allocb(size, pri)) == NULL) {
8748329232eSGordon Ross 			if ((*error = strwaitbuf(size, BPRI_HI)) != 0)
8758329232eSGordon Ross 				return (NULL);
8768329232eSGordon Ross 		}
8778329232eSGordon Ross 	}
8788329232eSGordon Ross 
8798329232eSGordon Ross 	return (mp);
8808329232eSGordon Ross }
8818329232eSGordon Ross 
8828329232eSGordon Ross /*
8838329232eSGordon Ross  * Call function 'func' with 'arg' when a class zero block can
8848329232eSGordon Ross  * be allocated with priority 'pri'.
8858329232eSGordon Ross  */
8868329232eSGordon Ross bufcall_id_t
esbbcall(uint_t pri,void (* func)(void *),void * arg)8878329232eSGordon Ross esbbcall(uint_t pri, void (*func)(void *), void *arg)
8888329232eSGordon Ross {
8898329232eSGordon Ross 	return (bufcall(1, pri, func, arg));
8908329232eSGordon Ross }
8918329232eSGordon Ross 
8928329232eSGordon Ross /*
8938329232eSGordon Ross  * Allocates an iocblk (M_IOCTL) block. Properly sets the credentials
8948329232eSGordon Ross  * ioc_id, rval and error of the struct ioctl to set up an ioctl call.
8958329232eSGordon Ross  * This provides consistency for all internal allocators of ioctl.
8968329232eSGordon Ross  */
8978329232eSGordon Ross mblk_t *
mkiocb(uint_t cmd)8988329232eSGordon Ross mkiocb(uint_t cmd)
8998329232eSGordon Ross {
9008329232eSGordon Ross 	struct iocblk	*ioc;
9018329232eSGordon Ross 	mblk_t		*mp;
9028329232eSGordon Ross 
9038329232eSGordon Ross 	/*
9048329232eSGordon Ross 	 * Allocate enough space for any of the ioctl related messages.
9058329232eSGordon Ross 	 */
9068329232eSGordon Ross 	if ((mp = allocb(sizeof (union ioctypes), BPRI_MED)) == NULL)
9078329232eSGordon Ross 		return (NULL);
9088329232eSGordon Ross 
9098329232eSGordon Ross 	bzero(mp->b_rptr, sizeof (union ioctypes));
9108329232eSGordon Ross 
9118329232eSGordon Ross 	/*
9128329232eSGordon Ross 	 * Set the mblk_t information and ptrs correctly.
9138329232eSGordon Ross 	 */
9148329232eSGordon Ross 	mp->b_wptr += sizeof (struct iocblk);
9158329232eSGordon Ross 	mp->b_datap->db_type = M_IOCTL;
9168329232eSGordon Ross 
9178329232eSGordon Ross 	/*
9188329232eSGordon Ross 	 * Fill in the fields.
9198329232eSGordon Ross 	 */
9208329232eSGordon Ross 	ioc		= (struct iocblk *)mp->b_rptr;
9218329232eSGordon Ross 	ioc->ioc_cmd	= cmd;
9228329232eSGordon Ross 	ioc->ioc_cr	= kcred;
9238329232eSGordon Ross 	ioc->ioc_id	= getiocseqno();
9248329232eSGordon Ross 	ioc->ioc_flag	= IOC_NATIVE;
9258329232eSGordon Ross 	return (mp);
9268329232eSGordon Ross }
9278329232eSGordon Ross 
9288329232eSGordon Ross /*
9298329232eSGordon Ross  * test if block of given size can be allocated with a request of
9308329232eSGordon Ross  * the given priority.
9318329232eSGordon Ross  * 'pri' is no longer used, but is retained for compatibility.
9328329232eSGordon Ross  */
9338329232eSGordon Ross /* ARGSUSED */
9348329232eSGordon Ross int
testb(size_t size,uint_t pri)9358329232eSGordon Ross testb(size_t size, uint_t pri)
9368329232eSGordon Ross {
9378329232eSGordon Ross 	return ((size + sizeof (dblk_t)) <= kmem_avail());
9388329232eSGordon Ross }
9398329232eSGordon Ross 
9408329232eSGordon Ross /* _KERNEL: bufcall, unbufcall */
9418329232eSGordon Ross 
9428329232eSGordon Ross /*
9438329232eSGordon Ross  * Duplicate a message block by block (uses dupb), returning
9448329232eSGordon Ross  * a pointer to the duplicate message.
9458329232eSGordon Ross  * Returns a non-NULL value only if the entire message
9468329232eSGordon Ross  * was dup'd.
9478329232eSGordon Ross  */
9488329232eSGordon Ross mblk_t *
dupmsg(mblk_t * bp)9498329232eSGordon Ross dupmsg(mblk_t *bp)
9508329232eSGordon Ross {
9518329232eSGordon Ross 	mblk_t *head, *nbp;
9528329232eSGordon Ross 
9538329232eSGordon Ross 	if (!bp || !(nbp = head = dupb(bp)))
9548329232eSGordon Ross 		return (NULL);
9558329232eSGordon Ross 
9568329232eSGordon Ross 	while (bp->b_cont) {
9578329232eSGordon Ross 		if (!(nbp->b_cont = dupb(bp->b_cont))) {
9588329232eSGordon Ross 			freemsg(head);
9598329232eSGordon Ross 			return (NULL);
9608329232eSGordon Ross 		}
9618329232eSGordon Ross 		nbp = nbp->b_cont;
9628329232eSGordon Ross 		bp = bp->b_cont;
9638329232eSGordon Ross 	}
9648329232eSGordon Ross 	return (head);
9658329232eSGordon Ross }
9668329232eSGordon Ross 
9678329232eSGordon Ross #define	DUPB_NOLOAN(bp) \
9688329232eSGordon Ross 	((((bp)->b_datap->db_struioflag & STRUIO_ZC) != 0) ? \
9698329232eSGordon Ross 	copyb((bp)) : dupb((bp)))
9708329232eSGordon Ross 
9718329232eSGordon Ross mblk_t *
dupmsg_noloan(mblk_t * bp)9728329232eSGordon Ross dupmsg_noloan(mblk_t *bp)
9738329232eSGordon Ross {
9748329232eSGordon Ross 	mblk_t *head, *nbp;
9758329232eSGordon Ross 
9768329232eSGordon Ross 	if (bp == NULL || DB_TYPE(bp) != M_DATA ||
9778329232eSGordon Ross 	    ((nbp = head = DUPB_NOLOAN(bp)) == NULL))
9788329232eSGordon Ross 		return (NULL);
9798329232eSGordon Ross 
9808329232eSGordon Ross 	while (bp->b_cont) {
9818329232eSGordon Ross 		if ((nbp->b_cont = DUPB_NOLOAN(bp->b_cont)) == NULL) {
9828329232eSGordon Ross 			freemsg(head);
9838329232eSGordon Ross 			return (NULL);
9848329232eSGordon Ross 		}
9858329232eSGordon Ross 		nbp = nbp->b_cont;
9868329232eSGordon Ross 		bp = bp->b_cont;
9878329232eSGordon Ross 	}
9888329232eSGordon Ross 	return (head);
9898329232eSGordon Ross }
9908329232eSGordon Ross 
9918329232eSGordon Ross /*
9928329232eSGordon Ross  * Copy data from message and data block to newly allocated message and
9938329232eSGordon Ross  * data block. Returns new message block pointer, or NULL if error.
9948329232eSGordon Ross  * The alignment of rptr (w.r.t. word alignment) will be the same in the copy
9958329232eSGordon Ross  * as in the original even when db_base is not word aligned. (bug 1052877)
9968329232eSGordon Ross  */
9978329232eSGordon Ross mblk_t *
copyb(mblk_t * bp)9988329232eSGordon Ross copyb(mblk_t *bp)
9998329232eSGordon Ross {
10008329232eSGordon Ross 	mblk_t	*nbp;
10018329232eSGordon Ross 	dblk_t	*dp, *ndp;
10028329232eSGordon Ross 	uchar_t *base;
10038329232eSGordon Ross 	size_t	size;
10048329232eSGordon Ross 	size_t	unaligned;
10058329232eSGordon Ross 
10068329232eSGordon Ross 	ASSERT(bp->b_wptr >= bp->b_rptr);
10078329232eSGordon Ross 
10088329232eSGordon Ross 	dp = bp->b_datap;
10098329232eSGordon Ross 	if (dp->db_fthdr != NULL)
10108329232eSGordon Ross 		STR_FTEVENT_MBLK(bp, caller(), FTEV_COPYB, 0);
10118329232eSGordon Ross 
10128329232eSGordon Ross 	size = dp->db_lim - dp->db_base;
10138329232eSGordon Ross 	unaligned = P2PHASE((uintptr_t)dp->db_base, sizeof (uint_t));
10148329232eSGordon Ross 	if ((nbp = allocb_tmpl(size + unaligned, bp)) == NULL)
10158329232eSGordon Ross 		return (NULL);
10168329232eSGordon Ross 	nbp->b_flag = bp->b_flag;
10178329232eSGordon Ross 	nbp->b_band = bp->b_band;
10188329232eSGordon Ross 	ndp = nbp->b_datap;
10198329232eSGordon Ross 
10208329232eSGordon Ross 	/*
10218329232eSGordon Ross 	 * Well, here is a potential issue.  If we are trying to
10228329232eSGordon Ross 	 * trace a flow, and we copy the message, we might lose
10238329232eSGordon Ross 	 * information about where this message might have been.
10248329232eSGordon Ross 	 * So we should inherit the FT data.  On the other hand,
10258329232eSGordon Ross 	 * a user might be interested only in alloc to free data.
10268329232eSGordon Ross 	 * So I guess the real answer is to provide a tunable.
10278329232eSGordon Ross 	 */
10288329232eSGordon Ross 	STR_FTEVENT_MBLK(nbp, caller(), FTEV_COPYB, 1);
10298329232eSGordon Ross 
10308329232eSGordon Ross 	base = ndp->db_base + unaligned;
10318329232eSGordon Ross 	bcopy(dp->db_base, ndp->db_base + unaligned, size);
10328329232eSGordon Ross 
10338329232eSGordon Ross 	nbp->b_rptr = base + (bp->b_rptr - dp->db_base);
10348329232eSGordon Ross 	nbp->b_wptr = nbp->b_rptr + MBLKL(bp);
10358329232eSGordon Ross 
10368329232eSGordon Ross 	return (nbp);
10378329232eSGordon Ross }
10388329232eSGordon Ross 
10398329232eSGordon Ross /*
10408329232eSGordon Ross  * Copy data from message to newly allocated message using new
10418329232eSGordon Ross  * data blocks.  Returns a pointer to the new message, or NULL if error.
10428329232eSGordon Ross  */
10438329232eSGordon Ross mblk_t *
copymsg(mblk_t * bp)10448329232eSGordon Ross copymsg(mblk_t *bp)
10458329232eSGordon Ross {
10468329232eSGordon Ross 	mblk_t *head, *nbp;
10478329232eSGordon Ross 
10488329232eSGordon Ross 	if (!bp || !(nbp = head = copyb(bp)))
10498329232eSGordon Ross 		return (NULL);
10508329232eSGordon Ross 
10518329232eSGordon Ross 	while (bp->b_cont) {
10528329232eSGordon Ross 		if (!(nbp->b_cont = copyb(bp->b_cont))) {
10538329232eSGordon Ross 			freemsg(head);
10548329232eSGordon Ross 			return (NULL);
10558329232eSGordon Ross 		}
10568329232eSGordon Ross 		nbp = nbp->b_cont;
10578329232eSGordon Ross 		bp = bp->b_cont;
10588329232eSGordon Ross 	}
10598329232eSGordon Ross 	return (head);
10608329232eSGordon Ross }
10618329232eSGordon Ross 
10628329232eSGordon Ross /*
10638329232eSGordon Ross  * link a message block to tail of message
10648329232eSGordon Ross  */
10658329232eSGordon Ross void
linkb(mblk_t * mp,mblk_t * bp)10668329232eSGordon Ross linkb(mblk_t *mp, mblk_t *bp)
10678329232eSGordon Ross {
10688329232eSGordon Ross 	ASSERT(mp && bp);
10698329232eSGordon Ross 
10708329232eSGordon Ross 	for (; mp->b_cont; mp = mp->b_cont)
10718329232eSGordon Ross 		;
10728329232eSGordon Ross 	mp->b_cont = bp;
10738329232eSGordon Ross }
10748329232eSGordon Ross 
10758329232eSGordon Ross /*
10768329232eSGordon Ross  * unlink a message block from head of message
10778329232eSGordon Ross  * return pointer to new message.
10788329232eSGordon Ross  * NULL if message becomes empty.
10798329232eSGordon Ross  */
10808329232eSGordon Ross mblk_t *
unlinkb(mblk_t * bp)10818329232eSGordon Ross unlinkb(mblk_t *bp)
10828329232eSGordon Ross {
10838329232eSGordon Ross 	mblk_t *bp1;
10848329232eSGordon Ross 
10858329232eSGordon Ross 	bp1 = bp->b_cont;
10868329232eSGordon Ross 	bp->b_cont = NULL;
10878329232eSGordon Ross 	return (bp1);
10888329232eSGordon Ross }
10898329232eSGordon Ross 
10908329232eSGordon Ross /*
10918329232eSGordon Ross  * remove a message block "bp" from message "mp"
10928329232eSGordon Ross  *
10938329232eSGordon Ross  * Return pointer to new message or NULL if no message remains.
10948329232eSGordon Ross  * Return -1 if bp is not found in message.
10958329232eSGordon Ross  */
10968329232eSGordon Ross mblk_t *
rmvb(mblk_t * mp,mblk_t * bp)10978329232eSGordon Ross rmvb(mblk_t *mp, mblk_t *bp)
10988329232eSGordon Ross {
10998329232eSGordon Ross 	mblk_t *tmp;
11008329232eSGordon Ross 	mblk_t *lastp = NULL;
11018329232eSGordon Ross 
11028329232eSGordon Ross 	ASSERT(mp && bp);
11038329232eSGordon Ross 	for (tmp = mp; tmp; tmp = tmp->b_cont) {
11048329232eSGordon Ross 		if (tmp == bp) {
11058329232eSGordon Ross 			if (lastp)
11068329232eSGordon Ross 				lastp->b_cont = tmp->b_cont;
11078329232eSGordon Ross 			else
11088329232eSGordon Ross 				mp = tmp->b_cont;
11098329232eSGordon Ross 			tmp->b_cont = NULL;
11108329232eSGordon Ross 			return (mp);
11118329232eSGordon Ross 		}
11128329232eSGordon Ross 		lastp = tmp;
11138329232eSGordon Ross 	}
11148329232eSGordon Ross 	return ((mblk_t *)-1);
11158329232eSGordon Ross }
11168329232eSGordon Ross 
11178329232eSGordon Ross /*
11188329232eSGordon Ross  * Concatenate and align first len bytes of common
11198329232eSGordon Ross  * message type.  Len == -1, means concat everything.
11208329232eSGordon Ross  * Returns 1 on success, 0 on failure
11218329232eSGordon Ross  * After the pullup, mp points to the pulled up data.
11228329232eSGordon Ross  */
11238329232eSGordon Ross int
pullupmsg(mblk_t * mp,ssize_t len)11248329232eSGordon Ross pullupmsg(mblk_t *mp, ssize_t len)
11258329232eSGordon Ross {
11268329232eSGordon Ross 	mblk_t *bp, *b_cont;
11278329232eSGordon Ross 	dblk_t *dbp;
11288329232eSGordon Ross 	ssize_t n;
11298329232eSGordon Ross 
11308329232eSGordon Ross 	ASSERT(mp->b_datap->db_ref > 0);
11318329232eSGordon Ross 	ASSERT(mp->b_next == NULL && mp->b_prev == NULL);
11328329232eSGordon Ross 
11338329232eSGordon Ross 	/*
11348329232eSGordon Ross 	 * We won't handle Multidata message, since it contains
11358329232eSGordon Ross 	 * metadata which this function has no knowledge of; we
11368329232eSGordon Ross 	 * assert on DEBUG, and return failure otherwise.
11378329232eSGordon Ross 	 */
11388329232eSGordon Ross 	ASSERT(mp->b_datap->db_type != M_MULTIDATA);
11398329232eSGordon Ross 	if (mp->b_datap->db_type == M_MULTIDATA)
11408329232eSGordon Ross 		return (0);
11418329232eSGordon Ross 
11428329232eSGordon Ross 	if (len == -1) {
11438329232eSGordon Ross 		if (mp->b_cont == NULL && str_aligned(mp->b_rptr))
11448329232eSGordon Ross 			return (1);
11458329232eSGordon Ross 		len = xmsgsize(mp);
11468329232eSGordon Ross 	} else {
11478329232eSGordon Ross 		ssize_t first_mblk_len = mp->b_wptr - mp->b_rptr;
11488329232eSGordon Ross 		ASSERT(first_mblk_len >= 0);
11498329232eSGordon Ross 		/*
11508329232eSGordon Ross 		 * If the length is less than that of the first mblk,
11518329232eSGordon Ross 		 * we want to pull up the message into an aligned mblk.
11528329232eSGordon Ross 		 * Though not part of the spec, some callers assume it.
11538329232eSGordon Ross 		 */
11548329232eSGordon Ross 		if (len <= first_mblk_len) {
11558329232eSGordon Ross 			if (str_aligned(mp->b_rptr))
11568329232eSGordon Ross 				return (1);
11578329232eSGordon Ross 			len = first_mblk_len;
11588329232eSGordon Ross 		} else if (xmsgsize(mp) < len)
11598329232eSGordon Ross 			return (0);
11608329232eSGordon Ross 	}
11618329232eSGordon Ross 
11628329232eSGordon Ross 	if ((bp = allocb_tmpl(len, mp)) == NULL)
11638329232eSGordon Ross 		return (0);
11648329232eSGordon Ross 
11658329232eSGordon Ross 	dbp = bp->b_datap;
11668329232eSGordon Ross 	*bp = *mp;		/* swap mblks so bp heads the old msg... */
11678329232eSGordon Ross 	mp->b_datap = dbp;	/* ... and mp heads the new message */
11688329232eSGordon Ross 	mp->b_datap->db_mblk = mp;
11698329232eSGordon Ross 	bp->b_datap->db_mblk = bp;
11708329232eSGordon Ross 	mp->b_rptr = mp->b_wptr = dbp->db_base;
11718329232eSGordon Ross 
11728329232eSGordon Ross 	do {
11738329232eSGordon Ross 		ASSERT(bp->b_datap->db_ref > 0);
11748329232eSGordon Ross 		ASSERT(bp->b_wptr >= bp->b_rptr);
11758329232eSGordon Ross 		n = MIN(bp->b_wptr - bp->b_rptr, len);
11768329232eSGordon Ross 		ASSERT(n >= 0);		/* allow zero-length mblk_t's */
11778329232eSGordon Ross 		if (n > 0)
11788329232eSGordon Ross 			bcopy(bp->b_rptr, mp->b_wptr, (size_t)n);
11798329232eSGordon Ross 		mp->b_wptr += n;
11808329232eSGordon Ross 		bp->b_rptr += n;
11818329232eSGordon Ross 		len -= n;
11828329232eSGordon Ross 		if (bp->b_rptr != bp->b_wptr)
11838329232eSGordon Ross 			break;
11848329232eSGordon Ross 		b_cont = bp->b_cont;
11858329232eSGordon Ross 		freeb(bp);
11868329232eSGordon Ross 		bp = b_cont;
11878329232eSGordon Ross 	} while (len && bp);
11888329232eSGordon Ross 
11898329232eSGordon Ross 	mp->b_cont = bp;	/* tack on whatever wasn't pulled up */
11908329232eSGordon Ross 
11918329232eSGordon Ross 	return (1);
11928329232eSGordon Ross }
11938329232eSGordon Ross 
11948329232eSGordon Ross /*
11958329232eSGordon Ross  * Concatenate and align at least the first len bytes of common message
11968329232eSGordon Ross  * type.  Len == -1 means concatenate everything.  The original message is
11978329232eSGordon Ross  * unaltered.  Returns a pointer to a new message on success, otherwise
11988329232eSGordon Ross  * returns NULL.
11998329232eSGordon Ross  */
12008329232eSGordon Ross mblk_t *
msgpullup(mblk_t * mp,ssize_t len)12018329232eSGordon Ross msgpullup(mblk_t *mp, ssize_t len)
12028329232eSGordon Ross {
12038329232eSGordon Ross 	mblk_t	*newmp;
12048329232eSGordon Ross 	ssize_t	totlen;
12058329232eSGordon Ross 	ssize_t	n;
12068329232eSGordon Ross 
12078329232eSGordon Ross 	/*
12088329232eSGordon Ross 	 * We won't handle Multidata message, since it contains
12098329232eSGordon Ross 	 * metadata which this function has no knowledge of; we
12108329232eSGordon Ross 	 * assert on DEBUG, and return failure otherwise.
12118329232eSGordon Ross 	 */
12128329232eSGordon Ross 	ASSERT(mp->b_datap->db_type != M_MULTIDATA);
12138329232eSGordon Ross 	if (mp->b_datap->db_type == M_MULTIDATA)
12148329232eSGordon Ross 		return (NULL);
12158329232eSGordon Ross 
12168329232eSGordon Ross 	totlen = xmsgsize(mp);
12178329232eSGordon Ross 
12188329232eSGordon Ross 	if ((len > 0) && (len > totlen))
12198329232eSGordon Ross 		return (NULL);
12208329232eSGordon Ross 
12218329232eSGordon Ross 	/*
12228329232eSGordon Ross 	 * Copy all of the first msg type into one new mblk, then dupmsg
12238329232eSGordon Ross 	 * and link the rest onto this.
12248329232eSGordon Ross 	 */
12258329232eSGordon Ross 
12268329232eSGordon Ross 	len = totlen;
12278329232eSGordon Ross 
12288329232eSGordon Ross 	if ((newmp = allocb_tmpl(len, mp)) == NULL)
12298329232eSGordon Ross 		return (NULL);
12308329232eSGordon Ross 
12318329232eSGordon Ross 	newmp->b_flag = mp->b_flag;
12328329232eSGordon Ross 	newmp->b_band = mp->b_band;
12338329232eSGordon Ross 
12348329232eSGordon Ross 	while (len > 0) {
12358329232eSGordon Ross 		n = mp->b_wptr - mp->b_rptr;
12368329232eSGordon Ross 		ASSERT(n >= 0);		/* allow zero-length mblk_t's */
12378329232eSGordon Ross 		if (n > 0)
12388329232eSGordon Ross 			bcopy(mp->b_rptr, newmp->b_wptr, n);
12398329232eSGordon Ross 		newmp->b_wptr += n;
12408329232eSGordon Ross 		len -= n;
12418329232eSGordon Ross 		mp = mp->b_cont;
12428329232eSGordon Ross 	}
12438329232eSGordon Ross 
12448329232eSGordon Ross 	if (mp != NULL) {
12458329232eSGordon Ross 		newmp->b_cont = dupmsg(mp);
12468329232eSGordon Ross 		if (newmp->b_cont == NULL) {
12478329232eSGordon Ross 			freemsg(newmp);
12488329232eSGordon Ross 			return (NULL);
12498329232eSGordon Ross 		}
12508329232eSGordon Ross 	}
12518329232eSGordon Ross 
12528329232eSGordon Ross 	return (newmp);
12538329232eSGordon Ross }
12548329232eSGordon Ross 
12558329232eSGordon Ross /*
12568329232eSGordon Ross  * Trim bytes from message
12578329232eSGordon Ross  *  len > 0, trim from head
12588329232eSGordon Ross  *  len < 0, trim from tail
12598329232eSGordon Ross  * Returns 1 on success, 0 on failure.
12608329232eSGordon Ross  */
12618329232eSGordon Ross int
adjmsg(mblk_t * mp,ssize_t len)12628329232eSGordon Ross adjmsg(mblk_t *mp, ssize_t len)
12638329232eSGordon Ross {
12648329232eSGordon Ross 	mblk_t *bp;
12658329232eSGordon Ross 	mblk_t *save_bp = NULL;
12668329232eSGordon Ross 	mblk_t *prev_bp;
12678329232eSGordon Ross 	mblk_t *bcont;
12688329232eSGordon Ross 	unsigned char type;
12698329232eSGordon Ross 	ssize_t n;
12708329232eSGordon Ross 	int fromhead;
12718329232eSGordon Ross 	int first;
12728329232eSGordon Ross 
12738329232eSGordon Ross 	ASSERT(mp != NULL);
12748329232eSGordon Ross 	/*
12758329232eSGordon Ross 	 * We won't handle Multidata message, since it contains
12768329232eSGordon Ross 	 * metadata which this function has no knowledge of; we
12778329232eSGordon Ross 	 * assert on DEBUG, and return failure otherwise.
12788329232eSGordon Ross 	 */
12798329232eSGordon Ross 	ASSERT(mp->b_datap->db_type != M_MULTIDATA);
12808329232eSGordon Ross 	if (mp->b_datap->db_type == M_MULTIDATA)
12818329232eSGordon Ross 		return (0);
12828329232eSGordon Ross 
12838329232eSGordon Ross 	if (len < 0) {
12848329232eSGordon Ross 		fromhead = 0;
12858329232eSGordon Ross 		len = -len;
12868329232eSGordon Ross 	} else {
12878329232eSGordon Ross 		fromhead = 1;
12888329232eSGordon Ross 	}
12898329232eSGordon Ross 
12908329232eSGordon Ross 	if (xmsgsize(mp) < len)
12918329232eSGordon Ross 		return (0);
12928329232eSGordon Ross 
12938329232eSGordon Ross 	if (fromhead) {
12948329232eSGordon Ross 		first = 1;
12958329232eSGordon Ross 		while (len) {
12968329232eSGordon Ross 			ASSERT(mp->b_wptr >= mp->b_rptr);
12978329232eSGordon Ross 			n = MIN(mp->b_wptr - mp->b_rptr, len);
12988329232eSGordon Ross 			mp->b_rptr += n;
12998329232eSGordon Ross 			len -= n;
13008329232eSGordon Ross 
13018329232eSGordon Ross 			/*
13028329232eSGordon Ross 			 * If this is not the first zero length
13038329232eSGordon Ross 			 * message remove it
13048329232eSGordon Ross 			 */
13058329232eSGordon Ross 			if (!first && (mp->b_wptr == mp->b_rptr)) {
13068329232eSGordon Ross 				bcont = mp->b_cont;
13078329232eSGordon Ross 				freeb(mp);
13088329232eSGordon Ross 				mp = save_bp->b_cont = bcont;
13098329232eSGordon Ross 			} else {
13108329232eSGordon Ross 				save_bp = mp;
13118329232eSGordon Ross 				mp = mp->b_cont;
13128329232eSGordon Ross 			}
13138329232eSGordon Ross 			first = 0;
13148329232eSGordon Ross 		}
13158329232eSGordon Ross 	} else {
13168329232eSGordon Ross 		type = mp->b_datap->db_type;
13178329232eSGordon Ross 		while (len) {
13188329232eSGordon Ross 			bp = mp;
13198329232eSGordon Ross 			save_bp = NULL;
13208329232eSGordon Ross 
13218329232eSGordon Ross 			/*
13228329232eSGordon Ross 			 * Find the last message of same type
13238329232eSGordon Ross 			 */
13248329232eSGordon Ross 			while (bp && bp->b_datap->db_type == type) {
13258329232eSGordon Ross 				ASSERT(bp->b_wptr >= bp->b_rptr);
13268329232eSGordon Ross 				prev_bp = save_bp;
13278329232eSGordon Ross 				save_bp = bp;
13288329232eSGordon Ross 				bp = bp->b_cont;
13298329232eSGordon Ross 			}
13308329232eSGordon Ross 			if (save_bp == NULL)
13318329232eSGordon Ross 				break;
13328329232eSGordon Ross 			n = MIN(save_bp->b_wptr - save_bp->b_rptr, len);
13338329232eSGordon Ross 			save_bp->b_wptr -= n;
13348329232eSGordon Ross 			len -= n;
13358329232eSGordon Ross 
13368329232eSGordon Ross 			/*
13378329232eSGordon Ross 			 * If this is not the first message
13388329232eSGordon Ross 			 * and we have taken away everything
13398329232eSGordon Ross 			 * from this message, remove it
13408329232eSGordon Ross 			 */
13418329232eSGordon Ross 
13428329232eSGordon Ross 			if ((save_bp != mp) &&
13438329232eSGordon Ross 			    (save_bp->b_wptr == save_bp->b_rptr)) {
13448329232eSGordon Ross 				bcont = save_bp->b_cont;
13458329232eSGordon Ross 				freeb(save_bp);
13468329232eSGordon Ross 				prev_bp->b_cont = bcont;
13478329232eSGordon Ross 			}
13488329232eSGordon Ross 		}
13498329232eSGordon Ross 	}
13508329232eSGordon Ross 	return (1);
13518329232eSGordon Ross }
13528329232eSGordon Ross 
13538329232eSGordon Ross /*
13548329232eSGordon Ross  * get number of data bytes in message
13558329232eSGordon Ross  */
13568329232eSGordon Ross size_t
msgdsize(mblk_t * bp)13578329232eSGordon Ross msgdsize(mblk_t *bp)
13588329232eSGordon Ross {
13598329232eSGordon Ross 	size_t count = 0;
13608329232eSGordon Ross 
13618329232eSGordon Ross 	for (; bp; bp = bp->b_cont)
13628329232eSGordon Ross 		if (bp->b_datap->db_type == M_DATA) {
13638329232eSGordon Ross 			ASSERT(bp->b_wptr >= bp->b_rptr);
13648329232eSGordon Ross 			count += bp->b_wptr - bp->b_rptr;
13658329232eSGordon Ross 		}
13668329232eSGordon Ross 	return (count);
13678329232eSGordon Ross }
13688329232eSGordon Ross 
13698329232eSGordon Ross /* getq() etc to EOF removed */
1370