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