Print this page
11490 SRS ring polling disabled for VLANs
11491 Want DLS bypass for VLAN traffic
11492 add VLVF bypass to ixgbe core
2869 duplicate packets with vnics over aggrs
11489 DLS stat delete and aggr kstat can deadlock
Portions contributed by: Theo Schlossnagle <jesus@omniti.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/io/aggr/aggr_grp.c
          +++ new/usr/src/uts/common/io/aggr/aggr_grp.c
↓ open down ↓ 12 lines elided ↑ open up ↑
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23      - * Copyright (c) 2017, Joyent, Inc.
       23 + * Copyright 2018 Joyent, Inc.
  24   24   */
  25   25  
  26   26  /*
  27   27   * IEEE 802.3ad Link Aggregation -- Link Aggregation Groups.
  28   28   *
  29   29   * An instance of the structure aggr_grp_t is allocated for each
  30   30   * link aggregation group. When created, aggr_grp_t objects are
  31   31   * entered into the aggr_grp_hash hash table maintained by the modhash
  32   32   * module. The hash key is the linkid associated with the link
  33   33   * aggregation group.
↓ open down ↓ 83 lines elided ↑ open up ↑
 117  117  static boolean_t aggr_grp_sdu_check(aggr_grp_t *, aggr_port_t *);
 118  118  static boolean_t aggr_grp_margin_check(aggr_grp_t *, aggr_port_t *);
 119  119  
 120  120  static int aggr_add_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *);
 121  121  static void aggr_rem_pseudo_rx_group(aggr_port_t *, aggr_pseudo_rx_group_t *);
 122  122  static int aggr_pseudo_disable_intr(mac_intr_handle_t);
 123  123  static int aggr_pseudo_enable_intr(mac_intr_handle_t);
 124  124  static int aggr_pseudo_start_ring(mac_ring_driver_t, uint64_t);
 125  125  static int aggr_addmac(void *, const uint8_t *);
 126  126  static int aggr_remmac(void *, const uint8_t *);
      127 +static int aggr_addvlan(mac_group_driver_t, uint16_t);
      128 +static int aggr_remvlan(mac_group_driver_t, uint16_t);
 127  129  static mblk_t *aggr_rx_poll(void *, int);
 128  130  static void aggr_fill_ring(void *, mac_ring_type_t, const int,
 129  131      const int, mac_ring_info_t *, mac_ring_handle_t);
 130  132  static void aggr_fill_group(void *, mac_ring_type_t, const int,
 131  133      mac_group_info_t *, mac_group_handle_t);
 132  134  
 133  135  static kmem_cache_t     *aggr_grp_cache;
 134  136  static mod_hash_t       *aggr_grp_hash;
 135  137  static krwlock_t        aggr_grp_lock;
 136  138  static uint_t           aggr_grp_cnt;
↓ open down ↓ 180 lines elided ↑ open up ↑
 317  319          if (port->lp_ifspeed == 0 ||
 318  320              port->lp_link_state != LINK_STATE_UP ||
 319  321              port->lp_link_duplex != LINK_DUPLEX_FULL) {
 320  322                  /*
 321  323                   * Can't attach a MAC port with unknown link speed,
 322  324                   * down link, or not in full duplex mode.
 323  325                   */
 324  326                  return (B_FALSE);
 325  327          }
 326  328  
      329 +        mutex_enter(&grp->lg_stat_lock);
 327  330          if (grp->lg_ifspeed == 0) {
 328  331                  /*
 329  332                   * The group inherits the speed of the first link being
 330  333                   * attached.
 331  334                   */
 332  335                  grp->lg_ifspeed = port->lp_ifspeed;
 333  336                  link_state_changed = B_TRUE;
 334  337          } else if (grp->lg_ifspeed != port->lp_ifspeed) {
 335  338                  /*
 336  339                   * The link speed of the MAC port must be the same as
 337  340                   * the group link speed, as per 802.3ad. Since it is
 338  341                   * not, the attach is cancelled.
 339  342                   */
      343 +                mutex_exit(&grp->lg_stat_lock);
 340  344                  return (B_FALSE);
 341  345          }
      346 +        mutex_exit(&grp->lg_stat_lock);
 342  347  
 343  348          grp->lg_nattached_ports++;
 344  349  
 345  350          /*
 346  351           * Update the group link state.
 347  352           */
 348  353          if (grp->lg_link_state != LINK_STATE_UP) {
 349  354                  grp->lg_link_state = LINK_STATE_UP;
      355 +                mutex_enter(&grp->lg_stat_lock);
 350  356                  grp->lg_link_duplex = LINK_DUPLEX_FULL;
      357 +                mutex_exit(&grp->lg_stat_lock);
 351  358                  link_state_changed = B_TRUE;
 352  359          }
 353  360  
 354  361          /*
 355  362           * Update port's state.
 356  363           */
 357  364          port->lp_state = AGGR_PORT_STATE_ATTACHED;
 358  365  
 359  366          aggr_grp_multicst_port(port, B_TRUE);
 360  367  
