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