xref: /illumos-gate/usr/src/uts/sun4v/io/vio_util.c (revision fcf3ce44)
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  */
47 int
48 vio_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  */
114 int
115 vio_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  */
166 mblk_t *
167 vio_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  */
190 void
191 vio_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  */
230 int
231 vio_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  */
293 void
294 vio_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  */
325 mblk_t *
326 vio_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  */
366 boolean_t
367 vio_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