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/errno.h> 29 #include <sys/debug.h> 30 #include <sys/time.h> 31 #include <sys/sysmacros.h> 32 #include <sys/systm.h> 33 #include <sys/user.h> 34 #include <sys/stropts.h> 35 #include <sys/stream.h> 36 #include <sys/strlog.h> 37 #include <sys/strsubr.h> 38 #include <sys/cmn_err.h> 39 #include <sys/cpu.h> 40 #include <sys/kmem.h> 41 #include <sys/conf.h> 42 #include <sys/ddi.h> 43 #include <sys/sunddi.h> 44 #include <sys/ksynch.h> 45 #include <sys/stat.h> 46 #include <sys/kstat.h> 47 #include <sys/vtrace.h> 48 #include <sys/strsun.h> 49 #include <sys/dlpi.h> 50 #include <sys/ethernet.h> 51 #include <net/if.h> 52 #include <sys/varargs.h> 53 #include <sys/machsystm.h> 54 #include <sys/modctl.h> 55 #include <sys/modhash.h> 56 #include <sys/mac_provider.h> 57 #include <sys/mac_ether.h> 58 #include <sys/taskq.h> 59 #include <sys/note.h> 60 #include <sys/mach_descrip.h> 61 #include <sys/mac.h> 62 #include <sys/mdeg.h> 63 #include <sys/ldc.h> 64 #include <sys/vsw_fdb.h> 65 #include <sys/vsw.h> 66 #include <sys/vio_mailbox.h> 67 #include <sys/vnet_mailbox.h> 68 #include <sys/vnet_common.h> 69 #include <sys/vio_util.h> 70 #include <sys/sdt.h> 71 #include <sys/atomic.h> 72 #include <sys/callb.h> 73 74 75 #define VSW_DDS_NEXT_REQID(vsharep) (++vsharep->vs_req_id) 76 77 extern boolean_t vsw_hio_enabled; /* HybridIO enabled? */ 78 extern int vsw_hio_max_cleanup_retries; 79 extern int vsw_hio_cleanup_delay; 80 81 /* Functions imported from other files */ 82 extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t); 83 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate); 84 extern void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans, 85 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids); 86 87 /* Functions exported to other files */ 88 void vsw_hio_init(vsw_t *vswp); 89 void vsw_hio_cleanup(vsw_t *vswp); 90 void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp); 91 void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp); 92 void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg); 93 void vsw_hio_start_ports(vsw_t *vswp); 94 void vsw_hio_stop_port(vsw_port_t *portp); 95 96 /* Support functions */ 97 static void vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot); 98 static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp); 99 static void vsw_hio_free_share(vsw_share_t *vsharep); 100 static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp); 101 static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id); 102 static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp); 103 static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, 104 uint64_t cookie, uint64_t macaddr, uint32_t req_id); 105 static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack); 106 static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep); 107 static boolean_t vsw_hio_reboot_callb(void *arg, int code); 108 static boolean_t vsw_hio_panic_callb(void *arg, int code); 109 110 /* 111 * Locking strategy for HybridIO is followed as below: 112 * 113 * - As the Shares are associated with a network device, the 114 * the global lock('vswp>mac_lock') is used for all Shares 115 * related operations. 116 * - The 'port->maccl_rwlock' is used to synchronize only the 117 * the operations that operate on that port's mac client. That 118 * is, the share_bind and unbind operations only. 119 * 120 * - The locking hierarchy follows that the global mac_lock is 121 * acquired first and then the ports mac client lock(maccl_rwlock) 122 */ 123 124 125 static kstat_t *vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp); 126 static void vsw_hio_destroy_kstats(vsw_t *vswp); 127 static int vsw_hio_kstats_update(kstat_t *ksp, int rw); 128 129 /* 130 * vsw_hio_init -- Initialize the HybridIO related info. 131 * - Query SHARES and RINGS capability. Both capabilities 132 * need to be supported by the physical-device. 133 */ 134 void 135 vsw_hio_init(vsw_t *vswp) 136 { 137 vsw_hio_t *hiop = &vswp->vhio; 138 int num_shares; 139 int i; 140 141 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 142 D1(vswp, "%s:enter\n", __func__); 143 if (vsw_hio_enabled == B_FALSE) { 144 return; 145 } 146 147 vswp->hio_capable = B_FALSE; 148 num_shares = mac_share_capable(vswp->mh); 149 if (num_shares == 0) { 150 D2(vswp, "%s: %s is not HybridIO capable\n", __func__, 151 vswp->physname); 152 return; 153 } 154 hiop->vh_num_shares = num_shares; 155 hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) * 156 hiop->vh_num_shares), KM_SLEEP); 157 for (i = 0; i < hiop->vh_num_shares; i++) { 158 hiop->vh_shares[i].vs_state = VSW_SHARE_FREE; 159 hiop->vh_shares[i].vs_index = i; 160 hiop->vh_shares[i].vs_vswp = vswp; 161 } 162 vswp->hio_capable = B_TRUE; 163 164 /* 165 * Register to get reboot and panic events so that 166 * we can cleanup HybridIO resources gracefully. 167 */ 168 vswp->hio_reboot_cb_id = callb_add(vsw_hio_reboot_callb, 169 (void *)vswp, CB_CL_MDBOOT, "vsw_hio"); 170 171 vswp->hio_panic_cb_id = callb_add(vsw_hio_panic_callb, 172 (void *)vswp, CB_CL_PANIC, "vsw_hio"); 173 174 /* setup kstats for hybrid resources */ 175 hiop->vh_ksp = vsw_hio_setup_kstats(DRV_NAME, "hio", vswp); 176 if (hiop->vh_ksp == NULL) { 177 DERR(vswp, "%s: kstats setup failed", __func__); 178 } 179 180 D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__, 181 vswp->physname, hiop->vh_num_shares); 182 D1(vswp, "%s:exit\n", __func__); 183 } 184 185 /* 186 * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain. 187 * - Allocate a free share. 188 * - Bind the Guest's MAC address. 189 */ 190 static vsw_share_t * 191 vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp) 192 { 193 vsw_share_t *vsharep; 194 vsw_port_t *portp = ldcp->ldc_port; 195 uint64_t ldc_id = ldcp->ldc_id; 196 int rv; 197 198 D1(vswp, "%s:enter\n", __func__); 199 vsharep = vsw_hio_find_free_share(vswp); 200 if (vsharep == NULL) { 201 /* No free shares available */ 202 return (NULL); 203 } 204 205 WRITE_ENTER(&portp->maccl_rwlock); 206 rv = mac_share_bind(portp->p_mch, ldc_id, &vsharep->vs_cookie); 207 RW_EXIT(&portp->maccl_rwlock); 208 if (rv != 0) { 209 return (NULL); 210 } 211 212 /* Cache some useful info */ 213 vsharep->vs_ldcid = ldcp->ldc_id; 214 vsharep->vs_macaddr = vnet_macaddr_strtoul( 215 portp->p_macaddr.ether_addr_octet); 216 vsharep->vs_portp = ldcp->ldc_port; 217 vsharep->vs_state |= VSW_SHARE_ASSIGNED; 218 219 D1(vswp, "%s:exit\n", __func__); 220 return (vsharep); 221 } 222 223 /* 224 * vsw_hio_find_free_share -- Find a free Share. 225 */ 226 static vsw_share_t * 227 vsw_hio_find_free_share(vsw_t *vswp) 228 { 229 vsw_hio_t *hiop = &vswp->vhio; 230 vsw_share_t *vsharep; 231 int i; 232 233 D1(vswp, "%s:enter\n", __func__); 234 for (i = 0; i < hiop->vh_num_shares; i++) { 235 vsharep = &hiop->vh_shares[i]; 236 if (vsharep->vs_state == VSW_SHARE_FREE) { 237 D1(vswp, "%s:Returning free share(%d)\n", 238 __func__, vsharep->vs_index); 239 return (vsharep); 240 } 241 } 242 D1(vswp, "%s:no free share\n", __func__); 243 return (NULL); 244 } 245 246 /* 247 * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding 248 * share structure. 249 */ 250 static vsw_share_t * 251 vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id) 252 { 253 vsw_hio_t *hiop = &vswp->vhio; 254 vsw_share_t *vsharep; 255 int i; 256 257 D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id); 258 for (i = 0; i < hiop->vh_num_shares; i++) { 259 vsharep = &hiop->vh_shares[i]; 260 if (vsharep->vs_state == VSW_SHARE_FREE) { 261 continue; 262 } 263 if (vsharep->vs_ldcid == ldc_id) { 264 D1(vswp, "%s:returning share(%d)", 265 __func__, vsharep->vs_index); 266 return (vsharep); 267 } 268 } 269 D1(vswp, "%s:returning NULL", __func__); 270 return (NULL); 271 } 272 273 /* 274 * vsw_hio_find_vshare_port -- Given portp, find the corresponding 275 * share structure. 276 */ 277 static vsw_share_t * 278 vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp) 279 { 280 vsw_hio_t *hiop = &vswp->vhio; 281 vsw_share_t *vsharep; 282 int i; 283 284 D1(vswp, "%s:enter, portp=0x%p", __func__, portp); 285 for (i = 0; i < hiop->vh_num_shares; i++) { 286 vsharep = &hiop->vh_shares[i]; 287 if (vsharep->vs_state == VSW_SHARE_FREE) { 288 continue; 289 } 290 if (vsharep->vs_portp == portp) { 291 D1(vswp, "%s:returning share(%d)", 292 __func__, vsharep->vs_index); 293 return (vsharep); 294 } 295 } 296 D1(vswp, "%s:returning NULL", __func__); 297 return (NULL); 298 } 299 300 /* 301 * vsw_hio_free_share -- Unbind the MAC address and free share. 302 */ 303 static void 304 vsw_hio_free_share(vsw_share_t *vsharep) 305 { 306 vsw_t *vswp = vsharep->vs_vswp; 307 vsw_port_t *portp = vsharep->vs_portp; 308 309 D1(vswp, "%s:enter\n", __func__); 310 311 WRITE_ENTER(&portp->maccl_rwlock); 312 mac_share_unbind(portp->p_mch); 313 RW_EXIT(&portp->maccl_rwlock); 314 vsharep->vs_state = VSW_SHARE_FREE; 315 vsharep->vs_macaddr = 0; 316 vsharep->vs_portp = NULL; 317 318 /* DERR only for printing by default */ 319 DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX", 320 vsharep->vs_ldcid, vsharep->vs_cookie); 321 D1(vswp, "%s:exit\n", __func__); 322 } 323 324 325 /* 326 * vsw_hio_cleanup -- Cleanup the HybridIO. It unregisters the callbs 327 * and frees all shares. 328 */ 329 void 330 vsw_hio_cleanup(vsw_t *vswp) 331 { 332 D1(vswp, "%s:enter\n", __func__); 333 334 /* Unregister reboot and panic callbs. */ 335 if (vswp->hio_reboot_cb_id) { 336 (void) callb_delete(vswp->hio_reboot_cb_id); 337 vswp->hio_reboot_cb_id = 0; 338 } 339 if (vswp->hio_panic_cb_id) { 340 (void) callb_delete(vswp->hio_panic_cb_id); 341 vswp->hio_panic_cb_id = 0; 342 } 343 vsw_hio_free_all_shares(vswp, B_FALSE); 344 vsw_hio_destroy_kstats(vswp); 345 D1(vswp, "%s:exit\n", __func__); 346 } 347 348 /* 349 * vsw_hio_free_all_shares -- A routine to free all shares gracefully. 350 * The following are the steps followed to accomplish this: 351 * 352 * - First clear 'hio_capable' to avoid further share allocations. 353 * - If a share is in accepted(ACKD) state, that means the guest 354 * has HybridIO setup etc. If so, send a DEL_SHARE message and 355 * give some time(delay) for the guest to ACK. 356 * - If the Share is another state, give some time to transition to 357 * ACKD state, then try the above. 358 * - After max retries, reset the ports to brute force the shares 359 * to be freed. Give a little delay for the LDC reset code to 360 * free the Share. 361 */ 362 static void 363 vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot) 364 { 365 vsw_hio_t *hiop = &vswp->vhio; 366 vsw_port_list_t *plist = &vswp->plist; 367 vsw_share_t *vsharep; 368 int free_shares = 0; 369 int max_retries = vsw_hio_max_cleanup_retries; 370 int i; 371 372 D1(vswp, "%s:enter\n", __func__); 373 374 /* 375 * Acquire plist->lockrw to make the locking a bit easier 376 * and keep the ports in a stable state while we are cleaningup 377 * HybridIO. 378 */ 379 READ_ENTER(&plist->lockrw); 380 mutex_enter(&vswp->mac_lock); 381 /* 382 * first clear the hio_capable flag so that no more 383 * HybridIO operations are initiated. 384 */ 385 vswp->hio_capable = B_FALSE; 386 387 do { 388 free_shares = 0; 389 for (i = 0; i < hiop->vh_num_shares; i++) { 390 vsharep = &hiop->vh_shares[i]; 391 if (vsharep->vs_state == VSW_SHARE_FREE) { 392 free_shares++; 393 continue; 394 } 395 /* 396 * If the share is in DDS_ACKD state, then 397 * send DEL_SHARE message so that guest can 398 * release its Hybrid resource. 399 */ 400 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 401 int rv; 402 403 /* send DDS_DEL_SHARE */ 404 D1(vswp, "%s:sending DEL_SHARE msg for " 405 "share(%d)", __func__, vsharep->vs_index); 406 rv = vsw_hio_send_delshare_msg(vsharep); 407 if (rv != 0) { 408 /* 409 * No alternative, reset the port 410 * to force the release of Hybrid 411 * resources. 412 */ 413 vsw_hio_port_reset(vsharep->vs_portp, 414 B_FALSE); 415 } 416 } 417 if (max_retries == 1) { 418 /* 419 * Last retry, reset the port. 420 * If it is reboot case, issue an immediate 421 * reset. 422 */ 423 DWARN(vswp, "%s:All retries failed, " 424 " cause a reset to trigger cleanup for " 425 "share(%d)", __func__, vsharep->vs_index); 426 vsw_hio_port_reset(vsharep->vs_portp, reboot); 427 } 428 } 429 if (free_shares == hiop->vh_num_shares) { 430 /* Clean up is done */ 431 break; 432 } 433 /* 434 * Release the lock so that reply for DEL_SHARE 435 * messages come and get processed, that is, shares 436 * get freed. 437 * This delay is also needed for the port reset to 438 * release the Hybrid resource. 439 */ 440 mutex_exit(&vswp->mac_lock); 441 drv_usecwait(vsw_hio_cleanup_delay); 442 mutex_enter(&vswp->mac_lock); 443 max_retries--; 444 } while ((free_shares < hiop->vh_num_shares) && (max_retries > 0)); 445 446 /* By now, all shares should be freed */ 447 if (free_shares != hiop->vh_num_shares) { 448 if (reboot == B_FALSE) { 449 cmn_err(CE_NOTE, "vsw%d: All physical resources " 450 "could not be freed", vswp->instance); 451 } 452 } 453 454 kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares); 455 hiop->vh_shares = NULL; 456 hiop->vh_num_shares = 0; 457 mutex_exit(&vswp->mac_lock); 458 RW_EXIT(&plist->lockrw); 459 D1(vswp, "%s:exit\n", __func__); 460 } 461 462 /* 463 * vsw_hio_start_ports -- Start HybridIO for ports that have 464 * already established connection before HybridIO is intialized. 465 */ 466 void 467 vsw_hio_start_ports(vsw_t *vswp) 468 { 469 vsw_port_list_t *plist = &vswp->plist; 470 vsw_port_t *portp; 471 vsw_share_t *vsharep; 472 boolean_t reset; 473 474 if (vswp->hio_capable == B_FALSE) { 475 return; 476 } 477 READ_ENTER(&plist->lockrw); 478 for (portp = plist->head; portp != NULL; portp = portp->p_next) { 479 if ((portp->p_hio_enabled == B_FALSE) || 480 (portp->p_hio_capable == B_FALSE)) { 481 continue; 482 } 483 484 reset = B_FALSE; 485 mutex_enter(&vswp->mac_lock); 486 vsharep = vsw_hio_find_vshare_port(vswp, portp); 487 if (vsharep == NULL) { 488 reset = B_TRUE; 489 } 490 mutex_exit(&vswp->mac_lock); 491 492 if (reset == B_TRUE) { 493 /* Cause a rest to trigger HybridIO setup */ 494 vsw_hio_port_reset(portp, B_FALSE); 495 } 496 } 497 RW_EXIT(&plist->lockrw); 498 } 499 500 /* 501 * vsw_hio_start -- Start HybridIO for a guest(given LDC) 502 */ 503 void 504 vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp) 505 { 506 vsw_share_t *vsharep; 507 uint32_t req_id; 508 int rv; 509 510 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 511 mutex_enter(&vswp->mac_lock); 512 if (vswp->hio_capable == B_FALSE) { 513 mutex_exit(&vswp->mac_lock); 514 D2(vswp, "%s:not HIO capable", __func__); 515 return; 516 } 517 518 /* Verify if a share was already allocated */ 519 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 520 if (vsharep != NULL) { 521 mutex_exit(&vswp->mac_lock); 522 D2(vswp, "%s:Share already allocated to ldc=0x%lx", 523 __func__, ldcp->ldc_id); 524 return; 525 } 526 vsharep = vsw_hio_alloc_share(vswp, ldcp); 527 if (vsharep == NULL) { 528 mutex_exit(&vswp->mac_lock); 529 D2(vswp, "%s: no Share available for ldc=0x%lx", 530 __func__, ldcp->ldc_id); 531 return; 532 } 533 req_id = VSW_DDS_NEXT_REQID(vsharep); 534 rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie, 535 vsharep->vs_macaddr, req_id); 536 if (rv != 0) { 537 /* 538 * Failed to send a DDS message, so cleanup now. 539 */ 540 vsw_hio_free_share(vsharep); 541 mutex_exit(&vswp->mac_lock); 542 return; 543 } 544 vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD; 545 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 546 mutex_exit(&vswp->mac_lock); 547 548 /* DERR only to print by default */ 549 DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX", 550 ldcp->ldc_id, vsharep->vs_cookie); 551 552 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 553 } 554 555 /* 556 * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc). 557 */ 558 void 559 vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp) 560 { 561 vsw_share_t *vsharep; 562 563 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 564 565 mutex_enter(&vswp->mac_lock); 566 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 567 if (vsharep == NULL) { 568 D1(vswp, "%s:no share found for ldc=0x%lx", 569 __func__, ldcp->ldc_id); 570 mutex_exit(&vswp->mac_lock); 571 return; 572 } 573 vsw_hio_free_share(vsharep); 574 mutex_exit(&vswp->mac_lock); 575 576 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 577 } 578 579 /* 580 * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the guest. 581 */ 582 static int 583 vsw_hio_send_delshare_msg(vsw_share_t *vsharep) 584 { 585 vsw_t *vswp = vsharep->vs_vswp; 586 vsw_port_t *portp; 587 vsw_ldc_list_t *ldcl; 588 vsw_ldc_t *ldcp; 589 uint32_t req_id; 590 uint64_t cookie = vsharep->vs_cookie; 591 uint64_t macaddr = vsharep->vs_macaddr; 592 int rv; 593 594 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 595 mutex_exit(&vswp->mac_lock); 596 597 portp = vsharep->vs_portp; 598 if (portp == NULL) { 599 mutex_enter(&vswp->mac_lock); 600 return (0); 601 } 602 603 ldcl = &portp->p_ldclist; 604 READ_ENTER(&ldcl->lockrw); 605 ldcp = ldcl->head; 606 if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) { 607 RW_EXIT(&ldcl->lockrw); 608 mutex_enter(&vswp->mac_lock); 609 return (0); 610 } 611 req_id = VSW_DDS_NEXT_REQID(vsharep); 612 rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE, 613 cookie, macaddr, req_id); 614 615 RW_EXIT(&ldcl->lockrw); 616 mutex_enter(&vswp->mac_lock); 617 if (rv == 0) { 618 vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD; 619 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 620 } 621 return (rv); 622 } 623 624 /* 625 * vsw_send_dds_msg -- Send a DDS message. 626 */ 627 static int 628 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t 629 cookie, uint64_t macaddr, uint32_t req_id) 630 { 631 vsw_t *vswp = ldcp->ldc_port->p_vswp; 632 vio_dds_msg_t vmsg; 633 dds_share_msg_t *smsg = &vmsg.msg.share_msg; 634 int rv; 635 636 D1(vswp, "%s:enter\n", __func__); 637 vmsg.tag.vio_msgtype = VIO_TYPE_CTRL; 638 vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO; 639 vmsg.tag.vio_subtype_env = VIO_DDS_INFO; 640 vmsg.tag.vio_sid = ldcp->local_session; 641 vmsg.dds_class = DDS_VNET_NIU; 642 vmsg.dds_subclass = dds_subclass; 643 vmsg.dds_req_id = req_id; 644 smsg->macaddr = macaddr; 645 smsg->cookie = cookie; 646 rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE); 647 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 648 return (rv); 649 } 650 651 /* 652 * vsw_process_dds_msg -- Process a DDS message received from a guest. 653 */ 654 void 655 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg) 656 { 657 vsw_share_t *vsharep; 658 vio_dds_msg_t *dmsg = msg; 659 660 D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id); 661 if (dmsg->dds_class != DDS_VNET_NIU) { 662 /* discard */ 663 return; 664 } 665 mutex_enter(&vswp->mac_lock); 666 /* 667 * We expect to receive DDS messages only from guests that 668 * have HybridIO started. 669 */ 670 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 671 if (vsharep == NULL) { 672 mutex_exit(&vswp->mac_lock); 673 return; 674 } 675 676 switch (dmsg->dds_subclass) { 677 case DDS_VNET_ADD_SHARE: 678 /* A response for ADD_SHARE message. */ 679 D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__); 680 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 681 DWARN(vswp, "%s: invalid ADD_SHARE response message " 682 " share state=0x%X", __func__, vsharep->vs_state); 683 break; 684 } 685 686 if (dmsg->dds_req_id != vsharep->vs_req_id) { 687 DWARN(vswp, "%s: invalid req_id in ADD_SHARE response" 688 " message req_id=0x%X share's req_id=0x%X", 689 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 690 break; 691 } 692 693 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 694 DWARN(vswp, "%s: NACK received for ADD_SHARE" 695 " message ldcid=0x%lx", __func__, ldcp->ldc_id); 696 /* cleanup for NACK */ 697 vsw_hio_free_share(vsharep); 698 } else { 699 D2(vswp, "%s: ACK received for ADD_SHARE", __func__); 700 vsharep->vs_state &= ~VSW_SHARE_DDS_SENT; 701 vsharep->vs_state |= VSW_SHARE_DDS_ACKD; 702 } 703 break; 704 705 case DDS_VNET_DEL_SHARE: 706 /* A response for DEL_SHARE message */ 707 D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__); 708 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 709 DWARN(vswp, "%s: invalid DEL_SHARE response message " 710 " share state=0x%X", __func__, vsharep->vs_state); 711 break; 712 } 713 714 if (dmsg->dds_req_id != vsharep->vs_req_id) { 715 DWARN(vswp, "%s: invalid req_id in DEL_SHARE response" 716 " message share req_id=0x%X share's req_id=0x%X", 717 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 718 break; 719 } 720 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 721 DWARN(vswp, "%s: NACK received for DEL_SHARE", 722 __func__); 723 } 724 725 /* There is nothing we can do, free share now */ 726 vsw_hio_free_share(vsharep); 727 break; 728 729 case DDS_VNET_REL_SHARE: 730 /* Guest has released Share voluntarily, so free it now */ 731 D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__); 732 /* send ACK */ 733 (void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE); 734 vsw_hio_free_share(vsharep); 735 break; 736 default: 737 DERR(vswp, "%s: Invalid DDS message type=0x%X", 738 __func__, dmsg->dds_subclass); 739 break; 740 } 741 mutex_exit(&vswp->mac_lock); 742 D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id); 743 } 744 745 /* 746 * vsw_send_dds_resp_msg -- Send a DDS response message. 747 */ 748 static int 749 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack) 750 { 751 vsw_t *vswp = ldcp->ldc_port->p_vswp; 752 int rv; 753 754 D1(vswp, "%s:enter\n", __func__); 755 if (ack == B_TRUE) { 756 dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK; 757 dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS; 758 } else { 759 dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK; 760 dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL; 761 } 762 rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE); 763 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 764 return (rv); 765 } 766 767 /* 768 * vsw_hio_port_update -- update Hybrid mode change for a port. 769 */ 770 void 771 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled) 772 { 773 /* Verify if the mode really changed */ 774 if (portp->p_hio_enabled == hio_enabled) { 775 return; 776 } 777 778 if (hio_enabled == B_FALSE) { 779 /* Hybrid Mode is disabled, so stop HybridIO */ 780 vsw_hio_stop_port(portp); 781 portp->p_hio_enabled = B_FALSE; 782 783 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 784 } else { 785 portp->p_hio_enabled = B_TRUE; 786 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 787 788 /* reset the port to initiate HybridIO setup */ 789 vsw_hio_port_reset(portp, B_FALSE); 790 } 791 } 792 793 /* 794 * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence 795 * followed is similar to vsw_hio_free_all_shares(). 796 * 797 */ 798 void 799 vsw_hio_stop_port(vsw_port_t *portp) 800 { 801 vsw_t *vswp = portp->p_vswp; 802 vsw_share_t *vsharep; 803 int max_retries = vsw_hio_max_cleanup_retries; 804 805 D1(vswp, "%s:enter\n", __func__); 806 mutex_enter(&vswp->mac_lock); 807 808 if (vswp->hio_capable == B_FALSE) { 809 mutex_exit(&vswp->mac_lock); 810 return; 811 } 812 813 vsharep = vsw_hio_find_vshare_port(vswp, portp); 814 if (vsharep == NULL) { 815 mutex_exit(&vswp->mac_lock); 816 return; 817 } 818 819 do { 820 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 821 int rv; 822 823 /* send DDS_DEL_SHARE */ 824 D1(vswp, "%s:sending DEL_SHARE msg for " 825 "share(%d)", __func__, vsharep->vs_index); 826 rv = vsw_hio_send_delshare_msg(vsharep); 827 if (rv != 0) { 828 /* 829 * Cause a port reset to trigger 830 * cleanup. 831 */ 832 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 833 } 834 } 835 if (max_retries == 1) { 836 /* last retry */ 837 DWARN(vswp, "%s:All retries failed, " 838 " cause a reset to trigger cleanup for " 839 "share(%d)", __func__, vsharep->vs_index); 840 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 841 } 842 843 /* Check if the share still assigned to this port */ 844 if ((vsharep->vs_portp != portp) || 845 (vsharep->vs_state == VSW_SHARE_FREE)) { 846 break; 847 } 848 849 /* 850 * Release the lock so that reply for DEL_SHARE 851 * messages come and get processed, that is, shares 852 * get freed. 853 */ 854 mutex_exit(&vswp->mac_lock); 855 drv_usecwait(vsw_hio_cleanup_delay); 856 mutex_enter(&vswp->mac_lock); 857 858 /* Check if the share still assigned to this port */ 859 if ((vsharep->vs_portp != portp) || 860 (vsharep->vs_state == VSW_SHARE_FREE)) { 861 break; 862 } 863 max_retries--; 864 } while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0)); 865 866 mutex_exit(&vswp->mac_lock); 867 D1(vswp, "%s:exit\n", __func__); 868 } 869 870 /* 871 * vsw_hio_rest_all -- Resets all ports that have shares allocated. 872 * It is called only in the panic code path, so the LDC channels 873 * are reset immediately. 874 */ 875 static void 876 vsw_hio_reset_all(vsw_t *vswp) 877 { 878 vsw_hio_t *hiop = &vswp->vhio; 879 vsw_share_t *vsharep; 880 int i; 881 882 D1(vswp, "%s:enter\n", __func__); 883 884 if (vswp->hio_capable != B_TRUE) 885 return; 886 887 for (i = 0; i < hiop->vh_num_shares; i++) { 888 vsharep = &hiop->vh_shares[i]; 889 if (vsharep->vs_state == VSW_SHARE_FREE) { 890 continue; 891 } 892 /* 893 * Reset the port with immediate flag enabled, 894 * to cause LDC reset immediately. 895 */ 896 vsw_hio_port_reset(vsharep->vs_portp, B_TRUE); 897 } 898 D1(vswp, "%s:exit\n", __func__); 899 } 900 901 /* 902 * vsw_hio_reboot_callb -- Called for reboot event. It tries to 903 * free all currently allocated shares. 904 */ 905 /* ARGSUSED */ 906 static boolean_t 907 vsw_hio_reboot_callb(void *arg, int code) 908 { 909 vsw_t *vswp = arg; 910 911 D1(vswp, "%s:enter\n", __func__); 912 vsw_hio_free_all_shares(vswp, B_TRUE); 913 D1(vswp, "%s:exit\n", __func__); 914 return (B_TRUE); 915 } 916 917 /* 918 * vsw_hio_panic_callb -- Called from panic event. It resets all 919 * the ports that have shares allocated. This is done to 920 * trigger the cleanup in the guest ahead of HV reset. 921 */ 922 /* ARGSUSED */ 923 static boolean_t 924 vsw_hio_panic_callb(void *arg, int code) 925 { 926 vsw_t *vswp = arg; 927 928 D1(vswp, "%s:enter\n", __func__); 929 vsw_hio_reset_all(vswp); 930 D1(vswp, "%s:exit\n", __func__); 931 return (B_TRUE); 932 } 933 934 /* 935 * Setup kstats for hio statistics. 936 */ 937 static kstat_t * 938 vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp) 939 { 940 kstat_t *ksp; 941 vsw_hio_kstats_t *hiokp; 942 vsw_hio_t *hiop; 943 char share_assigned_info[MAXNAMELEN]; 944 size_t size; 945 int i; 946 947 hiop = &vswp->vhio; 948 /* 949 * vsw_hio_stats_t structure is variable size structure 950 * having fields defined only for one share. So, we need 951 * allocate additional space for the rest of the shares. 952 */ 953 size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t); 954 ASSERT(hiop->vh_num_shares >= 1); 955 size += ((hiop->vh_num_shares - 1) * 2); 956 957 ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc", 958 KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL); 959 960 if (ksp == NULL) { 961 return (NULL); 962 } 963 hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) * 964 size, KM_SLEEP); 965 ksp->ks_data = hiokp; 966 967 hiop->vh_ksp = ksp; 968 hiop->vh_kstatsp = hiokp; 969 hiop->vh_kstat_size = size; 970 971 kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR); 972 kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares", 973 KSTAT_DATA_ULONG); 974 975 for (i = 0; i < hiop->vh_num_shares; i++) { 976 (void) sprintf(share_assigned_info, "%s%d", "hio_share_", i); 977 kstat_named_init(&(hiokp->share[i].assigned), 978 share_assigned_info, KSTAT_DATA_ULONG); 979 980 (void) sprintf(share_assigned_info, "%s%d%s", 981 "hio_share_", i, "_state"); 982 kstat_named_init(&(hiokp->share[i].state), 983 share_assigned_info, KSTAT_DATA_ULONG); 984 } 985 986 ksp->ks_update = vsw_hio_kstats_update; 987 ksp->ks_private = (void *)vswp; 988 kstat_install(ksp); 989 return (ksp); 990 } 991 992 /* 993 * Destroy hio kstats. 994 */ 995 static void 996 vsw_hio_destroy_kstats(vsw_t *vswp) 997 { 998 kstat_t *ksp; 999 vsw_hio_t *hiop; 1000 1001 ASSERT(vswp != NULL); 1002 1003 ksp = vswp->vhio.vh_ksp; 1004 hiop = &vswp->vhio; 1005 if (ksp != NULL) { 1006 kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) * 1007 hiop->vh_kstat_size); 1008 kstat_delete(ksp); 1009 hiop->vh_kstatsp = NULL; 1010 hiop->vh_ksp = NULL; 1011 } 1012 } 1013 1014 /* 1015 * Update hio kstats. 1016 */ 1017 static int 1018 vsw_hio_kstats_update(kstat_t *ksp, int rw) 1019 { 1020 vsw_t *vswp; 1021 vsw_hio_t *hiop; 1022 vsw_hio_kstats_t *hiokp; 1023 int i; 1024 1025 vswp = (vsw_t *)ksp->ks_private; 1026 ASSERT(vswp != NULL); 1027 1028 hiop = &vswp->vhio; 1029 hiokp = hiop->vh_kstatsp; 1030 1031 if (rw == KSTAT_READ) { 1032 if (vswp->hio_capable) { 1033 (void) strcpy(hiokp->hio_capable.value.c, "Yes"); 1034 } else { 1035 /* not hio capable, just return */ 1036 (void) strcpy(hiokp->hio_capable.value.c, "No"); 1037 return (0); 1038 } 1039 1040 mutex_enter(&vswp->mac_lock); 1041 hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares; 1042 for (i = 0; i < hiop->vh_num_shares; i++) { 1043 hiokp->share[i].assigned.value.ul = 1044 hiop->vh_shares[i].vs_macaddr; 1045 hiokp->share[i].state.value.ul = 1046 hiop->vh_shares[i].vs_state; 1047 } 1048 mutex_exit(&vswp->mac_lock); 1049 } else { 1050 return (EACCES); 1051 } 1052 1053 return (0); 1054 } 1055