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