↓ open down ↓ 37 lines elided ↑ open up ↑
 398  405          if (grp->lg_lacp_mode == AGGR_LACP_OFF)
 399  406                  aggr_send_port_disable(port);
 400  407          else
 401  408                  aggr_lacp_port_detached(port);
 402  409  
 403  410          port->lp_state = AGGR_PORT_STATE_STANDBY;
 404  411  
 405  412          grp->lg_nattached_ports--;
 406  413          if (grp->lg_nattached_ports == 0) {
 407  414                  /* the last attached MAC port of the group is being detached */
 408      -                grp->lg_ifspeed = 0;
 409  415                  grp->lg_link_state = LINK_STATE_DOWN;
      416 +                mutex_enter(&grp->lg_stat_lock);
      417 +                grp->lg_ifspeed = 0;
 410  418                  grp->lg_link_duplex = LINK_DUPLEX_UNKNOWN;
      419 +                mutex_exit(&grp->lg_stat_lock);
 411  420                  link_state_changed = B_TRUE;
 412  421          }
 413  422  
 414  423          return (link_state_changed);
 415  424  }
 416  425  
 417  426  /*
 418  427   * Update the MAC addresses of the constituent ports of the specified
 419  428   * group. This function is invoked:
 420  429   * - after creating a new aggregation group.
↓ open down ↓ 247 lines elided ↑ open up ↑
 668  677                  ring->arr_flags &= ~MAC_PSEUDO_RING_INUSE;
 669  678                  ring->arr_hw_rh = NULL;
 670  679                  ring->arr_port = NULL;
 671  680                  rx_grp->arg_ring_cnt--;
 672  681                  mac_hwring_teardown(hw_rh);
 673  682                  break;
 674  683          }
 675  684  }
 676  685  
 677  686  /*
 678      - * This function is called to create pseudo rings over the hardware rings of
 679      - * the underlying device. Note that there is a 1:1 mapping between the pseudo
 680      - * RX rings of the aggr and the hardware rings of the underlying port.
      687 + * Create pseudo rings over the HW rings of the port.
      688 + *
      689 + * o Create a pseudo ring in rx_grp per HW ring in the port's HW group.
      690 + *
      691 + * o Program existing unicast filters on the pseudo group into the HW group.
      692 + *
      693 + * o Program existing VLAN filters on the pseudo group into the HW group.
 681  694   */
 682  695  static int
 683  696  aggr_add_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp)
 684  697  {
 685  698          aggr_grp_t              *grp = port->lp_grp;
 686  699          mac_ring_handle_t       hw_rh[MAX_RINGS_PER_GROUP];
 687  700          aggr_unicst_addr_t      *addr, *a;
 688  701          mac_perim_handle_t      pmph;
      702 +        aggr_vlan_t             *avp;
 689  703          int                     hw_rh_cnt, i = 0, j;
 690  704          int                     err = 0;
 691  705  
 692  706          ASSERT(MAC_PERIM_HELD(grp->lg_mh));
 693  707          mac_perim_enter_by_mh(port->lp_mh, &pmph);
 694  708  
 695  709          /*
 696      -         * This function must be called after the aggr registers its mac
 697      -         * and its RX group has been initialized.
      710 +         * This function must be called after the aggr registers its MAC
      711 +         * and its Rx group has been initialized.
 698  712           */
 699  713          ASSERT(rx_grp->arg_gh != NULL);
 700  714  
 701  715          /*
 702      -         * Get the list the the underlying HW rings.
      716 +         * Get the list of the underlying HW rings.
 703  717           */
 704  718          hw_rh_cnt = mac_hwrings_get(port->lp_mch,
 705  719              &port->lp_hwgh, hw_rh, MAC_RING_TYPE_RX);
 706  720  
 707  721          if (port->lp_hwgh != NULL) {
 708  722                  /*
 709      -                 * Quiesce the HW ring and the mac srs on the ring. Note
      723 +                 * Quiesce the HW ring and the MAC SRS on the ring. Note
 710  724                   * that the HW ring will be restarted when the pseudo ring
 711  725                   * is started. At that time all the packets will be
 712      -                 * directly passed up to the pseudo RX ring and handled
 713      -                 * by mac srs created over the pseudo RX ring.
      726 +                 * directly passed up to the pseudo Rx ring and handled
      727 +                 * by MAC SRS created over the pseudo Rx ring.
 714  728                   */
 715  729                  mac_rx_client_quiesce(port->lp_mch);
 716  730                  mac_srs_perm_quiesce(port->lp_mch, B_TRUE);
 717  731          }
 718  732  
 719  733          /*
 720      -         * Add all the unicast addresses to the newly added port.
      734 +         * Add existing VLAN and unicast address filters to the port.
 721  735           */
      736 +        for (avp = list_head(&rx_grp->arg_vlans); avp != NULL;
      737 +            avp = list_next(&rx_grp->arg_vlans, avp)) {
      738 +                if ((err = aggr_port_addvlan(port, avp->av_vid)) != 0)
      739 +                        goto err;
      740 +        }
      741 +
 722  742          for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next) {
 723  743                  if ((err = aggr_port_addmac(port, addr->aua_addr)) != 0)
 724      -                        break;
      744 +                        goto err;
 725  745          }
 726  746  
 727      -        for (i = 0; err == 0 && i < hw_rh_cnt; i++)
      747 +        for (i = 0; i < hw_rh_cnt; i++) {
 728  748                  err = aggr_add_pseudo_rx_ring(port, rx_grp, hw_rh[i]);
      749 +                if (err != 0)
      750 +                        goto err;
      751 +        }
 729  752  
 730      -        if (err != 0) {
 731      -                for (j = 0; j < i; j++)
 732      -                        aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[j]);
      753 +        port->lp_rx_grp_added = B_TRUE;
      754 +        mac_perim_exit(pmph);
      755 +        return (0);
 733  756  
 734      -                for (a = rx_grp->arg_macaddr; a != addr; a = a->aua_next)
 735      -                        aggr_port_remmac(port, a->aua_addr);
      757 +err:
      758 +        ASSERT(err != 0);
 736  759  
 737      -                if (port->lp_hwgh != NULL) {
 738      -                        mac_srs_perm_quiesce(port->lp_mch, B_FALSE);
 739      -                        mac_rx_client_restart(port->lp_mch);
 740      -                        port->lp_hwgh = NULL;
      760 +        for (j = 0; j < i; j++)
      761 +                aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[j]);
      762 +
      763 +        for (a = rx_grp->arg_macaddr; a != addr; a = a->aua_next)
      764 +                aggr_port_remmac(port, a->aua_addr);
      765 +
      766 +        if (avp != NULL)
      767 +                avp = list_prev(&rx_grp->arg_vlans, avp);
      768 +
      769 +        for (; avp != NULL; avp = list_prev(&rx_grp->arg_vlans, avp)) {
      770 +                int err2;
      771 +
      772 +                if ((err2 = aggr_port_remvlan(port, avp->av_vid)) != 0) {
      773 +                        cmn_err(CE_WARN, "Failed to remove VLAN %u from port %s"
      774 +                            ": errno %d.", avp->av_vid,
      775 +                            mac_client_name(port->lp_mch), err2);
 741  776                  }
 742      -        } else {
 743      -                port->lp_rx_grp_added = B_TRUE;
 744  777          }
 745      -done:
      778 +
      779 +        if (port->lp_hwgh != NULL) {
      780 +                mac_srs_perm_quiesce(port->lp_mch, B_FALSE);
      781 +                mac_rx_client_restart(port->lp_mch);
      782 +                port->lp_hwgh = NULL;
      783 +        }
      784 +
 746  785          mac_perim_exit(pmph);
 747  786          return (err);
 748  787  }
 749  788  
 750  789  /*
 751      - * This function is called by aggr to remove pseudo RX rings over the
 752      - * HW rings of the underlying port.
      790 + * Destroy the pseudo rings mapping to this port and remove all VLAN
      791 + * and unicast filters from this port. Even if there are no underlying
      792 + * HW rings we must still remove the unicast filters to take the port
      793 + * out of promisc mode.
 753  794   */
 754  795  static void
 755  796  aggr_rem_pseudo_rx_group(aggr_port_t *port, aggr_pseudo_rx_group_t *rx_grp)
 756  797  {
 757  798          aggr_grp_t              *grp = port->lp_grp;
 758  799          mac_ring_handle_t       hw_rh[MAX_RINGS_PER_GROUP];
 759  800          aggr_unicst_addr_t      *addr;
 760  801          mac_group_handle_t      hwgh;
 761  802          mac_perim_handle_t      pmph;
 762  803          int                     hw_rh_cnt, i;
↓ open down ↓ 1 lines elided ↑ open up ↑
 764  805          ASSERT(MAC_PERIM_HELD(grp->lg_mh));
 765  806          mac_perim_enter_by_mh(port->lp_mh, &pmph);
 766  807  
 767  808          if (!port->lp_rx_grp_added)
 768  809                  goto done;
 769  810  
 770  811          ASSERT(rx_grp->arg_gh != NULL);
 771  812          hw_rh_cnt = mac_hwrings_get(port->lp_mch,
 772  813              &hwgh, hw_rh, MAC_RING_TYPE_RX);
 773  814  
 774      -        /*
 775      -         * If hw_rh_cnt is 0, it means that the underlying port does not
 776      -         * support RX rings. Directly return in this case.
 777      -         */
 778  815          for (i = 0; i < hw_rh_cnt; i++)
 779  816                  aggr_rem_pseudo_rx_ring(rx_grp, hw_rh[i]);
 780  817  
 781  818          for (addr = rx_grp->arg_macaddr; addr != NULL; addr = addr->aua_next)
 782  819                  aggr_port_remmac(port, addr->aua_addr);
 783  820  
      821 +        for (aggr_vlan_t *avp = list_head(&rx_grp->arg_vlans); avp != NULL;
      822 +            avp = list_next(&rx_grp->arg_vlans, avp)) {
      823 +                int err;
      824 +
      825 +                if ((err = aggr_port_remvlan(port, avp->av_vid)) != 0) {
      826 +                        cmn_err(CE_WARN, "Failed to remove VLAN %u from port %s"
      827 +                            ": errno %d.", avp->av_vid,
      828 +                            mac_client_name(port->lp_mch), err);
      829 +                }
      830 +        }
      831 +
 784  832          if (port->lp_hwgh != NULL) {
 785  833                  port->lp_hwgh = NULL;
 786  834  
 787  835                  /*
 788  836                   * First clear the permanent-quiesced flag of the RX srs then
 789  837                   * restart the HW ring and the mac srs on the ring. Note that
 790  838                   * the HW ring and associated SRS will soon been removed when
 791  839                   * the port is removed from the aggr.
 792  840                   */
 793  841                  mac_srs_perm_quiesce(port->lp_mch, B_FALSE);
↓ open down ↓ 506 lines elided ↑ open up ↑
1300 1348              aggr_lacp_rx_thread, grp, 0, &p0, TS_RUN, minclsyspri);
1301 1349          grp->lg_tx_notify_thread = thread_create(NULL, 0,
1302 1350              aggr_tx_notify_thread, grp, 0, &p0, TS_RUN, minclsyspri);
1303 1351          grp->lg_tx_blocked_rings = kmem_zalloc((sizeof (mac_ring_handle_t *) *
1304 1352              MAX_RINGS_PER_GROUP), KM_SLEEP);
1305 1353          grp->lg_tx_blocked_cnt = 0;
1306 1354          bzero(&grp->lg_rx_group, sizeof (aggr_pseudo_rx_group_t));
1307 1355          bzero(&grp->lg_tx_group, sizeof (aggr_pseudo_tx_group_t));
1308 1356          aggr_lacp_init_grp(grp);
1309 1357  
     1358 +        grp->lg_rx_group.arg_untagged = 0;
     1359 +        list_create(&(grp->lg_rx_group.arg_vlans), sizeof (aggr_vlan_t),
     1360 +            offsetof(aggr_vlan_t, av_link));
     1361 +
1310 1362          /* add MAC ports to group */
1311 1363          grp->lg_ports = NULL;
1312 1364          grp->lg_nports = 0;
1313 1365          grp->lg_nattached_ports = 0;
1314 1366          grp->lg_ntx_ports = 0;
1315 1367  
1316 1368          /*
1317 1369           * If key is not specified by the user, allocate the key.
1318 1370           */
1319 1371          if ((key == 0) && ((key = (uint32_t)id_alloc(key_ids)) == 0)) {
1320 1372                  err = ENOMEM;
1321 1373                  goto bail;
1322 1374          }
1323 1375          grp->lg_key = key;
1324 1376  
1325 1377          for (i = 0; i < nports; i++) {
1326      -                err = aggr_grp_add_port(grp, ports[i].lp_linkid, force, NULL);
     1378 +                err = aggr_grp_add_port(grp, ports[i].lp_linkid, force, &port);
1327 1379                  if (err != 0)
1328 1380                          goto bail;
1329 1381          }
1330 1382  
1331 1383          /*
1332 1384           * If no explicit MAC address was specified by the administrator,
1333 1385           * set it to the MAC address of the first port.
1334 1386           */
1335 1387          grp->lg_addr_fixed = mac_fixed;
1336 1388          if (grp->lg_addr_fixed) {
↓ open down ↓ 201 lines elided ↑ open up ↑
1538 1590           * the current counter from the underlying MAC then subtracting the
1539 1591           * value of the counter at the moment it was added to the
1540 1592           * aggregation.
1541 1593           */
1542 1594          for (i = 0; i < MAC_NSTAT; i++) {
1543 1595                  stat = i + MAC_STAT_MIN;
1544 1596                  if (!MAC_STAT_ISACOUNTER(stat))
1545 1597                          continue;
1546 1598                  val = aggr_port_stat(port, stat);
1547 1599                  val -= port->lp_stat[i];
     1600 +                mutex_enter(&grp->lg_stat_lock);
1548 1601                  grp->lg_stat[i] += val;
     1602 +                mutex_exit(&grp->lg_stat_lock);
1549 1603          }
1550 1604          for (i = 0; i < ETHER_NSTAT; i++) {
1551 1605                  stat = i + MACTYPE_STAT_MIN;
1552 1606                  if (!ETHER_STAT_ISACOUNTER(stat))
1553 1607                          continue;
1554 1608                  val = aggr_port_stat(port, stat);
1555 1609                  val -= port->lp_ether_stat[i];
     1610 +                mutex_enter(&grp->lg_stat_lock);
1556 1611                  grp->lg_ether_stat[i] += val;
     1612 +                mutex_exit(&grp->lg_stat_lock);
1557 1613          }
1558 1614  
1559 1615          grp->lg_nports--;
1560 1616          mac_perim_exit(mph);
1561 1617  
1562 1618          aggr_rem_pseudo_tx_group(port, &grp->lg_tx_group);
1563 1619          aggr_port_delete(port);
1564 1620  
1565 1621          /*
1566 1622           * If the group MAC address has changed, update the MAC address of
↓ open down ↓ 228 lines elided ↑ open up ↑
1795 1851          /*
1796 1852           * Wait for the port's lacp timer thread and its notification callback
1797 1853           * to exit before calling mac_unregister() since both needs to access
1798 1854           * the mac perimeter of the grp.
1799 1855           */
1800 1856          aggr_grp_port_wait(grp);
1801 1857  
1802 1858          VERIFY(mac_unregister(grp->lg_mh) == 0);
1803 1859          grp->lg_mh = NULL;
1804 1860  
     1861 +        list_destroy(&(grp->lg_rx_group.arg_vlans));
     1862 +
1805 1863          AGGR_GRP_REFRELE(grp);
1806 1864          return (0);
1807 1865  }
1808 1866  
1809 1867  void
1810 1868  aggr_grp_free(aggr_grp_t *grp)
1811 1869  {
1812 1870          ASSERT(grp->lg_refs == 0);
1813 1871          ASSERT(grp->lg_port_ref == 0);
1814 1872          if (grp->lg_key > AGGR_MAX_KEY) {
↓ open down ↓ 62 lines elided ↑ open up ↑
1877 1935  {
1878 1936          miocnak(q, mp, 0, ENOTSUP);
1879 1937  }
1880 1938  
1881 1939  static int
1882 1940  aggr_grp_stat(aggr_grp_t *grp, uint_t stat, uint64_t *val)
1883 1941  {
1884 1942          aggr_port_t     *port;
1885 1943          uint_t          stat_index;
1886 1944  
     1945 +        ASSERT(MUTEX_HELD(&grp->lg_stat_lock));
     1946 +
1887 1947          /* We only aggregate counter statistics. */
1888 1948          if (IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat) ||
1889 1949              IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat)) {
1890 1950                  return (ENOTSUP);
1891 1951          }
1892 1952  
1893 1953          /*
1894 1954           * Counter statistics for a group are computed by aggregating the
1895 1955           * counters of the members MACs while they were aggregated, plus
1896 1956           * the residual counter of the group itself, which is updated each
↓ open down ↓ 48 lines elided ↑ open up ↑
1945 2005  
1946 2006                  *val = mac_stat_get(port->lp_mh, stat);
1947 2007          }
1948 2008          return (0);
1949 2009  }
1950 2010  
1951 2011  static int
1952 2012  aggr_m_stat(void *arg, uint_t stat, uint64_t *val)
1953 2013  {
1954 2014          aggr_grp_t              *grp = arg;
1955      -        mac_perim_handle_t      mph;
1956 2015          int                     rval = 0;
1957 2016  
1958      -        mac_perim_enter_by_mh(grp->lg_mh, &mph);
     2017 +        mutex_enter(&grp->lg_stat_lock);
1959 2018  
1960 2019          switch (stat) {
1961 2020          case MAC_STAT_IFSPEED:
1962 2021                  *val = grp->lg_ifspeed;
1963 2022                  break;
1964 2023  
1965 2024          case ETHER_STAT_LINK_DUPLEX:
1966 2025                  *val = grp->lg_link_duplex;
1967 2026                  break;
1968 2027  
1969 2028          default:
1970 2029                  /*
1971 2030                   * For all other statistics, we return the aggregated stat
1972 2031                   * from the underlying ports.  aggr_grp_stat() will set
1973 2032                   * rval appropriately if the statistic isn't a counter.
1974 2033                   */
1975 2034                  rval = aggr_grp_stat(grp, stat, val);
1976 2035          }
1977 2036  
1978      -        mac_perim_exit(mph);
     2037 +        mutex_exit(&grp->lg_stat_lock);
1979 2038          return (rval);
1980 2039  }
1981 2040  
1982 2041  static int
1983 2042  aggr_m_start(void *arg)
1984 2043  {
1985 2044          aggr_grp_t *grp = arg;
1986 2045          aggr_port_t *port;
1987 2046          mac_perim_handle_t mph, pmph;
1988 2047  
↓ open down ↓ 211 lines elided ↑ open up ↑
2200 2259                  }
2201 2260                  return (B_TRUE);
2202 2261          }
2203 2262          default:
2204 2263                  return (B_FALSE);
2205 2264          }
2206 2265          return (B_TRUE);
2207 2266  }
2208 2267  
2209 2268  /*
2210      - * Callback funtion for MAC layer to register groups.
     2269 + * Callback function for MAC layer to register groups.
2211 2270   */
2212 2271  static void
2213 2272  aggr_fill_group(void *arg, mac_ring_type_t rtype, const int index,
2214 2273      mac_group_info_t *infop, mac_group_handle_t gh)
2215 2274  {
2216 2275          aggr_grp_t *grp = arg;
2217 2276          aggr_pseudo_rx_group_t *rx_group;
2218 2277          aggr_pseudo_tx_group_t *tx_group;
2219 2278  
2220 2279          ASSERT(index == 0);
↓ open down ↓ 1 lines elided ↑ open up ↑
2222 2281                  rx_group = &grp->lg_rx_group;
2223 2282                  rx_group->arg_gh = gh;
2224 2283                  rx_group->arg_grp = grp;
2225 2284  
2226 2285                  infop->mgi_driver = (mac_group_driver_t)rx_group;
2227 2286                  infop->mgi_start = NULL;
2228 2287                  infop->mgi_stop = NULL;
2229 2288                  infop->mgi_addmac = aggr_addmac;
2230 2289                  infop->mgi_remmac = aggr_remmac;
2231 2290                  infop->mgi_count = rx_group->arg_ring_cnt;
     2291 +
     2292 +                /*
     2293 +                 * Always set the HW VLAN callbacks. They are smart
     2294 +                 * enough to know when a port has HW VLAN filters to
     2295 +                 * program and when it doesn't.
     2296 +                 */
     2297 +                infop->mgi_addvlan = aggr_addvlan;
     2298 +                infop->mgi_remvlan = aggr_remvlan;
2232 2299          } else {
2233 2300                  tx_group = &grp->lg_tx_group;
2234 2301                  tx_group->atg_gh = gh;
2235 2302          }
2236 2303  }
2237 2304  
2238 2305  /*
2239 2306   * Callback funtion for MAC layer to register all rings.
2240 2307   */
2241 2308  static void
↓ open down ↓ 188 lines elided ↑ open up ↑
2430 2497          }
2431 2498  
2432 2499          for (port = grp->lg_ports; port != NULL; port = port->lp_next)
2433 2500                  aggr_port_remmac(port, mac_addr);
2434 2501  
2435 2502          *pprev = addr->aua_next;
2436 2503          kmem_free(addr, sizeof (aggr_unicst_addr_t));
2437 2504  
2438 2505          mac_perim_exit(mph);
2439 2506          return (err);
     2507 +}
     2508 +
     2509 +/*
     2510 + * Search for VID in the Rx group's list and return a pointer if
     2511 + * found. Otherwise return NULL.
     2512 + */
     2513 +static aggr_vlan_t *
     2514 +aggr_find_vlan(aggr_pseudo_rx_group_t *rx_group, uint16_t vid)
     2515 +{
     2516 +        ASSERT(MAC_PERIM_HELD(rx_group->arg_grp->lg_mh));
     2517 +        for (aggr_vlan_t *avp = list_head(&rx_group->arg_vlans); avp != NULL;
     2518 +            avp = list_next(&rx_group->arg_vlans, avp)) {
     2519 +                if (avp->av_vid == vid)
     2520 +                        return (avp);
     2521 +        }
     2522 +
     2523 +        return (NULL);
     2524 +}
     2525 +
     2526 +/*
     2527 + * Accept traffic on the specified VID.
     2528 + *
     2529 + * Persist VLAN state in the aggr so that ports added later will
     2530 + * receive the correct filters. In the future it would be nice to
     2531 + * allow aggr to iterate its clients instead of duplicating state.
     2532 + */
     2533 +static int
     2534 +aggr_addvlan(mac_group_driver_t gdriver, uint16_t vid)
     2535 +{
     2536 +        aggr_pseudo_rx_group_t  *rx_group = (aggr_pseudo_rx_group_t *)gdriver;
     2537 +        aggr_grp_t              *aggr = rx_group->arg_grp;
     2538 +        aggr_port_t             *port, *p;
     2539 +        mac_perim_handle_t      mph;
     2540 +        int                     err = 0;
     2541 +        aggr_vlan_t             *avp = NULL;
     2542 +
     2543 +        mac_perim_enter_by_mh(aggr->lg_mh, &mph);
     2544 +
     2545 +        if (vid == MAC_VLAN_UNTAGGED) {
     2546 +                /*
     2547 +                 * Aggr is both a MAC provider and MAC client. As a
     2548 +                 * MAC provider it is passed MAC_VLAN_UNTAGGED by its
     2549 +                 * client. As a client itself, it should pass
     2550 +                 * VLAN_ID_NONE to its ports.
     2551 +                 */
     2552 +                vid = VLAN_ID_NONE;
     2553 +                rx_group->arg_untagged++;
     2554 +                goto update_ports;
     2555 +        }
     2556 +
     2557 +        avp = aggr_find_vlan(rx_group, vid);
     2558 +
     2559 +        if (avp != NULL) {
     2560 +                avp->av_refs++;
     2561 +                mac_perim_exit(mph);
     2562 +                return (0);
     2563 +        }
     2564 +
     2565 +        avp = kmem_zalloc(sizeof (aggr_vlan_t), KM_SLEEP);
     2566 +        avp->av_vid = vid;
     2567 +        avp->av_refs = 1;
     2568 +
     2569 +update_ports:
     2570 +        for (port = aggr->lg_ports; port != NULL; port = port->lp_next)
     2571 +                if ((err = aggr_port_addvlan(port, vid)) != 0)
     2572 +                        break;
     2573 +
     2574 +        if (err != 0) {
     2575 +                /*
     2576 +                 * If any of these calls fail then we are in a
     2577 +                 * situation where the ports have different HW state.
     2578 +                 * There's no reasonable action the MAC client can
     2579 +                 * take in this scenario to rectify the situation.
     2580 +                 */
     2581 +                for (p = aggr->lg_ports; p != port; p = p->lp_next) {
     2582 +                        int err2;
     2583 +
     2584 +                        if ((err2 = aggr_port_remvlan(p, vid)) != 0) {
     2585 +                                cmn_err(CE_WARN, "Failed to remove VLAN %u"
     2586 +                                    " from port %s: errno %d.", vid,
     2587 +                                    mac_client_name(p->lp_mch), err2);
     2588 +                        }
     2589 +
     2590 +                }
     2591 +
     2592 +                if (vid == VLAN_ID_NONE)
     2593 +                        rx_group->arg_untagged--;
     2594 +
     2595 +                if (avp != NULL) {
     2596 +                        kmem_free(avp, sizeof (aggr_vlan_t));
     2597 +                        avp = NULL;
     2598 +                }
     2599 +        }
     2600 +
     2601 +        if (avp != NULL)
     2602 +                list_insert_tail(&rx_group->arg_vlans, avp);
     2603 +
     2604 +done:
     2605 +        mac_perim_exit(mph);
     2606 +        return (err);
     2607 +}
     2608 +
     2609 +/*
     2610 + * Stop accepting traffic on this VLAN if it's the last use of this VLAN.
     2611 + */
     2612 +static int
     2613 +aggr_remvlan(mac_group_driver_t gdriver, uint16_t vid)
     2614 +{
     2615 +        aggr_pseudo_rx_group_t  *rx_group = (aggr_pseudo_rx_group_t *)gdriver;
     2616 +        aggr_grp_t              *aggr = rx_group->arg_grp;
     2617 +        aggr_port_t             *port, *p;
     2618 +        mac_perim_handle_t      mph;
     2619 +        int                     err = 0;
     2620 +        aggr_vlan_t             *avp = NULL;
     2621 +
     2622 +        mac_perim_enter_by_mh(aggr->lg_mh, &mph);
     2623 +
     2624 +        /*
     2625 +         * See the comment in aggr_addvlan().
     2626 +         */
     2627 +        if (vid == MAC_VLAN_UNTAGGED) {
     2628 +                vid = VLAN_ID_NONE;
     2629 +                rx_group->arg_untagged--;
     2630 +
     2631 +                if (rx_group->arg_untagged > 0)
     2632 +                        goto done;
     2633 +
     2634 +                goto update_ports;
     2635 +        }
     2636 +
     2637 +        avp = aggr_find_vlan(rx_group, vid);
     2638 +
     2639 +        if (avp == NULL) {
     2640 +                err = ENOENT;
     2641 +                goto done;
     2642 +        }
     2643 +
     2644 +        avp->av_refs--;
     2645 +
     2646 +        if (avp->av_refs > 0)
     2647 +                goto done;
     2648 +
     2649 +update_ports:
     2650 +        for (port = aggr->lg_ports; port != NULL; port = port->lp_next)
     2651 +                if ((err = aggr_port_remvlan(port, vid)) != 0)
     2652 +                        break;
     2653 +
     2654 +        /*
     2655 +         * See the comment in aggr_addvlan() for justification of the
     2656 +         * use of VERIFY here.
     2657 +         */
     2658 +        if (err != 0) {
     2659 +                for (p = aggr->lg_ports; p != port; p = p->lp_next) {
     2660 +                        int err2;
     2661 +
     2662 +                        if ((err2 = aggr_port_addvlan(p, vid)) != 0) {
     2663 +                                cmn_err(CE_WARN, "Failed to add VLAN %u"
     2664 +                                    " to port %s: errno %d.", vid,
     2665 +                                    mac_client_name(p->lp_mch), err2);
     2666 +                        }
     2667 +                }
     2668 +
     2669 +                if (avp != NULL)
     2670 +                        avp->av_refs++;
     2671 +
     2672 +                if (vid == VLAN_ID_NONE)
     2673 +                        rx_group->arg_untagged++;
     2674 +
     2675 +                goto done;
     2676 +        }
     2677 +
     2678 +        if (err == 0 && avp != NULL) {
     2679 +                VERIFY3U(avp->av_refs, ==, 0);
     2680 +                list_remove(&rx_group->arg_vlans, avp);
     2681 +                kmem_free(avp, sizeof (aggr_vlan_t));
     2682 +        }
     2683 +
     2684 +done:
     2685 +        mac_perim_exit(mph);
     2686 +        return (err);
2440 2687  }
2441 2688  
2442 2689  /*
2443 2690   * Add or remove the multicast addresses that are defined for the group
2444 2691   * to or from the specified port.
2445 2692   *
2446 2693   * Note that aggr_grp_multicst_port(..., B_TRUE) is called when the port
2447 2694   * is started and attached, and aggr_grp_multicst_port(..., B_FALSE) is
2448 2695   * called when the port is either stopped or detached.
2449 2696   */
↓ open down ↓ 574 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX