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 2010 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_t *ldcp; 588 uint32_t req_id; 589 uint64_t cookie = vsharep->vs_cookie; 590 uint64_t macaddr = vsharep->vs_macaddr; 591 int rv; 592 593 ASSERT(MUTEX_HELD(&vswp->mac_lock)); 594 mutex_exit(&vswp->mac_lock); 595 596 portp = vsharep->vs_portp; 597 if (portp == NULL) { 598 mutex_enter(&vswp->mac_lock); 599 return (0); 600 } 601 602 ldcp = portp->ldcp; 603 if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) { 604 mutex_enter(&vswp->mac_lock); 605 return (0); 606 } 607 req_id = VSW_DDS_NEXT_REQID(vsharep); 608 rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE, 609 cookie, macaddr, req_id); 610 611 mutex_enter(&vswp->mac_lock); 612 if (rv == 0) { 613 vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD; 614 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 615 } 616 return (rv); 617 } 618 619 /* 620 * vsw_send_dds_msg -- Send a DDS message. 621 */ 622 static int 623 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t 624 cookie, uint64_t macaddr, uint32_t req_id) 625 { 626 vsw_t *vswp = ldcp->ldc_port->p_vswp; 627 vio_dds_msg_t vmsg; 628 dds_share_msg_t *smsg = &vmsg.msg.share_msg; 629 int rv; 630 631 D1(vswp, "%s:enter\n", __func__); 632 vmsg.tag.vio_msgtype = VIO_TYPE_CTRL; 633 vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO; 634 vmsg.tag.vio_subtype_env = VIO_DDS_INFO; 635 vmsg.tag.vio_sid = ldcp->local_session; 636 vmsg.dds_class = DDS_VNET_NIU; 637 vmsg.dds_subclass = dds_subclass; 638 vmsg.dds_req_id = req_id; 639 smsg->macaddr = macaddr; 640 smsg->cookie = cookie; 641 rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE); 642 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 643 return (rv); 644 } 645 646 /* 647 * vsw_process_dds_msg -- Process a DDS message received from a guest. 648 */ 649 void 650 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg) 651 { 652 vsw_share_t *vsharep; 653 vio_dds_msg_t *dmsg = msg; 654 655 D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id); 656 if (dmsg->dds_class != DDS_VNET_NIU) { 657 /* discard */ 658 return; 659 } 660 mutex_enter(&vswp->mac_lock); 661 /* 662 * We expect to receive DDS messages only from guests that 663 * have HybridIO started. 664 */ 665 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 666 if (vsharep == NULL) { 667 mutex_exit(&vswp->mac_lock); 668 return; 669 } 670 671 switch (dmsg->dds_subclass) { 672 case DDS_VNET_ADD_SHARE: 673 /* A response for ADD_SHARE message. */ 674 D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__); 675 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 676 DWARN(vswp, "%s: invalid ADD_SHARE response message " 677 " share state=0x%X", __func__, vsharep->vs_state); 678 break; 679 } 680 681 if (dmsg->dds_req_id != vsharep->vs_req_id) { 682 DWARN(vswp, "%s: invalid req_id in ADD_SHARE response" 683 " message req_id=0x%X share's req_id=0x%X", 684 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 685 break; 686 } 687 688 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 689 DWARN(vswp, "%s: NACK received for ADD_SHARE" 690 " message ldcid=0x%lx", __func__, ldcp->ldc_id); 691 /* cleanup for NACK */ 692 vsw_hio_free_share(vsharep); 693 } else { 694 D2(vswp, "%s: ACK received for ADD_SHARE", __func__); 695 vsharep->vs_state &= ~VSW_SHARE_DDS_SENT; 696 vsharep->vs_state |= VSW_SHARE_DDS_ACKD; 697 } 698 break; 699 700 case DDS_VNET_DEL_SHARE: 701 /* A response for DEL_SHARE message */ 702 D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__); 703 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 704 DWARN(vswp, "%s: invalid DEL_SHARE response message " 705 " share state=0x%X", __func__, vsharep->vs_state); 706 break; 707 } 708 709 if (dmsg->dds_req_id != vsharep->vs_req_id) { 710 DWARN(vswp, "%s: invalid req_id in DEL_SHARE response" 711 " message share req_id=0x%X share's req_id=0x%X", 712 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 713 break; 714 } 715 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 716 DWARN(vswp, "%s: NACK received for DEL_SHARE", 717 __func__); 718 } 719 720 /* There is nothing we can do, free share now */ 721 vsw_hio_free_share(vsharep); 722 break; 723 724 case DDS_VNET_REL_SHARE: 725 /* Guest has released Share voluntarily, so free it now */ 726 D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__); 727 /* send ACK */ 728 (void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE); 729 vsw_hio_free_share(vsharep); 730 break; 731 default: 732 DERR(vswp, "%s: Invalid DDS message type=0x%X", 733 __func__, dmsg->dds_subclass); 734 break; 735 } 736 mutex_exit(&vswp->mac_lock); 737 D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id); 738 } 739 740 /* 741 * vsw_send_dds_resp_msg -- Send a DDS response message. 742 */ 743 static int 744 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack) 745 { 746 vsw_t *vswp = ldcp->ldc_port->p_vswp; 747 int rv; 748 749 D1(vswp, "%s:enter\n", __func__); 750 if (ack == B_TRUE) { 751 dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK; 752 dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS; 753 } else { 754 dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK; 755 dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL; 756 } 757 rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE); 758 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 759 return (rv); 760 } 761 762 /* 763 * vsw_hio_port_update -- update Hybrid mode change for a port. 764 */ 765 void 766 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled) 767 { 768 /* Verify if the mode really changed */ 769 if (portp->p_hio_enabled == hio_enabled) { 770 return; 771 } 772 773 if (hio_enabled == B_FALSE) { 774 /* Hybrid Mode is disabled, so stop HybridIO */ 775 vsw_hio_stop_port(portp); 776 portp->p_hio_enabled = B_FALSE; 777 778 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 779 } else { 780 portp->p_hio_enabled = B_TRUE; 781 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 782 783 /* reset the port to initiate HybridIO setup */ 784 vsw_hio_port_reset(portp, B_FALSE); 785 } 786 } 787 788 /* 789 * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence 790 * followed is similar to vsw_hio_free_all_shares(). 791 * 792 */ 793 void 794 vsw_hio_stop_port(vsw_port_t *portp) 795 { 796 vsw_t *vswp = portp->p_vswp; 797 vsw_share_t *vsharep; 798 int max_retries = vsw_hio_max_cleanup_retries; 799 800 D1(vswp, "%s:enter\n", __func__); 801 mutex_enter(&vswp->mac_lock); 802 803 if (vswp->hio_capable == B_FALSE) { 804 mutex_exit(&vswp->mac_lock); 805 return; 806 } 807 808 vsharep = vsw_hio_find_vshare_port(vswp, portp); 809 if (vsharep == NULL) { 810 mutex_exit(&vswp->mac_lock); 811 return; 812 } 813 814 do { 815 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 816 int rv; 817 818 /* send DDS_DEL_SHARE */ 819 D1(vswp, "%s:sending DEL_SHARE msg for " 820 "share(%d)", __func__, vsharep->vs_index); 821 rv = vsw_hio_send_delshare_msg(vsharep); 822 if (rv != 0) { 823 /* 824 * Cause a port reset to trigger 825 * cleanup. 826 */ 827 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 828 } 829 } 830 if (max_retries == 1) { 831 /* last retry */ 832 DWARN(vswp, "%s:All retries failed, " 833 " cause a reset to trigger cleanup for " 834 "share(%d)", __func__, vsharep->vs_index); 835 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 836 } 837 838 /* Check if the share still assigned to this port */ 839 if ((vsharep->vs_portp != portp) || 840 (vsharep->vs_state == VSW_SHARE_FREE)) { 841 break; 842 } 843 844 /* 845 * Release the lock so that reply for DEL_SHARE 846 * messages come and get processed, that is, shares 847 * get freed. 848 */ 849 mutex_exit(&vswp->mac_lock); 850 drv_usecwait(vsw_hio_cleanup_delay); 851 mutex_enter(&vswp->mac_lock); 852 853 /* Check if the share still assigned to this port */ 854 if ((vsharep->vs_portp != portp) || 855 (vsharep->vs_state == VSW_SHARE_FREE)) { 856 break; 857 } 858 max_retries--; 859 } while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0)); 860 861 mutex_exit(&vswp->mac_lock); 862 D1(vswp, "%s:exit\n", __func__); 863 } 864 865 /* 866 * vsw_hio_rest_all -- Resets all ports that have shares allocated. 867 * It is called only in the panic code path, so the LDC channels 868 * are reset immediately. 869 */ 870 static void 871 vsw_hio_reset_all(vsw_t *vswp) 872 { 873 vsw_hio_t *hiop = &vswp->vhio; 874 vsw_share_t *vsharep; 875 int i; 876 877 D1(vswp, "%s:enter\n", __func__); 878 879 if (vswp->hio_capable != B_TRUE) 880 return; 881 882 for (i = 0; i < hiop->vh_num_shares; i++) { 883 vsharep = &hiop->vh_shares[i]; 884 if (vsharep->vs_state == VSW_SHARE_FREE) { 885 continue; 886 } 887 /* 888 * Reset the port with immediate flag enabled, 889 * to cause LDC reset immediately. 890 */ 891 vsw_hio_port_reset(vsharep->vs_portp, B_TRUE); 892 } 893 D1(vswp, "%s:exit\n", __func__); 894 } 895 896 /* 897 * vsw_hio_reboot_callb -- Called for reboot event. It tries to 898 * free all currently allocated shares. 899 */ 900 /* ARGSUSED */ 901 static boolean_t 902 vsw_hio_reboot_callb(void *arg, int code) 903 { 904 vsw_t *vswp = arg; 905 906 D1(vswp, "%s:enter\n", __func__); 907 vsw_hio_free_all_shares(vswp, B_TRUE); 908 D1(vswp, "%s:exit\n", __func__); 909 return (B_TRUE); 910 } 911 912 /* 913 * vsw_hio_panic_callb -- Called from panic event. It resets all 914 * the ports that have shares allocated. This is done to 915 * trigger the cleanup in the guest ahead of HV reset. 916 */ 917 /* ARGSUSED */ 918 static boolean_t 919 vsw_hio_panic_callb(void *arg, int code) 920 { 921 vsw_t *vswp = arg; 922 923 D1(vswp, "%s:enter\n", __func__); 924 vsw_hio_reset_all(vswp); 925 D1(vswp, "%s:exit\n", __func__); 926 return (B_TRUE); 927 } 928 929 /* 930 * Setup kstats for hio statistics. 931 */ 932 static kstat_t * 933 vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp) 934 { 935 kstat_t *ksp; 936 vsw_hio_kstats_t *hiokp; 937 vsw_hio_t *hiop; 938 char share_assigned_info[MAXNAMELEN]; 939 size_t size; 940 int i; 941 942 hiop = &vswp->vhio; 943 /* 944 * vsw_hio_stats_t structure is variable size structure 945 * having fields defined only for one share. So, we need 946 * allocate additional space for the rest of the shares. 947 */ 948 size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t); 949 ASSERT(hiop->vh_num_shares >= 1); 950 size += ((hiop->vh_num_shares - 1) * 2); 951 952 ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc", 953 KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL); 954 955 if (ksp == NULL) { 956 return (NULL); 957 } 958 hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) * 959 size, KM_SLEEP); 960 ksp->ks_data = hiokp; 961 962 hiop->vh_ksp = ksp; 963 hiop->vh_kstatsp = hiokp; 964 hiop->vh_kstat_size = size; 965 966 kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR); 967 kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares", 968 KSTAT_DATA_ULONG); 969 970 for (i = 0; i < hiop->vh_num_shares; i++) { 971 (void) sprintf(share_assigned_info, "%s%d", "hio_share_", i); 972 kstat_named_init(&(hiokp->share[i].assigned), 973 share_assigned_info, KSTAT_DATA_ULONG); 974 975 (void) sprintf(share_assigned_info, "%s%d%s", 976 "hio_share_", i, "_state"); 977 kstat_named_init(&(hiokp->share[i].state), 978 share_assigned_info, KSTAT_DATA_ULONG); 979 } 980 981 ksp->ks_update = vsw_hio_kstats_update; 982 ksp->ks_private = (void *)vswp; 983 kstat_install(ksp); 984 return (ksp); 985 } 986 987 /* 988 * Destroy hio kstats. 989 */ 990 static void 991 vsw_hio_destroy_kstats(vsw_t *vswp) 992 { 993 kstat_t *ksp; 994 vsw_hio_t *hiop; 995 996 ASSERT(vswp != NULL); 997 998 ksp = vswp->vhio.vh_ksp; 999 hiop = &vswp->vhio; 1000 if (ksp != NULL) { 1001 kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) * 1002 hiop->vh_kstat_size); 1003 kstat_delete(ksp); 1004 hiop->vh_kstatsp = NULL; 1005 hiop->vh_ksp = NULL; 1006 } 1007 } 1008 1009 /* 1010 * Update hio kstats. 1011 */ 1012 static int 1013 vsw_hio_kstats_update(kstat_t *ksp, int rw) 1014 { 1015 vsw_t *vswp; 1016 vsw_hio_t *hiop; 1017 vsw_hio_kstats_t *hiokp; 1018 int i; 1019 1020 vswp = (vsw_t *)ksp->ks_private; 1021 ASSERT(vswp != NULL); 1022 1023 hiop = &vswp->vhio; 1024 hiokp = hiop->vh_kstatsp; 1025 1026 if (rw == KSTAT_READ) { 1027 if (vswp->hio_capable) { 1028 (void) strcpy(hiokp->hio_capable.value.c, "Yes"); 1029 } else { 1030 /* not hio capable, just return */ 1031 (void) strcpy(hiokp->hio_capable.value.c, "No"); 1032 return (0); 1033 } 1034 1035 mutex_enter(&vswp->mac_lock); 1036 hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares; 1037 for (i = 0; i < hiop->vh_num_shares; i++) { 1038 hiokp->share[i].assigned.value.ul = 1039 hiop->vh_shares[i].vs_macaddr; 1040 hiokp->share[i].state.value.ul = 1041 hiop->vh_shares[i].vs_state; 1042 } 1043 mutex_exit(&vswp->mac_lock); 1044 } else { 1045 return (EACCES); 1046 } 1047 1048 return (0); 1049 } 1050