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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/errno.h> 31 #include <sys/debug.h> 32 #include <sys/time.h> 33 #include <sys/sysmacros.h> 34 #include <sys/systm.h> 35 #include <sys/user.h> 36 #include <sys/stropts.h> 37 #include <sys/stream.h> 38 #include <sys/strlog.h> 39 #include <sys/strsubr.h> 40 #include <sys/cmn_err.h> 41 #include <sys/cpu.h> 42 #include <sys/kmem.h> 43 #include <sys/conf.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/ksynch.h> 47 #include <sys/stat.h> 48 #include <sys/kstat.h> 49 #include <sys/vtrace.h> 50 #include <sys/strsun.h> 51 #include <sys/dlpi.h> 52 #include <sys/ethernet.h> 53 #include <net/if.h> 54 #include <sys/varargs.h> 55 #include <sys/machsystm.h> 56 #include <sys/modctl.h> 57 #include <sys/modhash.h> 58 #include <sys/mac.h> 59 #include <sys/mac_ether.h> 60 #include <sys/taskq.h> 61 #include <sys/note.h> 62 #include <sys/mach_descrip.h> 63 #include <sys/mac.h> 64 #include <sys/mdeg.h> 65 #include <sys/ldc.h> 66 #include <sys/vsw_fdb.h> 67 #include <sys/vsw.h> 68 #include <sys/vio_mailbox.h> 69 #include <sys/vnet_mailbox.h> 70 #include <sys/vnet_common.h> 71 #include <sys/vio_util.h> 72 #include <sys/sdt.h> 73 #include <sys/atomic.h> 74 #include <sys/callb.h> 75 76 77 #define VSW_DDS_NEXT_REQID(vsharep) (++vsharep->vs_req_id) 78 79 extern boolean_t vsw_hio_enabled; /* HybridIO enabled? */ 80 extern int vsw_hio_max_cleanup_retries; 81 extern int vsw_hio_cleanup_delay; 82 83 /* Functions imported from other files */ 84 extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t); 85 extern int vsw_set_hw(vsw_t *, vsw_port_t *, int); 86 extern int vsw_unset_hw(vsw_t *, vsw_port_t *, int); 87 extern void vsw_hio_port_reset(vsw_port_t *portp); 88 89 /* Functions exported to other files */ 90 void vsw_hio_init(vsw_t *vswp); 91 void vsw_hio_cleanup(vsw_t *vswp); 92 void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp); 93 void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp); 94 void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg); 95 void vsw_hio_start_ports(vsw_t *vswp); 96 void vsw_hio_stop_port(vsw_port_t *portp); 97 98 /* Support functions */ 99 static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp); 100 static void vsw_hio_free_share(vsw_share_t *vsharep); 101 static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp); 102 static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id); 103 static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp); 104 static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, 105 uint64_t cookie, uint64_t macaddr, uint32_t req_id); 106 static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack); 107 static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep); 108 static int vsw_hio_bind_macaddr(vsw_share_t *vsharep); 109 static void vsw_hio_unbind_macaddr(vsw_share_t *vsharep); 110 111 112 /* 113 * vsw_hio_init -- Initialize the HybridIO related info. 114 * - Query SHARES and RINGS capability. Both capabilities 115 * need to be supported by the physical-device. 116 */ 117 void 118 vsw_hio_init(vsw_t *vswp) 119 { 120 vsw_hio_t *hiop = &vswp->vhio; 121 int i; 122 int rv; 123 124 D1(vswp, "%s:enter\n", __func__); 125 mutex_enter(&vswp->hw_lock); 126 if (vsw_hio_enabled == B_FALSE) { 127 mutex_exit(&vswp->hw_lock); 128 return; 129 } 130 131 vswp->hio_capable = B_FALSE; 132 rv = mac_capab_get(vswp->mh, MAC_CAPAB_SHARES, &hiop->vh_scapab); 133 if (rv == B_FALSE) { 134 D2(vswp, "%s: %s is not HybridIO capable\n", __func__, 135 vswp->physname); 136 mutex_exit(&vswp->hw_lock); 137 return; 138 } 139 rv = mac_capab_get(vswp->mh, MAC_CAPAB_RINGS, &hiop->vh_rcapab); 140 if (rv == B_FALSE) { 141 DWARN(vswp, "%s: %s has no RINGS capability\n", __func__, 142 vswp->physname); 143 mutex_exit(&vswp->hw_lock); 144 return; 145 } 146 hiop->vh_num_shares = hiop->vh_scapab.ms_snum; 147 hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) * 148 hiop->vh_num_shares), KM_SLEEP); 149 for (i = 0; i < hiop->vh_num_shares; i++) { 150 hiop->vh_shares[i].vs_state = VSW_SHARE_FREE; 151 hiop->vh_shares[i].vs_index = i; 152 hiop->vh_shares[i].vs_vswp = vswp; 153 } 154 vswp->hio_capable = B_TRUE; 155 D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__, 156 vswp->physname, hiop->vh_num_shares); 157 D1(vswp, "%s:exit\n", __func__); 158 mutex_exit(&vswp->hw_lock); 159 } 160 161 /* 162 * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain. 163 * - Allocate a free share. 164 * - Bind the Guest's MAC address. 165 */ 166 static vsw_share_t * 167 vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp) 168 { 169 vsw_hio_t *hiop = &vswp->vhio; 170 mac_capab_share_t *hcapab = &hiop->vh_scapab; 171 vsw_share_t *vsharep; 172 vsw_port_t *portp = ldcp->ldc_port; 173 uint64_t ldc_id = ldcp->ldc_id; 174 uint32_t rmin, rmax; 175 uint64_t rmap; 176 int rv; 177 178 D1(vswp, "%s:enter\n", __func__); 179 vsharep = vsw_hio_find_free_share(vswp); 180 if (vsharep == NULL) { 181 /* No free shares available */ 182 return (NULL); 183 } 184 /* 185 * Allocate a Share - it will come with rings/groups 186 * already assigned to it. 187 */ 188 rv = hcapab->ms_salloc(hcapab->ms_handle, ldc_id, 189 &vsharep->vs_cookie, &vsharep->vs_shdl); 190 if (rv != 0) { 191 D2(vswp, "Alloc a share failed for ldc=0x%lx rv=%d", 192 ldc_id, rv); 193 return (NULL); 194 } 195 196 /* 197 * Query the RX group number to bind the port's 198 * MAC address to it. 199 */ 200 hcapab->ms_squery(vsharep->vs_shdl, MAC_RING_TYPE_RX, 201 &rmin, &rmax, &rmap, &vsharep->vs_gnum); 202 203 /* Cache some useful info */ 204 vsharep->vs_ldcid = ldcp->ldc_id; 205 vsharep->vs_macaddr = vnet_macaddr_strtoul( 206 portp->p_macaddr.ether_addr_octet); 207 vsharep->vs_portp = ldcp->ldc_port; 208 209 /* Bind the Guest's MAC address */ 210 rv = vsw_hio_bind_macaddr(vsharep); 211 if (rv != 0) { 212 /* something went wrong, cleanup */ 213 hcapab->ms_sfree(vsharep->vs_shdl); 214 return (NULL); 215 } 216 217 vsharep->vs_state |= VSW_SHARE_ASSIGNED; 218 219 D1(vswp, "%s:exit\n", __func__); 220 return (vsharep); 221 } 222 223 /* 224 * vsw_hio_bind_macaddr -- Remove the port's MAC address from the 225 * physdev and bind it to the Share's RX group. 226 */ 227 static int 228 vsw_hio_bind_macaddr(vsw_share_t *vsharep) 229 { 230 vsw_t *vswp = vsharep->vs_vswp; 231 vsw_port_t *portp = vsharep->vs_portp; 232 mac_capab_rings_t *rcapab = &vswp->vhio.vh_rcapab; 233 mac_group_info_t *ginfop = &vsharep->vs_rxginfo; 234 int rv; 235 236 /* Get the RX groupinfo */ 237 rcapab->mr_gget(rcapab->mr_handle, MAC_RING_TYPE_RX, 238 vsharep->vs_gnum, &vsharep->vs_rxginfo, NULL); 239 240 /* Unset the MAC address first */ 241 if (portp->addr_set != VSW_ADDR_UNSET) { 242 (void) vsw_unset_hw(vswp, portp, VSW_VNETPORT); 243 } 244 245 /* Bind the MAC address to the RX group */ 246 rv = ginfop->mrg_addmac(ginfop->mrg_driver, 247 (uint8_t *)&portp->p_macaddr.ether_addr_octet); 248 if (rv != 0) { 249 /* Restore the address back as it was */ 250 (void) vsw_set_hw(vswp, portp, VSW_VNETPORT); 251 return (rv); 252 } 253 return (0); 254 } 255 256 /* 257 * vsw_hio_unbind_macaddr -- Unbind the port's MAC address and restore 258 * it back as it was before. 259 */ 260 static void 261 vsw_hio_unbind_macaddr(vsw_share_t *vsharep) 262 { 263 vsw_t *vswp = vsharep->vs_vswp; 264 vsw_port_t *portp = vsharep->vs_portp; 265 mac_group_info_t *ginfop = &vsharep->vs_rxginfo; 266 267 if (portp == NULL) { 268 return; 269 } 270 /* Unbind the MAC address from the RX group */ 271 (void) ginfop->mrg_remmac(ginfop->mrg_driver, 272 (uint8_t *)&portp->p_macaddr.ether_addr_octet); 273 274 /* Program the MAC address back */ 275 (void) vsw_set_hw(vswp, portp, VSW_VNETPORT); 276 } 277 278 /* 279 * vsw_hio_find_free_share -- Find a free Share. 280 */ 281 static vsw_share_t * 282 vsw_hio_find_free_share(vsw_t *vswp) 283 { 284 vsw_hio_t *hiop = &vswp->vhio; 285 vsw_share_t *vsharep; 286 int i; 287 288 D1(vswp, "%s:enter\n", __func__); 289 for (i = 0; i < hiop->vh_num_shares; i++) { 290 vsharep = &hiop->vh_shares[i]; 291 if (vsharep->vs_state == VSW_SHARE_FREE) { 292 D1(vswp, "%s:Returning free share(%d)\n", 293 __func__, vsharep->vs_index); 294 return (vsharep); 295 } 296 } 297 D1(vswp, "%s:no free share\n", __func__); 298 return (NULL); 299 } 300 301 /* 302 * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding 303 * share structure. 304 */ 305 static vsw_share_t * 306 vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id) 307 { 308 vsw_hio_t *hiop = &vswp->vhio; 309 vsw_share_t *vsharep; 310 int i; 311 312 D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id); 313 for (i = 0; i < hiop->vh_num_shares; i++) { 314 vsharep = &hiop->vh_shares[i]; 315 if (vsharep->vs_state == VSW_SHARE_FREE) { 316 continue; 317 } 318 if (vsharep->vs_ldcid == ldc_id) { 319 D1(vswp, "%s:returning share(%d)", 320 __func__, vsharep->vs_index); 321 return (vsharep); 322 } 323 } 324 D1(vswp, "%s:returning NULL", __func__); 325 return (NULL); 326 } 327 328 /* 329 * vsw_hio_find_vshare_port -- Given portp, find the corresponding 330 * share structure. 331 */ 332 static vsw_share_t * 333 vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp) 334 { 335 vsw_hio_t *hiop = &vswp->vhio; 336 vsw_share_t *vsharep; 337 int i; 338 339 D1(vswp, "%s:enter, portp=0x%p", __func__, portp); 340 for (i = 0; i < hiop->vh_num_shares; i++) { 341 vsharep = &hiop->vh_shares[i]; 342 if (vsharep->vs_state == VSW_SHARE_FREE) { 343 continue; 344 } 345 if (vsharep->vs_portp == portp) { 346 D1(vswp, "%s:returning share(%d)", 347 __func__, vsharep->vs_index); 348 return (vsharep); 349 } 350 } 351 D1(vswp, "%s:returning NULL", __func__); 352 return (NULL); 353 } 354 355 /* 356 * vsw_hio_free_share -- Unbind the MAC address and free share. 357 */ 358 static void 359 vsw_hio_free_share(vsw_share_t *vsharep) 360 { 361 vsw_t *vswp = vsharep->vs_vswp; 362 vsw_hio_t *hiop = &vswp->vhio; 363 mac_capab_share_t *hcapab = &hiop->vh_scapab; 364 365 D1(vswp, "%s:enter\n", __func__); 366 367 /* First unbind the MAC address and restore it back */ 368 vsw_hio_unbind_macaddr(vsharep); 369 370 /* free share */ 371 hcapab->ms_sfree(vsharep->vs_shdl); 372 vsharep->vs_state = VSW_SHARE_FREE; 373 374 /* DERR only for printing by default */ 375 DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX", 376 vsharep->vs_ldcid, vsharep->vs_cookie); 377 D1(vswp, "%s:exit\n", __func__); 378 } 379 380 381 /* 382 * vsw_hio_cleanup -- A rountine to free all shares gracefully. 383 * The following are the steps followed to accomplish this: 384 * 385 * - First clear 'hio_capable' to avoid further share allocations. 386 * - If a share is in accepted(ACKD) state, that means the guest 387 * has HybridIO setup etc. If so, send a DEL_SHARE message and 388 * give some time(delay) for the guest to ACK. 389 * - If the Share is another state, give some time to transition to 390 * ACKD state, then try the above. 391 * - After max retries, reset the ports to brute force the shares 392 * to be freed. Give a little delay for the LDC reset code to 393 * free the Share. 394 */ 395 void 396 vsw_hio_cleanup(vsw_t *vswp) 397 { 398 vsw_hio_t *hiop = &vswp->vhio; 399 vsw_port_list_t *plist = &vswp->plist; 400 vsw_share_t *vsharep; 401 int free_shares = 0; 402 int max_retries = vsw_hio_max_cleanup_retries; 403 int i; 404 405 D1(vswp, "%s:enter\n", __func__); 406 407 /* 408 * Acquire plist->lockrw to make the locking a bit easier 409 * and keep the ports in a stable state while we are cleaningup 410 * HybridIO. 411 */ 412 READ_ENTER(&plist->lockrw); 413 mutex_enter(&vswp->hw_lock); 414 /* 415 * first clear the hio_capable flag so that no more 416 * HybridIO operations are initiated. 417 */ 418 vswp->hio_capable = B_FALSE; 419 420 do { 421 free_shares = 0; 422 for (i = 0; i < hiop->vh_num_shares; i++) { 423 vsharep = &hiop->vh_shares[i]; 424 if (vsharep->vs_state == VSW_SHARE_FREE) { 425 free_shares++; 426 continue; 427 } 428 /* 429 * If the share is in DDS_ACKD state, then 430 * send DEL_SHARE message so that guest can 431 * release its Hybrid resource. 432 */ 433 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 434 int rv; 435 436 /* send DDS_DEL_SHARE */ 437 D1(vswp, "%s:sending DEL_SHARE msg for " 438 "share(%d)", __func__, vsharep->vs_index); 439 rv = vsw_hio_send_delshare_msg(vsharep); 440 if (rv != 0) { 441 /* 442 * No alternative, reset the port 443 * to force the release of Hybrid 444 * resources. 445 */ 446 vsw_hio_port_reset(vsharep->vs_portp); 447 } 448 } 449 if (max_retries == 1) { 450 /* Last retry, reset the port */ 451 DWARN(vswp, "%s:All retries failed, " 452 " cause a reset to trigger cleanup for " 453 "share(%d)", __func__, vsharep->vs_index); 454 vsw_hio_port_reset(vsharep->vs_portp); 455 } 456 } 457 if (free_shares == hiop->vh_num_shares) { 458 /* Clean up is done */ 459 break; 460 } 461 /* 462 * Release the lock so that reply for DEL_SHARE 463 * messages come and get processed, that is, shares 464 * get freed. 465 * This delay is also needed for the port reset to 466 * release the Hybrid resource. 467 */ 468 mutex_exit(&vswp->hw_lock); 469 delay(drv_usectohz(vsw_hio_cleanup_delay)); 470 mutex_enter(&vswp->hw_lock); 471 max_retries--; 472 } while ((free_shares < hiop->vh_num_shares) && (max_retries > 0)); 473 474 /* By now, all shares should be freed */ 475 ASSERT(free_shares == hiop->vh_num_shares); 476 if (free_shares != hiop->vh_num_shares) { 477 cmn_err(CE_NOTE, "vsw%d: All physical resources " 478 "could not be freed", vswp->instance); 479 } 480 481 kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares); 482 hiop->vh_shares = NULL; 483 hiop->vh_num_shares = 0; 484 mutex_exit(&vswp->hw_lock); 485 RW_EXIT(&plist->lockrw); 486 D1(vswp, "%s:exit\n", __func__); 487 } 488 489 /* 490 * vsw_hio_start_ports -- Start HybridIO for ports that have 491 * already established connection before HybridIO is intialized. 492 */ 493 void 494 vsw_hio_start_ports(vsw_t *vswp) 495 { 496 vsw_port_list_t *plist = &vswp->plist; 497 vsw_port_t *portp; 498 vsw_share_t *vsharep; 499 boolean_t reset; 500 501 if (vswp->hio_capable == B_FALSE) { 502 return; 503 } 504 READ_ENTER(&plist->lockrw); 505 for (portp = plist->head; portp != NULL; portp = portp->p_next) { 506 if ((portp->p_hio_enabled == B_FALSE) || 507 (portp->p_hio_capable == B_FALSE)) { 508 continue; 509 } 510 511 reset = B_FALSE; 512 mutex_enter(&vswp->hw_lock); 513 vsharep = vsw_hio_find_vshare_port(vswp, portp); 514 if (vsharep == NULL) { 515 reset = B_TRUE; 516 } 517 mutex_exit(&vswp->hw_lock); 518 519 if (reset == B_TRUE) { 520 /* Cause a rest to trigger HybridIO setup */ 521 vsw_hio_port_reset(portp); 522 } 523 } 524 RW_EXIT(&plist->lockrw); 525 } 526 527 /* 528 * vsw_hio_start -- Start HybridIO for a guest(given LDC) 529 */ 530 void 531 vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp) 532 { 533 vsw_share_t *vsharep; 534 uint32_t req_id; 535 int rv; 536 537 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 538 mutex_enter(&vswp->hw_lock); 539 if (vswp->hio_capable == B_FALSE) { 540 mutex_exit(&vswp->hw_lock); 541 D2(vswp, "%s:not HIO capable", __func__); 542 return; 543 } 544 545 /* Verify if a share was already allocated */ 546 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 547 if (vsharep != NULL) { 548 mutex_exit(&vswp->hw_lock); 549 D2(vswp, "%s:Share already allocated to ldc=0x%lx", 550 __func__, ldcp->ldc_id); 551 return; 552 } 553 vsharep = vsw_hio_alloc_share(vswp, ldcp); 554 if (vsharep == NULL) { 555 mutex_exit(&vswp->hw_lock); 556 D2(vswp, "%s: no Share available for ldc=0x%lx", 557 __func__, ldcp->ldc_id); 558 return; 559 } 560 req_id = VSW_DDS_NEXT_REQID(vsharep); 561 rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie, 562 vsharep->vs_macaddr, req_id); 563 if (rv != 0) { 564 /* 565 * Failed to send a DDS message, so cleanup now. 566 */ 567 vsw_hio_free_share(vsharep); 568 mutex_exit(&vswp->hw_lock); 569 return; 570 } 571 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 572 mutex_exit(&vswp->hw_lock); 573 574 /* DERR only to print by default */ 575 DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX", 576 ldcp->ldc_id, vsharep->vs_cookie); 577 578 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 579 } 580 581 /* 582 * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc). 583 */ 584 void 585 vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp) 586 { 587 vsw_share_t *vsharep; 588 589 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 590 591 mutex_enter(&vswp->hw_lock); 592 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 593 if (vsharep == NULL) { 594 D1(vswp, "%s:no share found for ldc=0x%lx", 595 __func__, ldcp->ldc_id); 596 mutex_exit(&vswp->hw_lock); 597 return; 598 } 599 vsw_hio_free_share(vsharep); 600 mutex_exit(&vswp->hw_lock); 601 602 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 603 } 604 605 /* 606 * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the guest. 607 */ 608 static int 609 vsw_hio_send_delshare_msg(vsw_share_t *vsharep) 610 { 611 vsw_t *vswp = vsharep->vs_vswp; 612 vsw_port_t *portp; 613 vsw_ldc_list_t *ldcl; 614 vsw_ldc_t *ldcp; 615 uint32_t req_id; 616 uint64_t cookie = vsharep->vs_cookie; 617 uint64_t macaddr = vsharep->vs_macaddr; 618 int rv; 619 620 ASSERT(MUTEX_HELD(&vswp->hw_lock)); 621 mutex_exit(&vswp->hw_lock); 622 623 portp = vsharep->vs_portp; 624 if (portp == NULL) { 625 mutex_enter(&vswp->hw_lock); 626 return (0); 627 } 628 629 ldcl = &portp->p_ldclist; 630 READ_ENTER(&ldcl->lockrw); 631 ldcp = ldcl->head; 632 if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) { 633 RW_EXIT(&ldcl->lockrw); 634 mutex_enter(&vswp->hw_lock); 635 return (0); 636 } 637 req_id = VSW_DDS_NEXT_REQID(vsharep); 638 rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE, 639 cookie, macaddr, req_id); 640 RW_EXIT(&ldcl->lockrw); 641 mutex_enter(&vswp->hw_lock); 642 return (rv); 643 } 644 645 /* 646 * vsw_send_dds_msg -- Send a DDS message. 647 */ 648 static int 649 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t 650 cookie, uint64_t macaddr, uint32_t req_id) 651 { 652 vsw_t *vswp = ldcp->ldc_port->p_vswp; 653 vio_dds_msg_t vmsg; 654 dds_share_msg_t *smsg = &vmsg.msg.share_msg; 655 int rv; 656 657 D1(vswp, "%s:enter\n", __func__); 658 vmsg.tag.vio_msgtype = VIO_TYPE_CTRL; 659 vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO; 660 vmsg.tag.vio_subtype_env = VIO_DDS_INFO; 661 vmsg.tag.vio_sid = ldcp->local_session; 662 vmsg.dds_class = DDS_VNET_NIU; 663 vmsg.dds_subclass = dds_subclass; 664 vmsg.dds_req_id = req_id; 665 smsg->macaddr = macaddr; 666 smsg->cookie = cookie; 667 rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE); 668 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 669 return (rv); 670 } 671 672 /* 673 * vsw_process_dds_msg -- Process a DDS message received from a guest. 674 */ 675 void 676 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg) 677 { 678 vsw_share_t *vsharep; 679 vio_dds_msg_t *dmsg = msg; 680 681 D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id); 682 if (dmsg->dds_class != DDS_VNET_NIU) { 683 /* discard */ 684 return; 685 } 686 mutex_enter(&vswp->hw_lock); 687 /* 688 * We expect to receive DDS messages only from guests that 689 * have HybridIO started. 690 */ 691 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 692 if (vsharep == NULL) { 693 mutex_exit(&vswp->hw_lock); 694 return; 695 } 696 697 switch (dmsg->dds_subclass) { 698 case DDS_VNET_ADD_SHARE: 699 /* A response for ADD_SHARE message. */ 700 D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__); 701 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 702 DWARN(vswp, "%s: invalid ADD_SHARE response message " 703 " share state=0x%X", __func__, vsharep->vs_state); 704 break; 705 } 706 707 if (dmsg->dds_req_id != vsharep->vs_req_id) { 708 DWARN(vswp, "%s: invalid req_id in ADD_SHARE response" 709 " message req_id=0x%X share's req_id=0x%X", 710 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 711 break; 712 } 713 714 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 715 DWARN(vswp, "%s: NACK received for ADD_SHARE" 716 " message ldcid=0x%lx", __func__, ldcp->ldc_id); 717 /* cleanup for NACK */ 718 vsw_hio_free_share(vsharep); 719 } else { 720 D2(vswp, "%s: ACK received for ADD_SHARE", __func__); 721 vsharep->vs_state &= ~VSW_SHARE_DDS_SENT; 722 vsharep->vs_state |= VSW_SHARE_DDS_ACKD; 723 } 724 break; 725 726 case DDS_VNET_DEL_SHARE: 727 /* A response for DEL_SHARE message */ 728 D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__); 729 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 730 DWARN(vswp, "%s: invalid ADD_SHARE response message " 731 " share state=0x%X", __func__, vsharep->vs_state); 732 break; 733 } 734 735 if (dmsg->dds_req_id != vsharep->vs_req_id) { 736 DWARN(vswp, "%s: invalid req_id in ADD_SHARE response" 737 " message share req_id=0x%X share's req_id=0x%X", 738 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 739 break; 740 } 741 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 742 DWARN(vswp, "%s: NACK received for ADD_SHARE", 743 __func__); 744 } 745 /* There is nothing we can do, free share now */ 746 vsw_hio_free_share(vsharep); 747 break; 748 749 case DDS_VNET_REL_SHARE: 750 /* Guest has released Share voluntarily, so free it now */ 751 D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__); 752 /* send ACK */ 753 (void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE); 754 vsw_hio_free_share(vsharep); 755 break; 756 default: 757 DERR(vswp, "%s: Invalid DDS message type=0x%X", 758 __func__, dmsg->dds_subclass); 759 break; 760 } 761 mutex_exit(&vswp->hw_lock); 762 D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id); 763 } 764 765 /* 766 * vsw_send_dds_resp_msg -- Send a DDS response message. 767 */ 768 static int 769 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack) 770 { 771 vsw_t *vswp = ldcp->ldc_port->p_vswp; 772 int rv; 773 774 D1(vswp, "%s:enter\n", __func__); 775 if (ack == B_TRUE) { 776 dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK; 777 dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS; 778 } else { 779 dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK; 780 dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL; 781 } 782 rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE); 783 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 784 return (rv); 785 } 786 787 /* 788 * vsw_hio_port_update -- update Hybrid mode change for a port. 789 */ 790 void 791 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled) 792 { 793 /* Verify if the mode really changed */ 794 if (portp->p_hio_enabled == hio_enabled) { 795 return; 796 } 797 798 if (hio_enabled == B_FALSE) { 799 /* Hybrid Mode is disabled, so stop HybridIO */ 800 vsw_hio_stop_port(portp); 801 portp->p_hio_enabled = B_FALSE; 802 } else { 803 portp->p_hio_enabled = B_TRUE; 804 /* reset the port to initiate HybridIO setup */ 805 vsw_hio_port_reset(portp); 806 } 807 } 808 809 /* 810 * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence 811 * followed is similar to vsw_hio_cleanup(). 812 * 813 */ 814 void 815 vsw_hio_stop_port(vsw_port_t *portp) 816 { 817 vsw_t *vswp = portp->p_vswp; 818 vsw_share_t *vsharep; 819 int max_retries = vsw_hio_max_cleanup_retries; 820 821 D1(vswp, "%s:enter\n", __func__); 822 mutex_enter(&vswp->hw_lock); 823 824 if (vswp->hio_capable == B_FALSE) { 825 mutex_exit(&vswp->hw_lock); 826 return; 827 } 828 829 vsharep = vsw_hio_find_vshare_port(vswp, portp); 830 if (vsharep == NULL) { 831 mutex_exit(&vswp->hw_lock); 832 return; 833 } 834 835 do { 836 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 837 int rv; 838 839 /* send DDS_DEL_SHARE */ 840 D1(vswp, "%s:sending DEL_SHARE msg for " 841 "share(%d)", __func__, vsharep->vs_index); 842 rv = vsw_hio_send_delshare_msg(vsharep); 843 if (rv != 0) { 844 /* 845 * Cause a port reset to trigger 846 * cleanup. 847 */ 848 vsw_hio_port_reset(vsharep->vs_portp); 849 } 850 } 851 if (max_retries == 1) { 852 /* last retry */ 853 DWARN(vswp, "%s:All retries failed, " 854 " cause a reset to trigger cleanup for " 855 "share(%d)", __func__, vsharep->vs_index); 856 vsw_hio_port_reset(vsharep->vs_portp); 857 } 858 859 /* Check if the share still assigned to this port */ 860 if ((vsharep->vs_portp != portp) || 861 (vsharep->vs_state == VSW_SHARE_FREE)) { 862 break; 863 } 864 865 /* 866 * Release the lock so that reply for DEL_SHARE 867 * messages come and get processed, that is, shares 868 * get freed. 869 */ 870 mutex_exit(&vswp->hw_lock); 871 delay(drv_usectohz(vsw_hio_cleanup_delay)); 872 mutex_enter(&vswp->hw_lock); 873 874 /* Check if the share still assigned to this port */ 875 if ((vsharep->vs_portp != portp) || 876 (vsharep->vs_state == VSW_SHARE_FREE)) { 877 break; 878 } 879 max_retries--; 880 } while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0)); 881 882 mutex_exit(&vswp->hw_lock); 883 D1(vswp, "%s:exit\n", __func__); 884 } 885