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 Emulex. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Source file containing the implementation of the driver entry points 29 * and related helper functions 30 */ 31 32 #include <oce_impl.h> 33 #include <oce_ioctl.h> 34 35 /* array of properties supported by this driver */ 36 char *oce_priv_props[] = { 37 "_tx_ring_size", 38 "_tx_bcopy_limit", 39 "_rx_ring_size", 40 "_rx_bcopy_limit", 41 NULL 42 }; 43 44 extern int pow10[]; 45 46 /* ---[ static function declarations ]----------------------------------- */ 47 static int oce_set_priv_prop(struct oce_dev *dev, const char *name, 48 uint_t size, const void *val); 49 50 static int oce_get_priv_prop(struct oce_dev *dev, const char *name, 51 uint_t size, void *val); 52 53 /* ---[ GLD entry points ]----------------------------------------------- */ 54 int 55 oce_m_start(void *arg) 56 { 57 struct oce_dev *dev = arg; 58 int ret; 59 60 mutex_enter(&dev->dev_lock); 61 62 if (dev->state & STATE_MAC_STARTED) { 63 mutex_exit(&dev->dev_lock); 64 return (0); 65 } 66 67 if (dev->suspended) { 68 mutex_exit(&dev->dev_lock); 69 return (EIO); 70 } 71 72 if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) { 73 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 74 mutex_exit(&dev->dev_lock); 75 return (EIO); 76 } 77 78 if (oce_fm_check_acc_handle(dev, dev->csr_handle) != DDI_FM_OK) { 79 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 80 mutex_exit(&dev->dev_lock); 81 return (EIO); 82 } 83 84 if (oce_fm_check_acc_handle(dev, dev->cfg_handle) != DDI_FM_OK) { 85 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 86 mutex_exit(&dev->dev_lock); 87 return (EIO); 88 } 89 90 ret = oce_start(dev); 91 if (ret != DDI_SUCCESS) { 92 mutex_exit(&dev->dev_lock); 93 return (EIO); 94 } 95 96 dev->state |= STATE_MAC_STARTED; 97 mutex_exit(&dev->dev_lock); 98 99 100 return (DDI_SUCCESS); 101 } 102 103 int 104 oce_start(struct oce_dev *dev) 105 { 106 int qidx = 0; 107 struct link_status link = {0}; 108 109 /* get link status */ 110 (void) oce_get_link_status(dev, &link); 111 112 dev->link_status = (link.logical_link_status == NTWK_LOGICAL_LINK_UP) ? 113 LINK_STATE_UP : LINK_STATE_DOWN; 114 115 dev->link_speed = link.qos_link_speed ? link.qos_link_speed * 10 : 116 pow10[link.mac_speed]; 117 118 mac_link_update(dev->mac_handle, dev->link_status); 119 120 for (qidx = 0; qidx < dev->nwqs; qidx++) { 121 (void) oce_start_wq(dev->wq[qidx]); 122 } 123 for (qidx = 0; qidx < dev->nrqs; qidx++) { 124 (void) oce_start_rq(dev->rq[qidx]); 125 } 126 (void) oce_start_mq(dev->mq); 127 /* enable interrupts */ 128 oce_ei(dev); 129 /* arm the eqs */ 130 for (qidx = 0; qidx < dev->neqs; qidx++) { 131 oce_arm_eq(dev, dev->eq[qidx]->eq_id, 0, B_TRUE, B_FALSE); 132 } 133 /* TODO update state */ 134 return (DDI_SUCCESS); 135 } /* oce_start */ 136 137 138 void 139 oce_m_stop(void *arg) 140 { 141 struct oce_dev *dev = arg; 142 143 /* disable interrupts */ 144 145 mutex_enter(&dev->dev_lock); 146 if (dev->suspended) { 147 mutex_exit(&dev->dev_lock); 148 return; 149 } 150 dev->state |= STATE_MAC_STOPPING; 151 oce_stop(dev); 152 dev->state &= ~(STATE_MAC_STOPPING | STATE_MAC_STARTED); 153 mutex_exit(&dev->dev_lock); 154 } 155 /* called with Tx/Rx comp locks held */ 156 void 157 oce_stop(struct oce_dev *dev) 158 { 159 int qidx; 160 /* disable interrupts */ 161 oce_di(dev); 162 for (qidx = 0; qidx < dev->nwqs; qidx++) { 163 mutex_enter(&dev->wq[qidx]->tx_lock); 164 } 165 mutex_enter(&dev->mq->lock); 166 /* complete the pending Tx */ 167 for (qidx = 0; qidx < dev->nwqs; qidx++) 168 oce_clean_wq(dev->wq[qidx]); 169 /* Release all the locks */ 170 mutex_exit(&dev->mq->lock); 171 for (qidx = 0; qidx < dev->nwqs; qidx++) 172 mutex_exit(&dev->wq[qidx]->tx_lock); 173 if (dev->link_status == LINK_STATE_UP) { 174 dev->link_status = LINK_STATE_UNKNOWN; 175 mac_link_update(dev->mac_handle, dev->link_status); 176 } 177 178 } /* oce_stop */ 179 180 int 181 oce_m_multicast(void *arg, boolean_t add, const uint8_t *mca) 182 { 183 184 struct oce_dev *dev = (struct oce_dev *)arg; 185 struct ether_addr *mca_drv_list; 186 struct ether_addr mca_hw_list[OCE_MAX_MCA]; 187 uint16_t new_mcnt = dev->num_mca; 188 int ret; 189 int i; 190 191 /* check the address */ 192 if ((mca[0] & 0x1) == 0) { 193 return (EINVAL); 194 } 195 /* Allocate the local array for holding the addresses temporarily */ 196 bzero(&mca_hw_list, sizeof (&mca_hw_list)); 197 mca_drv_list = &dev->multi_cast[0]; 198 199 DEV_LOCK(dev); 200 if (add) { 201 /* check if we exceeded hw max supported */ 202 if (new_mcnt < OCE_MAX_MCA) { 203 /* copy entire dev mca to the mbx */ 204 bcopy((void*)mca_drv_list, 205 (void*)mca_hw_list, 206 (dev->num_mca * sizeof (struct ether_addr))); 207 /* Append the new one to local list */ 208 bcopy(mca, &mca_hw_list[dev->num_mca], 209 sizeof (struct ether_addr)); 210 } 211 new_mcnt++; 212 } else { 213 struct ether_addr *hwlistp = &mca_hw_list[0]; 214 for (i = 0; i < dev->num_mca; i++) { 215 /* copy only if it does not match */ 216 if (bcmp((mca_drv_list + i), mca, ETHERADDRL)) { 217 bcopy(mca_drv_list + i, hwlistp, 218 ETHERADDRL); 219 hwlistp++; 220 } else { 221 new_mcnt--; 222 } 223 } 224 } 225 226 if (dev->suspended) { 227 goto finish; 228 } 229 if (new_mcnt > OCE_MAX_MCA) { 230 ret = oce_set_multicast_table(dev, dev->if_id, &mca_hw_list[0], 231 OCE_MAX_MCA, B_TRUE); 232 } else { 233 ret = oce_set_multicast_table(dev, dev->if_id, 234 &mca_hw_list[0], new_mcnt, B_FALSE); 235 } 236 if (ret != 0) { 237 DEV_UNLOCK(dev); 238 return (EIO); 239 } 240 /* 241 * Copy the local structure to dev structure 242 */ 243 finish: 244 if (new_mcnt && new_mcnt <= OCE_MAX_MCA) { 245 bcopy(mca_hw_list, mca_drv_list, 246 new_mcnt * sizeof (struct ether_addr)); 247 248 dev->num_mca = (uint16_t)new_mcnt; 249 } 250 DEV_UNLOCK(dev); 251 return (0); 252 } /* oce_m_multicast */ 253 254 int 255 oce_m_unicast(void *arg, const uint8_t *uca) 256 { 257 struct oce_dev *dev = arg; 258 int ret; 259 260 DEV_LOCK(dev); 261 if (dev->suspended) { 262 bcopy(uca, dev->unicast_addr, ETHERADDRL); 263 DEV_UNLOCK(dev); 264 return (DDI_SUCCESS); 265 } 266 267 /* Delete previous one and add new one */ 268 ret = oce_del_mac(dev, dev->if_id, &dev->pmac_id); 269 if (ret != DDI_SUCCESS) { 270 DEV_UNLOCK(dev); 271 return (EIO); 272 } 273 bzero(dev->unicast_addr, ETHERADDRL); 274 275 /* Set the New MAC addr earlier is no longer valid */ 276 ret = oce_add_mac(dev, dev->if_id, uca, &dev->pmac_id); 277 if (ret != DDI_SUCCESS) { 278 DEV_UNLOCK(dev); 279 return (EIO); 280 } 281 bcopy(uca, dev->unicast_addr, ETHERADDRL); 282 DEV_UNLOCK(dev); 283 return (ret); 284 } /* oce_m_unicast */ 285 286 /* 287 * Hashing policy for load balancing over the set of TX rings 288 * available to the driver. 289 */ 290 mblk_t * 291 oce_m_send(void *arg, mblk_t *mp) 292 { 293 struct oce_dev *dev = arg; 294 mblk_t *nxt_pkt; 295 mblk_t *rmp = NULL; 296 struct oce_wq *wq; 297 298 DEV_LOCK(dev); 299 if (dev->suspended || !(dev->state & STATE_MAC_STARTED)) { 300 DEV_UNLOCK(dev); 301 freemsg(mp); 302 return (NULL); 303 } 304 DEV_UNLOCK(dev); 305 /* 306 * Hash to pick a wq 307 */ 308 wq = oce_get_wq(dev, mp); 309 310 while (mp != NULL) { 311 /* Save the Pointer since mp will be freed in case of copy */ 312 nxt_pkt = mp->b_next; 313 mp->b_next = NULL; 314 /* Hardcode wq since we have only one */ 315 rmp = oce_send_packet(wq, mp); 316 if (rmp != NULL) { 317 /* reschedule Tx */ 318 wq->resched = B_TRUE; 319 oce_arm_cq(dev, wq->cq->cq_id, 0, B_TRUE); 320 /* restore the chain */ 321 rmp->b_next = nxt_pkt; 322 break; 323 } 324 mp = nxt_pkt; 325 } 326 return (rmp); 327 } /* oce_send */ 328 329 boolean_t 330 oce_m_getcap(void *arg, mac_capab_t cap, void *data) 331 { 332 struct oce_dev *dev = arg; 333 boolean_t ret = B_TRUE; 334 switch (cap) { 335 336 case MAC_CAPAB_HCKSUM: { 337 uint32_t *csum_flags = u32ptr(data); 338 *csum_flags = HCKSUM_ENABLE | 339 HCKSUM_INET_FULL_V4 | 340 HCKSUM_IPHDRCKSUM; 341 break; 342 } 343 case MAC_CAPAB_LSO: { 344 mac_capab_lso_t *mcap_lso = (mac_capab_lso_t *)data; 345 if (dev->lso_capable) { 346 mcap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4; 347 mcap_lso->lso_basic_tcp_ipv4.lso_max = OCE_LSO_MAX_SIZE; 348 } else { 349 ret = B_FALSE; 350 } 351 break; 352 } 353 default: 354 ret = B_FALSE; 355 break; 356 } 357 return (ret); 358 } /* oce_m_getcap */ 359 360 int 361 oce_m_setprop(void *arg, const char *name, mac_prop_id_t id, 362 uint_t size, const void *val) 363 { 364 struct oce_dev *dev = arg; 365 int ret = 0; 366 367 DEV_LOCK(dev); 368 switch (id) { 369 case MAC_PROP_MTU: { 370 uint32_t mtu; 371 372 bcopy(val, &mtu, sizeof (uint32_t)); 373 374 if (dev->mtu == mtu) { 375 ret = 0; 376 break; 377 } 378 379 if (mtu != OCE_MIN_MTU && mtu != OCE_MAX_MTU) { 380 ret = EINVAL; 381 break; 382 } 383 384 ret = mac_maxsdu_update(dev->mac_handle, mtu); 385 if (0 == ret) { 386 dev->mtu = mtu; 387 break; 388 } 389 break; 390 } 391 392 case MAC_PROP_FLOWCTRL: { 393 link_flowctrl_t flowctrl; 394 uint32_t fc = 0; 395 396 bcopy(val, &flowctrl, sizeof (link_flowctrl_t)); 397 398 switch (flowctrl) { 399 case LINK_FLOWCTRL_NONE: 400 fc = 0; 401 break; 402 403 case LINK_FLOWCTRL_RX: 404 fc = OCE_FC_RX; 405 break; 406 407 case LINK_FLOWCTRL_TX: 408 fc = OCE_FC_TX; 409 break; 410 411 case LINK_FLOWCTRL_BI: 412 fc = OCE_FC_RX | OCE_FC_TX; 413 break; 414 default: 415 ret = EINVAL; 416 break; 417 } /* switch flowctrl */ 418 419 if (ret) 420 break; 421 422 if (fc == dev->flow_control) 423 break; 424 425 if (dev->suspended) { 426 dev->flow_control = fc; 427 break; 428 } 429 /* call to set flow control */ 430 ret = oce_set_flow_control(dev, fc); 431 /* store the new fc setting on success */ 432 if (ret == 0) { 433 dev->flow_control = fc; 434 } 435 break; 436 } 437 438 case MAC_PROP_PRIVATE: 439 ret = oce_set_priv_prop(dev, name, size, val); 440 break; 441 442 default: 443 ret = ENOTSUP; 444 break; 445 } /* switch id */ 446 447 DEV_UNLOCK(dev); 448 return (ret); 449 } /* oce_m_setprop */ 450 451 int 452 oce_m_getprop(void *arg, const char *name, mac_prop_id_t id, 453 uint_t size, void *val) 454 { 455 struct oce_dev *dev = arg; 456 uint32_t ret = 0; 457 458 switch (id) { 459 case MAC_PROP_ADV_10GFDX_CAP: 460 case MAC_PROP_EN_10GFDX_CAP: 461 *(uint8_t *)val = 0x01; 462 break; 463 464 case MAC_PROP_DUPLEX: { 465 uint32_t *mode = (uint32_t *)val; 466 467 ASSERT(size >= sizeof (link_duplex_t)); 468 if (dev->state & STATE_MAC_STARTED) 469 *mode = LINK_DUPLEX_FULL; 470 else 471 *mode = LINK_DUPLEX_UNKNOWN; 472 break; 473 } 474 475 case MAC_PROP_SPEED: { 476 uint64_t *speed = (uint64_t *)val; 477 struct link_status link = {0}; 478 479 ASSERT(size >= sizeof (uint64_t)); 480 *speed = 0; 481 482 if (dev->state & STATE_MAC_STARTED) { 483 if (dev->link_speed < 0) { 484 (void) oce_get_link_status(dev, &link); 485 dev->link_speed = link.qos_link_speed ? 486 link.qos_link_speed * 10 : 487 pow10[link.mac_speed]; 488 } 489 490 *speed = dev->link_speed * 1000000ull; 491 } 492 break; 493 } 494 495 case MAC_PROP_FLOWCTRL: { 496 link_flowctrl_t *fc = (link_flowctrl_t *)val; 497 498 ASSERT(size >= sizeof (link_flowctrl_t)); 499 if (dev->flow_control & OCE_FC_TX && 500 dev->flow_control & OCE_FC_RX) 501 *fc = LINK_FLOWCTRL_BI; 502 else if (dev->flow_control == OCE_FC_TX) 503 *fc = LINK_FLOWCTRL_TX; 504 else if (dev->flow_control == OCE_FC_RX) 505 *fc = LINK_FLOWCTRL_RX; 506 else if (dev->flow_control == 0) 507 *fc = LINK_FLOWCTRL_NONE; 508 else 509 ret = EINVAL; 510 break; 511 } 512 513 case MAC_PROP_PRIVATE: 514 ret = oce_get_priv_prop(dev, name, size, val); 515 break; 516 517 default: 518 ret = ENOTSUP; 519 break; 520 } /* switch id */ 521 return (ret); 522 } /* oce_m_getprop */ 523 524 void 525 oce_m_propinfo(void *arg, const char *name, mac_prop_id_t pr_num, 526 mac_prop_info_handle_t prh) 527 { 528 _NOTE(ARGUNUSED(arg)); 529 530 switch (pr_num) { 531 case MAC_PROP_AUTONEG: 532 case MAC_PROP_EN_AUTONEG: 533 case MAC_PROP_ADV_1000FDX_CAP: 534 case MAC_PROP_EN_1000FDX_CAP: 535 case MAC_PROP_ADV_1000HDX_CAP: 536 case MAC_PROP_EN_1000HDX_CAP: 537 case MAC_PROP_ADV_100FDX_CAP: 538 case MAC_PROP_EN_100FDX_CAP: 539 case MAC_PROP_ADV_100HDX_CAP: 540 case MAC_PROP_EN_100HDX_CAP: 541 case MAC_PROP_ADV_10FDX_CAP: 542 case MAC_PROP_EN_10FDX_CAP: 543 case MAC_PROP_ADV_10HDX_CAP: 544 case MAC_PROP_EN_10HDX_CAP: 545 case MAC_PROP_ADV_100T4_CAP: 546 case MAC_PROP_EN_100T4_CAP: 547 case MAC_PROP_ADV_10GFDX_CAP: 548 case MAC_PROP_EN_10GFDX_CAP: 549 case MAC_PROP_SPEED: 550 case MAC_PROP_DUPLEX: 551 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 552 break; 553 554 case MAC_PROP_MTU: 555 mac_prop_info_set_range_uint32(prh, OCE_MIN_MTU, OCE_MAX_MTU); 556 break; 557 558 case MAC_PROP_PRIVATE: { 559 char valstr[64]; 560 int value; 561 562 if (strcmp(name, "_tx_ring_size") == 0) { 563 value = OCE_DEFAULT_TX_RING_SIZE; 564 } else if (strcmp(name, "_rx_ring_size") == 0) { 565 value = OCE_DEFAULT_RX_RING_SIZE; 566 } else { 567 return; 568 } 569 570 (void) snprintf(valstr, sizeof (valstr), "%d", value); 571 mac_prop_info_set_default_str(prh, valstr); 572 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 573 break; 574 } 575 } 576 } /* oce_m_propinfo */ 577 578 /* 579 * function to handle dlpi streams message from GLDv3 mac layer 580 */ 581 void 582 oce_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 583 { 584 struct oce_dev *dev = arg; 585 struct iocblk *iocp; 586 int cmd; 587 uint32_t payload_length; 588 int ret; 589 590 iocp = (struct iocblk *)voidptr(mp->b_rptr); 591 iocp->ioc_error = 0; 592 cmd = iocp->ioc_cmd; 593 594 DEV_LOCK(dev); 595 if (dev->suspended) { 596 miocnak(wq, mp, 0, EINVAL); 597 DEV_UNLOCK(dev); 598 return; 599 } 600 DEV_UNLOCK(dev); 601 602 switch (cmd) { 603 604 case OCE_ISSUE_MBOX: { 605 ret = oce_issue_mbox(dev, wq, mp, &payload_length); 606 if (ret != 0) { 607 miocnak(wq, mp, payload_length, ret); 608 } else { 609 miocack(wq, mp, payload_length, 0); 610 } 611 break; 612 } 613 614 default: 615 miocnak(wq, mp, 0, ENOTSUP); 616 break; 617 } 618 } /* oce_m_ioctl */ 619 620 int 621 oce_m_promiscuous(void *arg, boolean_t enable) 622 { 623 struct oce_dev *dev = arg; 624 int ret = 0; 625 626 DEV_LOCK(dev); 627 628 if (dev->promisc == enable) { 629 DEV_UNLOCK(dev); 630 return (ret); 631 } 632 633 if (dev->suspended) { 634 /* remember the setting */ 635 dev->promisc = enable; 636 DEV_UNLOCK(dev); 637 return (ret); 638 } 639 640 ret = oce_set_promiscuous(dev, enable); 641 if (ret == DDI_SUCCESS) 642 dev->promisc = enable; 643 DEV_UNLOCK(dev); 644 return (ret); 645 } /* oce_m_promiscuous */ 646 647 /* 648 * function to set a private property. 649 * Called from the set_prop GLD entry point 650 * 651 * dev - sofware handle to the device 652 * name - string containing the property name 653 * size - length of the string in name 654 * val - pointer to a location where the value to set is stored 655 * 656 * return EINVAL => invalid value in val 0 => success 657 */ 658 static int 659 oce_set_priv_prop(struct oce_dev *dev, const char *name, 660 uint_t size, const void *val) 661 { 662 int ret = ENOTSUP; 663 long result; 664 665 _NOTE(ARGUNUSED(size)); 666 667 if (NULL == val) { 668 ret = EINVAL; 669 return (ret); 670 } 671 672 if (strcmp(name, "_tx_bcopy_limit") == 0) { 673 (void) ddi_strtol(val, (char **)NULL, 0, &result); 674 if (result <= OCE_WQ_BUF_SIZE) { 675 if (result != dev->tx_bcopy_limit) 676 dev->tx_bcopy_limit = (uint32_t)result; 677 ret = 0; 678 } else { 679 ret = EINVAL; 680 } 681 } 682 if (strcmp(name, "_rx_bcopy_limit") == 0) { 683 (void) ddi_strtol(val, (char **)NULL, 0, &result); 684 if (result <= OCE_RQ_BUF_SIZE) { 685 if (result != dev->rx_bcopy_limit) 686 dev->rx_bcopy_limit = (uint32_t)result; 687 ret = 0; 688 } else { 689 ret = EINVAL; 690 } 691 } 692 693 return (ret); 694 } /* oce_set_priv_prop */ 695 696 /* 697 * function to get the value of a private property. Called from get_prop 698 * 699 * dev - software handle to the device 700 * name - string containing the property name 701 * size - length of the string contained name 702 * val - [OUT] pointer to the location where the result is returned 703 * 704 * return EINVAL => invalid request 0 => success 705 */ 706 static int 707 oce_get_priv_prop(struct oce_dev *dev, const char *name, 708 uint_t size, void *val) 709 { 710 int value; 711 712 if (strcmp(name, "_tx_ring_size") == 0) { 713 value = dev->tx_ring_size; 714 } else if (strcmp(name, "_tx_bcopy_limit") == 0) { 715 value = dev->tx_bcopy_limit; 716 } else if (strcmp(name, "_rx_ring_size") == 0) { 717 value = dev->rx_ring_size; 718 } else if (strcmp(name, "_rx_bcopy_limit") == 0) { 719 value = dev->rx_bcopy_limit; 720 } else { 721 return (ENOTSUP); 722 } 723 724 (void) snprintf(val, size, "%d", value); 725 return (0); 726 } /* oce_get_priv_prop */ 727