xref: /illumos-gate/usr/src/uts/sun4v/io/vio_util.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
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 
37 static int vio_pool_cleanup_retries = 10;	/* Max retries to free pool */
38 static 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  */
51 int
52 vio_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  */
131 int
132 vio_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  */
200 mblk_t *
201 vio_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  */
224 void
225 vio_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  */
264 int
265 vio_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  */
327 void
328 vio_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  */
360 mblk_t *
361 vio_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  */
401 boolean_t
402 vio_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