xref: /illumos-gate/usr/src/uts/sun4v/io/vio_util.c (revision 3af08d82)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <sys/cmn_err.h>
32 #include <sys/errno.h>
33 #include <sys/kmem.h>
34 #include <sys/ksynch.h>
35 #include <sys/stream.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/vio_util.h>
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 0 on success or EINVAL if num_mblks is zero or not
47  * a power of 2.
48  */
49 int
50 vio_create_mblks(uint64_t num_mblks, size_t mblk_size, vio_mblk_pool_t **poolp)
51 {
52 	vio_mblk_pool_t		*vmplp;
53 	vio_mblk_t		*vmp;
54 	uint8_t			*datap;
55 	int			i;
56 
57 	if (!(num_mblks) || (!ISP2(num_mblks))) {
58 		*poolp = 0;
59 		return (EINVAL);
60 	}
61 
62 	vmplp = kmem_zalloc(sizeof (*vmplp), KM_SLEEP);
63 	vmplp->quelen = num_mblks;
64 	vmplp->quemask = num_mblks - 1; /* expects quelen is power-of-2 */
65 	vmplp->mblk_size = mblk_size;
66 
67 	mutex_init(&vmplp->hlock, NULL, MUTEX_DRIVER,
68 				DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
69 	mutex_init(&vmplp->tlock, NULL, MUTEX_DRIVER,
70 				DDI_INTR_PRI(DDI_INTR_SOFTPRI_DEFAULT));
71 
72 	vmplp->basep = kmem_zalloc(num_mblks * sizeof (vio_mblk_t), KM_SLEEP);
73 	vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP);
74 	vmplp->nextp = NULL;
75 
76 	/* create a queue of pointers to free vio_mblk_t's */
77 	vmplp->quep = kmem_zalloc(vmplp->quelen * sizeof (vio_mblk_t *),
78 								KM_SLEEP);
79 	vmplp->head = 0;
80 	vmplp->tail =  0;
81 
82 	for (i = 0, datap = vmplp->datap; i < num_mblks; i++) {
83 
84 		vmp = &(vmplp->basep[i]);
85 		vmp->vmplp = vmplp;
86 		vmp->datap = datap;
87 		vmp->reclaim.free_func = vio_freeb;
88 		vmp->reclaim.free_arg = (caddr_t)vmp;
89 		vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED,
90 		    &vmp->reclaim);
91 
92 		if (vmp->mp == NULL)
93 			continue;
94 
95 		/* put this vmp on the free stack */
96 		vmplp->quep[vmplp->tail] = vmp;
97 		vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
98 
99 		datap += mblk_size;
100 	}
101 
102 	*poolp = vmplp;
103 	return (0);
104 }
105 
106 /*
107  * Destroy the pool of mblks. This can only succeed when
108  * all allocated mblks have been returned to the pool.
109  *
110  * It is up to the caller to ensure that no further mblks are
111  * requested from the pool after destroy has been invoked.
112  *
113  * Returns 0 on success, EINVAL if handle is invalid, or
114  * EBUSY if not all mblks reclaimed yet.
115  */
116 int
117 vio_destroy_mblks(vio_mblk_pool_t *vmplp)
118 {
119 	uint64_t i;
120 	uint64_t num_mblks;
121 	vio_mblk_t *vmp;
122 
123 	if (vmplp == NULL)
124 		return (EINVAL);
125 
126 	/*
127 	 * We can only destroy the pool once all the mblks have
128 	 * been reclaimed.
129 	 */
130 	if (vmplp->head != vmplp->tail) {
131 		/* some mblks still in use */
132 		return (EBUSY);
133 	}
134 
135 	num_mblks = vmplp->quelen;
136 
137 	/*
138 	 * Set pool flag to tell vio_freeb() which is invoked from freeb(),
139 	 * that it is being called in the context of vio_destroy_mblks().
140 	 * This results in freeing only mblk_t and dblk_t structures for
141 	 * each mp. The associated data buffers are freed below as one big
142 	 * chunk through kmem_free(vmplp->datap).
143 	 */
144 	vmplp->flag |= VMPL_FLAG_DESTROYING;
145 	for (i = 0; i < num_mblks; i++) {
146 		vmp = &(vmplp->basep[i]);
147 		if (vmp->mp)
148 			freeb(vmp->mp);
149 	}
150 	vmplp->flag &= ~(VMPL_FLAG_DESTROYING);
151 
152 	kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t));
153 	kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size);
154 	kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *));
155 
156 	mutex_destroy(&vmplp->hlock);
157 	mutex_destroy(&vmplp->tlock);
158 
159 	kmem_free(vmplp, sizeof (*vmplp));
160 
161 	return (0);
162 }
163 
164 /*
165  * Allocate a mblk from the free pool if one is available.
166  * Otherwise returns NULL.
167  */
168 mblk_t *
169 vio_allocb(vio_mblk_pool_t *vmplp)
170 {
171 	vio_mblk_t	*vmp = NULL;
172 	mblk_t		*mp = NULL;
173 	uint32_t	head;
174 
175 	mutex_enter(&vmplp->hlock);
176 	head = (vmplp->head + 1) & vmplp->quemask;
177 	if (head != vmplp->tail) {
178 		/* we have free mblks */
179 		vmp = vmplp->quep[vmplp->head];
180 		mp = vmp->mp;
181 		vmplp->head = head;
182 	}
183 	mutex_exit(&vmplp->hlock);
184 
185 	return (mp);
186 }
187 
188 /*
189  * Return a mblk to the free pool. Invoked when the upper IP
190  * layers do freemsg() etc on the mblk they were passed.
191  */
192 void
193 vio_freeb(void *arg)
194 {
195 	vio_mblk_t	*vmp = (vio_mblk_t *)arg;
196 	vio_mblk_pool_t	*vmplp = vmp->vmplp;
197 
198 	if (vmplp->flag & VMPL_FLAG_DESTROYING) {
199 		/*
200 		 * This flag indicates that freeb() is being called from
201 		 * vio_destroy_mblks().
202 		 * We don't need to alloc a new mblk_t/dblk_t pair for
203 		 * this data buffer, return from here and the data buffer
204 		 * itself will be freed in vio_destroy_mblks().
205 		 */
206 		return;
207 	}
208 
209 	vmp->mp = desballoc(vmp->datap, vmplp->mblk_size,
210 					BPRI_MED, &vmp->reclaim);
211 
212 	mutex_enter(&vmplp->tlock);
213 	vmplp->quep[vmplp->tail] = vmp;
214 	vmplp->tail = (vmplp->tail + 1) & vmplp->quemask;
215 	mutex_exit(&vmplp->tlock);
216 }
217