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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/sysmacros.h> 28 #include <sys/errno.h> 29 #include <sys/kmem.h> 30 #include <sys/ksynch.h> 31 #include <sys/stream.h> 32 #include <sys/ddi.h> 33 #include <sys/sunddi.h> 34 #include <sys/vio_util.h> 35 36 static int vio_pool_cleanup_retries = 10; /* Max retries to free pool */ 37 static int vio_pool_cleanup_delay = 10000; /* 10ms */ 38 39 /* 40 * Create a pool of mblks from which future vio_allocb() requests 41 * will be serviced. 42 * 43 * NOTE: num_mblks has to non-zero and a power-of-2 44 * 45 * Returns 46 * 0 on success 47 * EINVAL if num_mblks is zero or not a power of 2. 48 * ENOSPC if the pool could not be created due to alloc failures. 49 */ 50 int 51 vio_create_mblks(uint64_t num_mblks, size_t mblk_size, uint8_t *mblk_datap, 52 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 if (mblk_datap == NULL) { 77 vmplp->datap = kmem_zalloc(num_mblks * mblk_size, KM_SLEEP); 78 } else { 79 vmplp->datap = mblk_datap; 80 vmplp->flag |= VMPL_FLAG_CLIENT_DATA; 81 } 82 vmplp->nextp = NULL; 83 84 /* create a queue of pointers to free vio_mblk_t's */ 85 vmplp->quep = kmem_zalloc(vmplp->quelen * 86 sizeof (vio_mblk_t *), KM_SLEEP); 87 vmplp->head = 0; 88 vmplp->tail = 0; 89 90 for (i = 0, datap = vmplp->datap; i < num_mblks; i++) { 91 92 vmp = &(vmplp->basep[i]); 93 vmp->vmplp = vmplp; 94 vmp->datap = datap; 95 vmp->reclaim.free_func = vio_freeb; 96 vmp->reclaim.free_arg = (caddr_t)vmp; 97 vmp->mp = desballoc(vmp->datap, mblk_size, BPRI_MED, 98 &vmp->reclaim); 99 100 if (vmp->mp == NULL) { 101 /* reset tail */ 102 vmplp->tail = vmplp->head; 103 104 /* 105 * vio_destroy_mblks() frees mblks that have been 106 * allocated so far and then destroys the pool. 107 */ 108 rv = vio_destroy_mblks(vmplp); 109 ASSERT(rv == 0); 110 111 *poolp = NULL; 112 return (ENOSPC); 113 } 114 115 vmp->index = i; 116 vmp->state = VIO_MBLK_FREE; 117 118 /* put this vmp on the free stack */ 119 vmplp->quep[vmplp->tail] = vmp; 120 vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; 121 122 datap += mblk_size; 123 } 124 125 *poolp = vmplp; 126 return (0); 127 } 128 129 /* 130 * Destroy the pool of mblks. This can only succeed when 131 * all allocated mblks have been returned to the pool. 132 * 133 * It is up to the caller to ensure that no further mblks are 134 * requested from the pool after destroy has been invoked. 135 * 136 * Returns 0 on success, EINVAL if handle is invalid, or 137 * EBUSY if not all mblks reclaimed yet. 138 */ 139 int 140 vio_destroy_mblks(vio_mblk_pool_t *vmplp) 141 { 142 uint64_t i; 143 uint64_t num_mblks; 144 vio_mblk_t *vmp; 145 int pool_cleanup_retries = 0; 146 147 148 if (vmplp == NULL) 149 return (EINVAL); 150 151 /* 152 * We can only destroy the pool once all the mblks have 153 * been reclaimed. 154 */ 155 do { 156 if (vmplp->head == vmplp->tail) { 157 break; 158 } 159 160 /* some mblks still in use */ 161 drv_usecwait(vio_pool_cleanup_delay); 162 } while (++pool_cleanup_retries < vio_pool_cleanup_retries); 163 164 if (vmplp->head != vmplp->tail) { 165 return (EBUSY); 166 } 167 168 num_mblks = vmplp->quelen; 169 170 /* 171 * Set pool flag to tell vio_freeb() which is invoked from freeb(), 172 * that it is being called in the context of vio_destroy_mblks(). 173 * This results in freeing only mblk_t and dblk_t structures for 174 * each mp. The associated data buffers are freed below as one big 175 * chunk through kmem_free(vmplp->datap). 176 */ 177 vmplp->flag |= VMPL_FLAG_DESTROYING; 178 for (i = 0; i < num_mblks; i++) { 179 vmp = &(vmplp->basep[i]); 180 /* 181 * It is possible that mblks have been allocated only upto 182 * a certain index and the entire quelen has not been 183 * initialized. This might happen due to desballoc() failure 184 * while creating the pool. The below check handles this 185 * condition. 186 */ 187 if (vmp->mp != NULL) 188 freeb(vmp->mp); 189 } 190 vmplp->flag &= ~(VMPL_FLAG_DESTROYING); 191 192 kmem_free(vmplp->basep, num_mblks * sizeof (vio_mblk_t)); 193 if ((vmplp->flag & VMPL_FLAG_CLIENT_DATA) == 0) { 194 kmem_free(vmplp->datap, num_mblks * vmplp->mblk_size); 195 } 196 kmem_free(vmplp->quep, num_mblks * sizeof (vio_mblk_t *)); 197 198 mutex_destroy(&vmplp->hlock); 199 mutex_destroy(&vmplp->tlock); 200 201 kmem_free(vmplp, sizeof (*vmplp)); 202 203 return (0); 204 } 205 206 /* 207 * Allocate a vio_mblk from the free pool if one is available. 208 * Otherwise returns NULL. 209 */ 210 vio_mblk_t * 211 vio_allocb(vio_mblk_pool_t *vmplp) 212 { 213 vio_mblk_t *vmp = NULL; 214 uint32_t head; 215 216 mutex_enter(&vmplp->hlock); 217 head = (vmplp->head + 1) & vmplp->quemask; 218 if (head != vmplp->tail) { 219 /* we have free mblks */ 220 vmp = vmplp->quep[vmplp->head]; 221 vmplp->head = head; 222 ASSERT(vmp->state == VIO_MBLK_FREE); 223 vmp->state = VIO_MBLK_BOUND; 224 } 225 mutex_exit(&vmplp->hlock); 226 227 return (vmp); 228 } 229 230 /* 231 * Return a mblk to the free pool. Invoked when the upper IP 232 * layers do freemsg() etc on the mblk they were passed. 233 */ 234 void 235 vio_freeb(void *arg) 236 { 237 vio_mblk_t *vmp = (vio_mblk_t *)arg; 238 vio_mblk_pool_t *vmplp = vmp->vmplp; 239 240 if (vmplp->flag & VMPL_FLAG_DESTROYING) { 241 /* 242 * This flag indicates that freeb() is being called from 243 * vio_destroy_mblks(). 244 * We don't need to alloc a new mblk_t/dblk_t pair for 245 * this data buffer, return from here and the data buffer 246 * itself will be freed in vio_destroy_mblks(). 247 */ 248 return; 249 } 250 251 vmp->mp = desballoc(vmp->datap, vmplp->mblk_size, 252 BPRI_MED, &vmp->reclaim); 253 vmp->state = VIO_MBLK_FREE; 254 255 mutex_enter(&vmplp->tlock); 256 vmplp->quep[vmplp->tail] = vmp; 257 vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; 258 mutex_exit(&vmplp->tlock); 259 } 260 261 262 /* 263 * This function searches the given mblk pool for mblks that are in the 264 * BOUND state and moves them to the FREE state. Note that only clients that 265 * are operating in RxDringData mode use this function. This allows such 266 * clients to reclaim buffers that are provided to the peer as shared memory, 267 * before calling vio_destroy_mblks(). We don't need this in other cases 268 * as the buffer is locally managed. 269 */ 270 void 271 vio_clobber_pool(vio_mblk_pool_t *vmplp) 272 { 273 uint64_t num_mblks = vmplp->quelen; 274 uint64_t i; 275 vio_mblk_t *vmp; 276 277 mutex_enter(&vmplp->hlock); 278 mutex_enter(&vmplp->tlock); 279 for (i = 0; i < num_mblks; i++) { 280 vmp = &(vmplp->basep[i]); 281 if ((vmp->state & VIO_MBLK_BOUND) != 0) { 282 /* put this vmp on the free stack */ 283 vmp->state = VIO_MBLK_FREE; 284 ASSERT(vmplp->tail != vmplp->head); 285 vmplp->quep[vmplp->tail] = vmp; 286 vmplp->tail = (vmplp->tail + 1) & vmplp->quemask; 287 } 288 } 289 mutex_exit(&vmplp->tlock); 290 mutex_exit(&vmplp->hlock); 291 } 292 293 /* 294 * Create a multiple pools of mblks from which future vio_allocb() 295 * or vio_multipool_allocb() requests will be serviced. 296 * 297 * Arguments: 298 * vmultip -- A pointer to vio_multi_pool_t structure. 299 * num_pools -- Number of the pools. 300 * ... -- Variable arguments consisting a list of buffer sizes for 301 * each pool and list of number of buffers for each pool. 302 * 303 * NOTE: The restrictions of vio_create_mblks() apply to this interface also. 304 * 305 * Returns 0 on success or an error returned by vio_create_mblks(). 306 */ 307 int 308 vio_init_multipools(vio_multi_pool_t *vmultip, int num_pools, ...) 309 { 310 int i; 311 int status; 312 char *tbuf; 313 va_list vap; 314 vio_mblk_pool_t *fvmp = NULL; 315 316 /* 317 * Allocate memory for all of the following in one allocation. 318 * bufsz_tbl -- sizeof (uint32_t) * num_pools 319 * nbuf_tbl -- sizeof (uint32_t) * num_pools 320 * vmpp -- sizeof (vio_mblk_pool_t *) * numpools 321 */ 322 vmultip->tbsz = (sizeof (uint32_t) * num_pools) + 323 (sizeof (uint32_t) * num_pools) + 324 (sizeof (vio_mblk_pool_t *) * num_pools); 325 tbuf = kmem_zalloc(vmultip->tbsz, KM_SLEEP); 326 vmultip->bufsz_tbl = (uint32_t *)tbuf; 327 vmultip->nbuf_tbl = (uint32_t *)(tbuf + 328 (sizeof (uint32_t) * num_pools)); 329 vmultip->vmpp = (vio_mblk_pool_t **)(tbuf + 330 (sizeof (uint32_t) * num_pools * 2)); 331 vmultip->num_pools = num_pools; 332 333 /* initialize the array first */ 334 va_start(vap, num_pools); 335 for (i = 0; i < num_pools; i++) { 336 vmultip->bufsz_tbl[i] = va_arg(vap, uint32_t); 337 } 338 for (i = 0; i < num_pools; i++) { 339 vmultip->nbuf_tbl[i] = va_arg(vap, uint32_t); 340 } 341 va_end(vap); 342 343 for (i = 0; i < vmultip->num_pools; i++) { 344 status = vio_create_mblks(vmultip->nbuf_tbl[i], 345 vmultip->bufsz_tbl[i], NULL, &vmultip->vmpp[i]); 346 if (status != 0) { 347 vio_destroy_multipools(vmultip, &fvmp); 348 /* We expect to free the pools without failure here */ 349 ASSERT(fvmp == NULL); 350 return (status); 351 } 352 } 353 return (0); 354 } 355 356 /* 357 * Destroy the multiple pools of mblks. This can only succeed when 358 * all allocated mblks have been returned to the pool. 359 * 360 * If a pool of mblks couldn't be destroyed, then the failed vio_mblk_pool_t 361 * pointers are returned via th fvmp list. Its the caller's 362 * responsibility to check this list and free them later at an appropriate 363 * time with vio_destroy_mblks(). 364 * 365 * Arguments: 366 * vmultip -- A pointer to vio_multi_pool_t structure. 367 * fvmp -- A list in which the pools that couldn't be destroyed are 368 * returned. 369 */ 370 void 371 vio_destroy_multipools(vio_multi_pool_t *vmultip, vio_mblk_pool_t **fvmp) 372 { 373 int i; 374 vio_mblk_pool_t *vmp; 375 376 for (i = 0; i < vmultip->num_pools; i++) { 377 if ((vmp = vmultip->vmpp[i]) != NULL) { 378 if (vio_destroy_mblks(vmp)) { 379 /* 380 * if we cannot reclaim all mblks, then 381 * return the pool in the failed vmp 382 * list(fvmp). 383 */ 384 vmp->nextp = *fvmp; 385 *fvmp = vmp; 386 } 387 } 388 } 389 if (vmultip->tbsz != 0) 390 kmem_free(vmultip->bufsz_tbl, vmultip->tbsz); 391 vmultip->bufsz_tbl = NULL; 392 vmultip->nbuf_tbl = NULL; 393 vmultip->vmpp = NULL; 394 vmultip->num_pools = 0; 395 vmultip->tbsz = 0; 396 } 397 398 399 /* 400 * Allocate an vio_mblk from one of the free pools, but tries the pool that 401 * best fits size requested first. 402 */ 403 vio_mblk_t * 404 vio_multipool_allocb(vio_multi_pool_t *vmultip, size_t size) 405 { 406 int i; 407 vio_mblk_t *vmp = NULL; 408 409 /* Try allocating any size that fits */ 410 for (i = 0; i < vmultip->num_pools; i++) { 411 if (size > vmultip->bufsz_tbl[i]) { 412 continue; 413 } 414 vmp = vio_allocb(vmultip->vmpp[i]); 415 if (vmp != NULL) { 416 break; 417 } 418 } 419 return (vmp); 420 } 421 422 /* 423 * ----------------------------------------------------------------------------- 424 * LDoms versioning functions 425 * 426 * Future work: the version negotiating code in the various VIO drivers 427 * could be made common and placed here. 428 */ 429 430 /* 431 * Description: 432 * This function checks to see if the supplied version tuple (major,minor) 433 * is supported by the version 'ver', negotiated during the handshake 434 * between the client and the server (ver). 435 * 436 * Assumption: 437 * This function assumes that backward compatability is not broken in 438 * newer minor versions of the protocol (e.g. v1.5 & v1.1 support v1.0) 439 * 440 * Return Value: 441 * B_TRUE - The (major,minor) version is supported 442 * B_FALSE - not supported 443 */ 444 boolean_t 445 vio_ver_is_supported(vio_ver_t ver, uint16_t major, uint16_t minor) 446 { 447 if ((ver.major == major) && (ver.minor >= minor)) 448 return (B_TRUE); 449 450 return (B_FALSE); 451 } 452