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 #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 317 /* DERR only for printing by default */ 318 DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX", 319 vsharep->vs_ldcid, vsharep->vs_cookie); 320 D1(vswp, "%s:exit\n", __func__); 321 } 322 323 324 /* 325 * vsw_hio_cleanup -- Cleanup the HybridIO. It unregisters the callbs 326 * and frees all shares. 327 */ 328 void 329 vsw_hio_cleanup(vsw_t *vswp) 330 { 331 D1(vswp, "%s:enter\n", __func__); 332 333 /* Unregister reboot and panic callbs. */ 334 if (vswp->hio_reboot_cb_id) { 335 (void) callb_delete(vswp->hio_reboot_cb_id); 336 vswp->hio_reboot_cb_id = 0; 337 } 338 if (vswp->hio_panic_cb_id) { 339 (void) callb_delete(vswp->hio_panic_cb_id); 340 vswp->hio_panic_cb_id = 0; 341 } 342 vsw_hio_free_all_shares(vswp, B_FALSE); 343 vsw_hio_destroy_kstats(vswp); 344 D1(vswp, "%s:exit\n", __func__); 345 } 346 347 /* 348 * vsw_hio_free_all_shares -- A routine to free all shares gracefully. 349 * The following are the steps followed to accomplish this: 350 * 351 * - First clear 'hio_capable' to avoid further share allocations. 352 * - If a share is in accepted(ACKD) state, that means the guest 353 * has HybridIO setup etc. If so, send a DEL_SHARE message and 354 * give some time(delay) for the guest to ACK. 355 * - If the Share is another state, give some time to transition to 356 * ACKD state, then try the above. 357 * - After max retries, reset the ports to brute force the shares 358 * to be freed. Give a little delay for the LDC reset code to 359 * free the Share. 360 */ 361 static void 362 vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot) 363 { 364 vsw_hio_t *hiop = &vswp->vhio; 365 vsw_port_list_t *plist = &vswp->plist; 366 vsw_share_t *vsharep; 367 int free_shares = 0; 368 int max_retries = vsw_hio_max_cleanup_retries; 369 int i; 370 371 D1(vswp, "%s:enter\n", __func__); 372 373 /* 374 * Acquire plist->lockrw to make the locking a bit easier 375 * and keep the ports in a stable state while we are cleaningup 376 * HybridIO. 377 */ 378 READ_ENTER(&plist->lockrw); 379 mutex_enter(&vswp->mac_lock); 380 /* 381 * first clear the hio_capable flag so that no more 382 * HybridIO operations are initiated. 383 */ 384 vswp->hio_capable = B_FALSE; 385 386 do { 387 free_shares = 0; 388 for (i = 0; i < hiop->vh_num_shares; i++) { 389 vsharep = &hiop->vh_shares[i]; 390 if (vsharep->vs_state == VSW_SHARE_FREE) { 391 free_shares++; 392 continue; 393 } 394 /* 395 * If the share is in DDS_ACKD state, then 396 * send DEL_SHARE message so that guest can 397 * release its Hybrid resource. 398 */ 399 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 400 int rv; 401 402 /* send DDS_DEL_SHARE */ 403 D1(vswp, "%s:sending DEL_SHARE msg for " 404 "share(%d)", __func__, vsharep->vs_index); 405 rv = vsw_hio_send_delshare_msg(vsharep); 406 if (rv != 0) { 407 /* 408 * No alternative, reset the port 409 * to force the release of Hybrid 410 * resources. 411 */ 412 vsw_hio_port_reset(vsharep->vs_portp, 413 B_FALSE); 414 } 415 } 416 if (max_retries == 1) { 417 /* 418 * Last retry, reset the port. 419 * If it is reboot case, issue an immediate 420 * reset. 421 */ 422 DWARN(vswp, "%s:All retries failed, " 423 " cause a reset to trigger cleanup for " 424 "share(%d)", __func__, vsharep->vs_index); 425 vsw_hio_port_reset(vsharep->vs_portp, reboot); 426 } 427 } 428 if (free_shares == hiop->vh_num_shares) { 429 /* Clean up is done */ 430 break; 431 } 432 /* 433 * Release the lock so that reply for DEL_SHARE 434 * messages come and get processed, that is, shares 435 * get freed. 436 * This delay is also needed for the port reset to 437 * release the Hybrid resource. 438 */ 439 mutex_exit(&vswp->mac_lock); 440 drv_usecwait(vsw_hio_cleanup_delay); 441 mutex_enter(&vswp->mac_lock); 442 max_retries--; 443 } while ((free_shares < hiop->vh_num_shares) && (max_retries > 0)); 444 445 /* By now, all shares should be freed */ 446 if (free_shares != hiop->vh_num_shares) { 447 if (reboot == B_FALSE) { 448 cmn_err(CE_NOTE, "vsw%d: All physical resources " 449 "could not be freed", vswp->instance); 450 } 451 } 452 453 kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares); 454 hiop->vh_shares = NULL; 455 hiop->vh_num_shares = 0; 456 mutex_exit(&vswp->mac_lock); 457 RW_EXIT(&plist->lockrw); 458 D1(vswp, "%s:exit\n", __func__); 459 } 460 461 /* 462 * vsw_hio_start_ports -- Start HybridIO for ports that have 463 * already established connection before HybridIO is intialized. 464 */ 465 void 466 vsw_hio_start_ports(vsw_t *vswp) 467 { 468 vsw_port_list_t *plist = &vswp->plist; 469 vsw_port_t *portp; 470 vsw_share_t *vsharep; 471 boolean_t reset; 472 473 if (vswp->hio_capable == B_FALSE) { 474 return; 475 } 476 READ_ENTER(&plist->lockrw); 477 for (portp = plist->head; portp != NULL; portp = portp->p_next) { 478 if ((portp->p_hio_enabled == B_FALSE) || 479 (portp->p_hio_capable == B_FALSE)) { 480 continue; 481 } 482 483 reset = B_FALSE; 484 mutex_enter(&vswp->mac_lock); 485 vsharep = vsw_hio_find_vshare_port(vswp, portp); 486 if (vsharep == NULL) { 487 reset = B_TRUE; 488 } 489 mutex_exit(&vswp->mac_lock); 490 491 if (reset == B_TRUE) { 492 /* Cause a rest to trigger HybridIO setup */ 493 vsw_hio_port_reset(portp, B_FALSE); 494 } 495 } 496 RW_EXIT(&plist->lockrw); 497 } 498 499 /* 500 * vsw_hio_start -- Start HybridIO for a guest(given LDC) 501 */ 502 void 503 vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp) 504 { 505 vsw_share_t *vsharep; 506 uint32_t req_id; 507 int rv; 508 509 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 510 mutex_enter(&vswp->mac_lock); 511 if (vswp->hio_capable == B_FALSE) { 512 mutex_exit(&vswp->mac_lock); 513 D2(vswp, "%s:not HIO capable", __func__); 514 return; 515 } 516 517 /* Verify if a share was already allocated */ 518 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 519 if (vsharep != NULL) { 520 mutex_exit(&vswp->mac_lock); 521 D2(vswp, "%s:Share already allocated to ldc=0x%lx", 522 __func__, ldcp->ldc_id); 523 return; 524 } 525 vsharep = vsw_hio_alloc_share(vswp, ldcp); 526 if (vsharep == NULL) { 527 mutex_exit(&vswp->mac_lock); 528 D2(vswp, "%s: no Share available for ldc=0x%lx", 529 __func__, ldcp->ldc_id); 530 return; 531 } 532 req_id = VSW_DDS_NEXT_REQID(vsharep); 533 rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie, 534 vsharep->vs_macaddr, req_id); 535 if (rv != 0) { 536 /* 537 * Failed to send a DDS message, so cleanup now. 538 */ 539 vsw_hio_free_share(vsharep); 540 mutex_exit(&vswp->mac_lock); 541 return; 542 } 543 vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD; 544 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 545 mutex_exit(&vswp->mac_lock); 546 547 /* DERR only to print by default */ 548 DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX", 549 ldcp->ldc_id, vsharep->vs_cookie); 550 551 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 552 } 553 554 /* 555 * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc). 556 */ 557 void 558 vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp) 559 { 560 vsw_share_t *vsharep; 561 562 D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id); 563 564 mutex_enter(&vswp->mac_lock); 565 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 566 if (vsharep == NULL) { 567 D1(vswp, "%s:no share found for ldc=0x%lx", 568 __func__, ldcp->ldc_id); 569 mutex_exit(&vswp->mac_lock); 570 return; 571 } 572 vsw_hio_free_share(vsharep); 573 mutex_exit(&vswp->mac_lock); 574 575 D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id); 576 } 577 578 /* 579 * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the guest. 580 */ 581 static int 582 vsw_hio_send_delshare_msg(vsw_share_t *vsharep) 583 { 584 vsw_t *vswp = vsharep->vs_vswp; 585 vsw_port_t *portp; 586 vsw_ldc_list_t *ldcl; 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 ldcl = &portp->p_ldclist; 603 READ_ENTER(&ldcl->lockrw); 604 ldcp = ldcl->head; 605 if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) { 606 RW_EXIT(&ldcl->lockrw); 607 mutex_enter(&vswp->mac_lock); 608 return (0); 609 } 610 req_id = VSW_DDS_NEXT_REQID(vsharep); 611 rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE, 612 cookie, macaddr, req_id); 613 614 RW_EXIT(&ldcl->lockrw); 615 mutex_enter(&vswp->mac_lock); 616 if (rv == 0) { 617 vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD; 618 vsharep->vs_state |= VSW_SHARE_DDS_SENT; 619 } 620 return (rv); 621 } 622 623 /* 624 * vsw_send_dds_msg -- Send a DDS message. 625 */ 626 static int 627 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t 628 cookie, uint64_t macaddr, uint32_t req_id) 629 { 630 vsw_t *vswp = ldcp->ldc_port->p_vswp; 631 vio_dds_msg_t vmsg; 632 dds_share_msg_t *smsg = &vmsg.msg.share_msg; 633 int rv; 634 635 D1(vswp, "%s:enter\n", __func__); 636 vmsg.tag.vio_msgtype = VIO_TYPE_CTRL; 637 vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO; 638 vmsg.tag.vio_subtype_env = VIO_DDS_INFO; 639 vmsg.tag.vio_sid = ldcp->local_session; 640 vmsg.dds_class = DDS_VNET_NIU; 641 vmsg.dds_subclass = dds_subclass; 642 vmsg.dds_req_id = req_id; 643 smsg->macaddr = macaddr; 644 smsg->cookie = cookie; 645 rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE); 646 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 647 return (rv); 648 } 649 650 /* 651 * vsw_process_dds_msg -- Process a DDS message received from a guest. 652 */ 653 void 654 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg) 655 { 656 vsw_share_t *vsharep; 657 vio_dds_msg_t *dmsg = msg; 658 659 D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id); 660 if (dmsg->dds_class != DDS_VNET_NIU) { 661 /* discard */ 662 return; 663 } 664 mutex_enter(&vswp->mac_lock); 665 /* 666 * We expect to receive DDS messages only from guests that 667 * have HybridIO started. 668 */ 669 vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id); 670 if (vsharep == NULL) { 671 mutex_exit(&vswp->mac_lock); 672 return; 673 } 674 675 switch (dmsg->dds_subclass) { 676 case DDS_VNET_ADD_SHARE: 677 /* A response for ADD_SHARE message. */ 678 D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__); 679 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 680 DWARN(vswp, "%s: invalid ADD_SHARE response message " 681 " share state=0x%X", __func__, vsharep->vs_state); 682 break; 683 } 684 685 if (dmsg->dds_req_id != vsharep->vs_req_id) { 686 DWARN(vswp, "%s: invalid req_id in ADD_SHARE response" 687 " message req_id=0x%X share's req_id=0x%X", 688 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 689 break; 690 } 691 692 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 693 DWARN(vswp, "%s: NACK received for ADD_SHARE" 694 " message ldcid=0x%lx", __func__, ldcp->ldc_id); 695 /* cleanup for NACK */ 696 vsw_hio_free_share(vsharep); 697 } else { 698 D2(vswp, "%s: ACK received for ADD_SHARE", __func__); 699 vsharep->vs_state &= ~VSW_SHARE_DDS_SENT; 700 vsharep->vs_state |= VSW_SHARE_DDS_ACKD; 701 } 702 break; 703 704 case DDS_VNET_DEL_SHARE: 705 /* A response for DEL_SHARE message */ 706 D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__); 707 if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) { 708 DWARN(vswp, "%s: invalid DEL_SHARE response message " 709 " share state=0x%X", __func__, vsharep->vs_state); 710 break; 711 } 712 713 if (dmsg->dds_req_id != vsharep->vs_req_id) { 714 DWARN(vswp, "%s: invalid req_id in DEL_SHARE response" 715 " message share req_id=0x%X share's req_id=0x%X", 716 __func__, dmsg->dds_req_id, vsharep->vs_req_id); 717 break; 718 } 719 if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) { 720 DWARN(vswp, "%s: NACK received for DEL_SHARE", 721 __func__); 722 } 723 724 /* There is nothing we can do, free share now */ 725 vsw_hio_free_share(vsharep); 726 break; 727 728 case DDS_VNET_REL_SHARE: 729 /* Guest has released Share voluntarily, so free it now */ 730 D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__); 731 /* send ACK */ 732 (void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE); 733 vsw_hio_free_share(vsharep); 734 break; 735 default: 736 DERR(vswp, "%s: Invalid DDS message type=0x%X", 737 __func__, dmsg->dds_subclass); 738 break; 739 } 740 mutex_exit(&vswp->mac_lock); 741 D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id); 742 } 743 744 /* 745 * vsw_send_dds_resp_msg -- Send a DDS response message. 746 */ 747 static int 748 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack) 749 { 750 vsw_t *vswp = ldcp->ldc_port->p_vswp; 751 int rv; 752 753 D1(vswp, "%s:enter\n", __func__); 754 if (ack == B_TRUE) { 755 dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK; 756 dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS; 757 } else { 758 dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK; 759 dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL; 760 } 761 rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE); 762 D1(vswp, "%s:exit rv=%d\n", __func__, rv); 763 return (rv); 764 } 765 766 /* 767 * vsw_hio_port_update -- update Hybrid mode change for a port. 768 */ 769 void 770 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled) 771 { 772 /* Verify if the mode really changed */ 773 if (portp->p_hio_enabled == hio_enabled) { 774 return; 775 } 776 777 if (hio_enabled == B_FALSE) { 778 /* Hybrid Mode is disabled, so stop HybridIO */ 779 vsw_hio_stop_port(portp); 780 portp->p_hio_enabled = B_FALSE; 781 782 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 783 } else { 784 portp->p_hio_enabled = B_TRUE; 785 vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0); 786 787 /* reset the port to initiate HybridIO setup */ 788 vsw_hio_port_reset(portp, B_FALSE); 789 } 790 } 791 792 /* 793 * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence 794 * followed is similar to vsw_hio_free_all_shares(). 795 * 796 */ 797 void 798 vsw_hio_stop_port(vsw_port_t *portp) 799 { 800 vsw_t *vswp = portp->p_vswp; 801 vsw_share_t *vsharep; 802 int max_retries = vsw_hio_max_cleanup_retries; 803 804 D1(vswp, "%s:enter\n", __func__); 805 mutex_enter(&vswp->mac_lock); 806 807 if (vswp->hio_capable == B_FALSE) { 808 mutex_exit(&vswp->mac_lock); 809 return; 810 } 811 812 vsharep = vsw_hio_find_vshare_port(vswp, portp); 813 if (vsharep == NULL) { 814 mutex_exit(&vswp->mac_lock); 815 return; 816 } 817 818 do { 819 if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) { 820 int rv; 821 822 /* send DDS_DEL_SHARE */ 823 D1(vswp, "%s:sending DEL_SHARE msg for " 824 "share(%d)", __func__, vsharep->vs_index); 825 rv = vsw_hio_send_delshare_msg(vsharep); 826 if (rv != 0) { 827 /* 828 * Cause a port reset to trigger 829 * cleanup. 830 */ 831 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 832 } 833 } 834 if (max_retries == 1) { 835 /* last retry */ 836 DWARN(vswp, "%s:All retries failed, " 837 " cause a reset to trigger cleanup for " 838 "share(%d)", __func__, vsharep->vs_index); 839 vsw_hio_port_reset(vsharep->vs_portp, B_FALSE); 840 } 841 842 /* Check if the share still assigned to this port */ 843 if ((vsharep->vs_portp != portp) || 844 (vsharep->vs_state == VSW_SHARE_FREE)) { 845 break; 846 } 847 848 /* 849 * Release the lock so that reply for DEL_SHARE 850 * messages come and get processed, that is, shares 851 * get freed. 852 */ 853 mutex_exit(&vswp->mac_lock); 854 drv_usecwait(vsw_hio_cleanup_delay); 855 mutex_enter(&vswp->mac_lock); 856 857 /* Check if the share still assigned to this port */ 858 if ((vsharep->vs_portp != portp) || 859 (vsharep->vs_state == VSW_SHARE_FREE)) { 860 break; 861 } 862 max_retries--; 863 } while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0)); 864 865 mutex_exit(&vswp->mac_lock); 866 D1(vswp, "%s:exit\n", __func__); 867 } 868 869 /* 870 * vsw_hio_rest_all -- Resets all ports that have shares allocated. 871 * It is called only in the panic code path, so the LDC channels 872 * are reset immediately. 873 */ 874 static void 875 vsw_hio_reset_all(vsw_t *vswp) 876 { 877 vsw_hio_t *hiop = &vswp->vhio; 878 vsw_share_t *vsharep; 879 int i; 880 881 D1(vswp, "%s:enter\n", __func__); 882 883 if (vswp->hio_capable != B_TRUE) 884 return; 885 886 for (i = 0; i < hiop->vh_num_shares; i++) { 887 vsharep = &hiop->vh_shares[i]; 888 if (vsharep->vs_state == VSW_SHARE_FREE) { 889 continue; 890 } 891 /* 892 * Reset the port with immediate flag enabled, 893 * to cause LDC reset immediately. 894 */ 895 vsw_hio_port_reset(vsharep->vs_portp, B_TRUE); 896 } 897 D1(vswp, "%s:exit\n", __func__); 898 } 899 900 /* 901 * vsw_hio_reboot_callb -- Called for reboot event. It tries to 902 * free all currently allocated shares. 903 */ 904 /* ARGSUSED */ 905 static boolean_t 906 vsw_hio_reboot_callb(void *arg, int code) 907 { 908 vsw_t *vswp = arg; 909 910 D1(vswp, "%s:enter\n", __func__); 911 vsw_hio_free_all_shares(vswp, B_TRUE); 912 D1(vswp, "%s:exit\n", __func__); 913 return (B_TRUE); 914 } 915 916 /* 917 * vsw_hio_panic_callb -- Called from panic event. It resets all 918 * the ports that have shares allocated. This is done to 919 * trigger the cleanup in the guest ahead of HV reset. 920 */ 921 /* ARGSUSED */ 922 static boolean_t 923 vsw_hio_panic_callb(void *arg, int code) 924 { 925 vsw_t *vswp = arg; 926 927 D1(vswp, "%s:enter\n", __func__); 928 vsw_hio_reset_all(vswp); 929 D1(vswp, "%s:exit\n", __func__); 930 return (B_TRUE); 931 } 932 933 /* 934 * Setup kstats for hio statistics. 935 */ 936 static kstat_t * 937 vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp) 938 { 939 kstat_t *ksp; 940 vsw_hio_kstats_t *hiokp; 941 vsw_hio_t *hiop; 942 char share_assigned_info[MAXNAMELEN]; 943 size_t size; 944 int i; 945 946 hiop = &vswp->vhio; 947 /* 948 * vsw_hio_stats_t structure is variable size structure 949 * having fields defined only for one share. So, we need 950 * allocate additional space for the rest of the shares. 951 */ 952 size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t); 953 ASSERT(hiop->vh_num_shares >= 1); 954 size += ((hiop->vh_num_shares - 1) * 2); 955 956 ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc", 957 KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL); 958 959 if (ksp == NULL) { 960 return (NULL); 961 } 962 hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) * 963 size, KM_SLEEP); 964 ksp->ks_data = hiokp; 965 966 hiop->vh_ksp = ksp; 967 hiop->vh_kstatsp = hiokp; 968 hiop->vh_kstat_size = size; 969 970 kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR); 971 kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares", 972 KSTAT_DATA_ULONG); 973 974 for (i = 0; i < hiop->vh_num_shares; i++) { 975 (void) sprintf(share_assigned_info, "%s%d", "hio_share_", i); 976 kstat_named_init(&(hiokp->share[i].assigned), 977 share_assigned_info, KSTAT_DATA_ULONG); 978 979 (void) sprintf(share_assigned_info, "%s%d%s", 980 "hio_share_", i, "_state"); 981 kstat_named_init(&(hiokp->share[i].state), 982 share_assigned_info, KSTAT_DATA_ULONG); 983 } 984 985 ksp->ks_update = vsw_hio_kstats_update; 986 ksp->ks_private = (void *)vswp; 987 kstat_install(ksp); 988 return (ksp); 989 } 990 991 /* 992 * Destroy hio kstats. 993 */ 994 static void 995 vsw_hio_destroy_kstats(vsw_t *vswp) 996 { 997 kstat_t *ksp; 998 vsw_hio_t *hiop; 999 1000 ASSERT(vswp != NULL); 1001 1002 ksp = vswp->vhio.vh_ksp; 1003 hiop = &vswp->vhio; 1004 if (ksp != NULL) { 1005 kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) * 1006 hiop->vh_kstat_size); 1007 kstat_delete(ksp); 1008 hiop->vh_kstatsp = NULL; 1009 hiop->vh_ksp = NULL; 1010 } 1011 } 1012 1013 /* 1014 * Update hio kstats. 1015 */ 1016 static int 1017 vsw_hio_kstats_update(kstat_t *ksp, int rw) 1018 { 1019 vsw_t *vswp; 1020 vsw_hio_t *hiop; 1021 vsw_hio_kstats_t *hiokp; 1022 int i; 1023 1024 vswp = (vsw_t *)ksp->ks_private; 1025 ASSERT(vswp != NULL); 1026 1027 hiop = &vswp->vhio; 1028 hiokp = hiop->vh_kstatsp; 1029 1030 if (rw == KSTAT_READ) { 1031 if (vswp->hio_capable) { 1032 (void) strcpy(hiokp->hio_capable.value.c, "Yes"); 1033 } else { 1034 /* not hio capable, just return */ 1035 (void) strcpy(hiokp->hio_capable.value.c, "No"); 1036 return (0); 1037 } 1038 1039 mutex_enter(&vswp->mac_lock); 1040 hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares; 1041 for (i = 0; i < hiop->vh_num_shares; i++) { 1042 hiokp->share[i].assigned.value.ul = 1043 hiop->vh_shares[i].vs_macaddr; 1044 hiokp->share[i].state.value.ul = 1045 hiop->vh_shares[i].vs_state; 1046 } 1047 mutex_exit(&vswp->mac_lock); 1048 } else { 1049 return (EACCES); 1050 } 1051 1052 return (0); 1053 } 1054