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