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