vio_util.c revision 2793:a30c4090b49f
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 */
49int
50vio_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 */
116int
117vio_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 */
168mblk_t *
169vio_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 */
192void
193vio_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