vio_util.c revision 7529:77782a1cc1f9
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <sys/types.h>
28#include <sys/sysmacros.h>
29#include <sys/cmn_err.h>
30#include <sys/errno.h>
31#include <sys/kmem.h>
32#include <sys/ksynch.h>
33#include <sys/stream.h>
34#include <sys/ddi.h>
35#include <sys/sunddi.h>
36#include <sys/vio_util.h>
37
38/*
39 * Create a pool of mblks from which future vio_allocb() requests
40 * will be serviced.
41 *
42 * NOTE: num_mblks has to non-zero and a power-of-2
43 *
44 * Returns 0 on success or EINVAL if num_mblks is zero or not
45 * a power of 2.
46 */
47int
48vio_create_mblks(uint64_t num_mblks, size_t mblk_size, vio_mblk_pool_t **poolp)
49{
50	vio_mblk_pool_t		*vmplp;
51	vio_mblk_t		*vmp;
52	uint8_t			*datap;
53	int			i;
54
55	if (!(num_mblks) || (!ISP2(num_mblks))) {
56		*poolp = 0;
57		return (EINVAL);
58	}
59
60	vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP);
61	vmplp->quelen = num_mblks;
62	vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */
63	vmplp->mblk_size = mblk_size;
64
65	mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER,
66	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
67	mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER,
68	    DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
69
70	vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP);
71	vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP);
72	vmplp->nextp = NULL;
73
74	/* create a queue of pointers to free vio_mblk_t's */
75	vmplp->quep = kmem_zalloc(vmplp->quelen *
76	    sizeof (vio_mblk_t *), KM_SLEEP);
77	vmplp->head = 0;
78	vmplp->tail =  0;
79
80	for (i = 0, datap = vmplp->datap; i < num_mblks; i++) {
81
82		vmp = &(vmplp->basep[i]);
83		vmp->vmplp = vmplp;
84		vmp->datap = datap;
85		vmp->reclaim.free_func = vio_freeb;
86		vmp->reclaim.free_arg = (caddr_t)vmp;
87		vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED,
88		    &vmp->reclaim);
89
90		if (vmp->mp == NULL)
91			continue;
92
93		/* put this vmp on the free stack */
94		vmplp->quep[vmplp->tail] = vmp;
95		vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
96
97		datap += mblk_size;
98	}
99
100	*poolp = vmplp;
101	return (0);
102}
103
104/*
105 * Destroy the pool of mblks. This can only succeed when
106 * all allocated mblks have been returned to the pool.
107 *
108 * It is up to the caller to ensure that no further mblks are
109 * requested from the pool after destroy has been invoked.
110 *
111 * Returns 0 on success, EINVAL if handle is invalid, or
112 * EBUSY if not all mblks reclaimed yet.
113 */
114int
115vio_destroy_mblks(vio_mblk_pool_t *vmplp)
116{
117	uint64_t i;
118	uint64_t num_mblks;
119	vio_mblk_t *vmp;
120
121	if (vmplp == NULL)
122		return (EINVAL);
123
124	/*
125	 * We can only destroy the pool once all the mblks have
126	 * been reclaimed.
127	 */
128	if (vmplp->head != vmplp->tail) {
129		/* some mblks still in use */
130		return (EBUSY);
131	}
132
133	num_mblks = vmplp->quelen;
134
135	/*
136	 * Set pool flag to tell vio_freeb() which is invoked from freeb(),
137	 * that it is being called in the context of vio_destroy_mblks().
138	 * This results in freeing only mblk_t and dblk_t structures for
139	 * each mp. The associated data buffers are freed below as one big
140	 * chunk through kmem_free(vmplp->datap).
141	 */
142	vmplp->flag |= VMPL_FLAG_DESTROYING;
143	for (i = 0; i < num_mblks; i++) {
144		vmp = &(vmplp->basep[i]);
145		if (vmp->mp)
146			freeb(vmp->mp);
147	}
148	vmplp->flag &= ~(VMPL_FLAG_DESTROYING);
149
150	kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t));
151	kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size);
152	kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *));
153
154	mutex_destroy(&vmplp->hlock);
155	mutex_destroy(&vmplp->tlock);
156
157	kmem_free(vmplp, sizeof (*vmplp));
158
159	return (0);
160}
161
162/*
163 * Allocate a mblk from the free pool if one is available.
164 * Otherwise returns NULL.
165 */
166mblk_t *
167vio_allocb(vio_mblk_pool_t *vmplp)
168{
169	vio_mblk_t	*vmp = NULL;
170	mblk_t		*mp = NULL;
171	uint32_t	head;
172
173	mutex_enter(&vmplp->hlock);
174	head = (vmplp->head + 1) & vmplp->quemask;
175	if (head != vmplp->tail) {
176		/* we have free mblks */
177		vmp = vmplp->quep[vmplp->head];
178		mp = vmp->mp;
179		vmplp->head = head;
180	}
181	mutex_exit(&vmplp->hlock);
182
183	return (mp);
184}
185
186/*
187 * Return a mblk to the free pool. Invoked when the upper IP
188 * layers do freemsg() etc on the mblk they were passed.
189 */
190void
191vio_freeb(void *arg)
192{
193	vio_mblk_t	*vmp = (vio_mblk_t *)arg;
194	vio_mblk_pool_t	*vmplp = vmp->vmplp;
195
196	if (vmplp->flag & VMPL_FLAG_DESTROYING) {
197		/*
198		 * This flag indicates that freeb() is being called from
199		 * vio_destroy_mblks().
200		 * We don't need to alloc a new mblk_t/dblk_t pair for
201		 * this data buffer, return from here and the data buffer
202		 * itself will be freed in vio_destroy_mblks().
203		 */
204		return;
205	}
206
207	vmp->mp = desballoc(vmp->datap, vmplp->mblk_size,
208	    BPRI_MED, &vmp->reclaim);
209
210	mutex_enter(&vmplp->tlock);
211	vmplp->quep[vmplp->tail] = vmp;
212	vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
213	mutex_exit(&vmplp->tlock);
214}
215
216/*
217 * Create a multiple pools of mblks from which future vio_allocb()
218 * or vio_multipool_allocb() requests will be serviced.
219 *
220 * Arguments:
221 *	vmultip -- A pointer to vio_multi_pool_t structure.
222 *	num_pools -- Number of the pools.
223 *	... -- Variable arguments consisting a list of buffer sizes for
224 *		each pool and list of number of buffers for each pool.
225 *
226 * NOTE: The restrictions of vio_create_mblks() apply to this interface also.
227 *
228 * Returns 0 on success or an error returned by vio_create_mblks().
229 */
230int
231vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...)
232{
233	int		i;
234	int		status;
235	char		*tbuf;
236	va_list		vap;
237	vio_mblk_pool_t *fvmp = NULL;
238
239	/*
240	 * Allocate memory for all of the following in one allocation.
241	 * 	bufsz_tbl -- sizeof (uint32_t) * num_pools
242	 * 	nbuf_tbl  -- sizeof (uint32_t) * num_pools
243	 *	vmpp	  -- sizeof (vio_mblk_pool_t *) * numpools
244	 */
245	vmultip->tbsz = (sizeof (uint32_t) * num_pools) +
246	    (sizeof (uint32_t) * num_pools) +
247	    (sizeof (vio_mblk_pool_t *) * num_pools);
248	tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP);
249	vmultip->bufsz_tbl = (uint32_t *)tbuf;
250	vmultip->nbuf_tbl = (uint32_t *)(tbuf +
251	    (sizeof (uint32_t) * num_pools));
252	vmultip->vmpp = (vio_mblk_pool_t **)(tbuf +
253	    (sizeof (uint32_t) * num_pools * 2));
254	vmultip->num_pools = num_pools;
255
256	/* initialize the array first */
257	va_start(vap, num_pools);
258	for (i = 0; i < num_pools; i++) {
259		vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t);
260	}
261	for (i = 0; i < num_pools; i++) {
262		vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t);
263	}
264	va_end(vap);
265
266	for (i = 0; i < vmultip->num_pools; i++) {
267		status = vio_create_mblks(vmultip->nbuf_tbl[i],
268		    vmultip->bufsz_tbl[i], &vmultip->vmpp[i]);
269		if (status != 0) {
270			vio_destroy_multipools(vmultip, &fvmp);
271			/* We expect to free the pools without failure here */
272			ASSERT(fvmp == NULL);
273			return (status);
274		}
275	}
276	return (0);
277}
278
279/*
280 * Destroy the multiple pools of mblks. This can only succeed when
281 * all allocated mblks have been returned to the pool.
282 *
283 * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t
284 * pointers are returned via th fvmp list. Its the caller's
285 * responsibility to check this list and free them later at an appropriate
286 * time with vio_destroy_mblks().
287 *
288 * Arguments:
289 *	vmultip -- A pointer to vio_multi_pool_t structure.
290 *	fvmp -- A list in which the pools that couldn't be destroyed are
291 *		returned.
292 */
293void
294vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp)
295{
296	int i;
297	vio_mblk_pool_t *vmp;
298
299	for (i = 0; i < vmultip->num_pools; i++) {
300		if ((vmp = vmultip->vmpp[i]) != NULL) {
301			if (vio_destroy_mblks(vmp)) {
302				/*
303				 * if we cannot reclaim all mblks, then
304				 * return the pool in the failed vmp
305				 * list(fvmp).
306				 */
307				vmp->nextp =  *fvmp;
308				*fvmp = vmp;
309			}
310		}
311	}
312	kmem_free(vmultip->bufsz_tbl, vmultip->tbsz);
313	vmultip->bufsz_tbl = NULL;
314	vmultip->nbuf_tbl = NULL;
315	vmultip->vmpp = NULL;
316	vmultip->num_pools = 0;
317	vmultip->tbsz = 0;
318}
319
320
321/*
322 * Allocate an mblk from one of the free pools, but tries the pool that
323 * best fits size requested first.
324 */
325mblk_t *
326vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size)
327{
328	int i;
329	mblk_t *mp = NULL;
330
331	/* Try allocating any size that fits */
332	for (i = 0; i < vmultip->num_pools; i++) {
333		if (size > vmultip->bufsz_tbl[i]) {
334			continue;
335		}
336		mp = vio_allocb(vmultip->vmpp[i]);
337		if (mp != NULL) {
338			break;
339		}
340	}
341	return (mp);
342}
343
344/*
345 * -----------------------------------------------------------------------------
346 * LDoms versioning functions
347 *
348 * Future work: the version negotiating code in the various VIO drivers
349 * could be made common and placed here.
350 */
351
352/*
353 * Description:
354 *	This function checks to see if the supplied version tuple (major,minor)
355 *	is supported by the version 'ver', negotiated during the handshake
356 *	between the client and the server (ver).
357 *
358 * Assumption:
359 *	This function assumes that backward compatability is not broken in
360 *	newer minor versions of the protocol (e.g. v1.5 & v1.1 support v1.0)
361 *
362 * Return Value:
363 *	B_TRUE		- The (major,minor) version is supported
364 *	B_FALSE		- not supported
365 */
366boolean_t
367vio_ver_is_supported(vio_ver_t ver, uint16_t major, uint16_t minor)
368{
369	if ((ver.major == major) && (ver.minor >= minor))
370		return (B_TRUE);
371
372	return (B_FALSE);
373}
374