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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * IEEE 802.3ad Link Aggregation - Link Aggregation MAC ports. 30 * 31 * Implements the functions needed to manage the MAC ports that are 32 * part of Link Aggregation groups. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/sysmacros.h> 37 #include <sys/conf.h> 38 #include <sys/cmn_err.h> 39 #include <sys/list.h> 40 #include <sys/ksynch.h> 41 #include <sys/kmem.h> 42 #include <sys/stream.h> 43 #include <sys/modctl.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/atomic.h> 47 #include <sys/stat.h> 48 #include <sys/sdt.h> 49 #include <sys/dlpi.h> 50 51 #include <sys/aggr.h> 52 #include <sys/aggr_impl.h> 53 54 static kmem_cache_t *aggr_port_cache; 55 static void aggr_port_notify_cb(void *, mac_notify_type_t); 56 57 /*ARGSUSED*/ 58 static int 59 aggr_port_constructor(void *buf, void *arg, int kmflag) 60 { 61 aggr_port_t *port = buf; 62 63 bzero(buf, sizeof (aggr_port_t)); 64 rw_init(&port->lp_lock, NULL, RW_DRIVER, NULL); 65 66 return (0); 67 } 68 69 /*ARGSUSED*/ 70 static void 71 aggr_port_destructor(void *buf, void *arg) 72 { 73 aggr_port_t *port = buf; 74 75 rw_destroy(&port->lp_lock); 76 } 77 78 void 79 aggr_port_init(void) 80 { 81 aggr_port_cache = kmem_cache_create("aggr_port_cache", 82 sizeof (aggr_port_t), 0, aggr_port_constructor, 83 aggr_port_destructor, NULL, NULL, NULL, 0); 84 } 85 86 void 87 aggr_port_fini(void) 88 { 89 /* 90 * This function is called only after all groups have been 91 * freed. This ensures that there are no remaining allocated 92 * ports when this function is invoked. 93 */ 94 kmem_cache_destroy(aggr_port_cache); 95 } 96 97 mac_resource_handle_t 98 aggr_port_resource_add(void *arg, mac_resource_t *mrp) 99 { 100 aggr_port_t *port = (aggr_port_t *)arg; 101 aggr_grp_t *grp = port->lp_grp; 102 103 return (mac_resource_add(grp->lg_mh, mrp)); 104 } 105 106 void 107 aggr_port_init_callbacks(aggr_port_t *port) 108 { 109 /* add the port's receive callback */ 110 port->lp_mnh = mac_notify_add(port->lp_mh, aggr_port_notify_cb, 111 (void *)port); 112 113 /* set port's resource_add callback */ 114 mac_resource_set(port->lp_mh, aggr_port_resource_add, (void *)port); 115 } 116 117 int 118 aggr_port_create(const char *name, aggr_port_t **pp) 119 { 120 int err; 121 mac_handle_t mh; 122 aggr_port_t *port; 123 uint_t i; 124 const mac_info_t *mip; 125 char driver[MAXNAMELEN]; 126 uint_t ddi_instance; 127 128 *pp = NULL; 129 130 if (ddi_parse(name, driver, &ddi_instance) != DDI_SUCCESS) 131 return (EINVAL); 132 133 if ((err = mac_open(name, ddi_instance, &mh)) != 0) 134 return (err); 135 136 mip = mac_info(mh); 137 if (mip->mi_media != DL_ETHER || mip->mi_nativemedia != DL_ETHER) { 138 mac_close(mh); 139 return (EINVAL); 140 } 141 142 if (!mac_active_set(mh)) { 143 mac_close(mh); 144 return (EBUSY); 145 } 146 147 port = kmem_cache_alloc(aggr_port_cache, KM_SLEEP); 148 149 port->lp_refs = 1; 150 port->lp_next = NULL; 151 port->lp_mh = mh; 152 port->lp_mip = mip; 153 (void) strlcpy(port->lp_devname, name, sizeof (port->lp_devname)); 154 port->lp_closing = 0; 155 156 /* get the port's original MAC address */ 157 mac_unicst_get(port->lp_mh, port->lp_addr); 158 159 /* set port's transmit information */ 160 port->lp_txinfo = mac_tx_get(port->lp_mh); 161 162 /* initialize state */ 163 port->lp_state = AGGR_PORT_STATE_STANDBY; 164 port->lp_link_state = LINK_STATE_UNKNOWN; 165 port->lp_ifspeed = 0; 166 port->lp_link_duplex = LINK_DUPLEX_UNKNOWN; 167 port->lp_started = B_FALSE; 168 port->lp_tx_enabled = B_FALSE; 169 port->lp_promisc_on = B_FALSE; 170 171 /* 172 * Save the current statistics of the port. They will be used 173 * later by aggr_m_stats() when aggregating the stastics of 174 * the consistituent ports. 175 */ 176 for (i = 0; i < MAC_NSTAT; i++) { 177 port->lp_stat[i] = 178 aggr_port_stat(port, i + MAC_STAT_MIN); 179 } 180 for (i = 0; i < ETHER_NSTAT; i++) { 181 port->lp_ether_stat[i] = 182 aggr_port_stat(port, i + MACTYPE_STAT_MIN); 183 } 184 185 /* LACP related state */ 186 port->lp_collector_enabled = B_FALSE; 187 188 *pp = port; 189 return (0); 190 } 191 192 void 193 aggr_port_delete(aggr_port_t *port) 194 { 195 mac_rx_remove_wait(port->lp_mh); 196 mac_resource_set(port->lp_mh, NULL, NULL); 197 mac_notify_remove(port->lp_mh, port->lp_mnh); 198 mac_active_clear(port->lp_mh); 199 200 /* 201 * Restore the port MAC address. Note it is called after the 202 * port's notification callback being removed. This prevent 203 * port's MAC_NOTE_UNICST notify callback function being called. 204 */ 205 (void) mac_unicst_set(port->lp_mh, port->lp_addr); 206 207 mac_close(port->lp_mh); 208 AGGR_PORT_REFRELE(port); 209 } 210 211 void 212 aggr_port_free(aggr_port_t *port) 213 { 214 ASSERT(port->lp_refs == 0); 215 if (port->lp_grp != NULL) 216 AGGR_GRP_REFRELE(port->lp_grp); 217 port->lp_grp = NULL; 218 kmem_cache_free(aggr_port_cache, port); 219 } 220 221 /* 222 * Invoked upon receiving a MAC_NOTE_LINK notification for 223 * one of the consistuent ports. 224 */ 225 boolean_t 226 aggr_port_notify_link(aggr_grp_t *grp, aggr_port_t *port, boolean_t dolock) 227 { 228 boolean_t do_attach = B_FALSE; 229 boolean_t do_detach = B_FALSE; 230 boolean_t link_state_changed = B_TRUE; 231 uint64_t ifspeed; 232 link_state_t link_state; 233 link_duplex_t link_duplex; 234 235 if (dolock) { 236 AGGR_LACP_LOCK(grp); 237 rw_enter(&grp->lg_lock, RW_WRITER); 238 } else { 239 ASSERT(AGGR_LACP_LOCK_HELD(grp)); 240 ASSERT(RW_WRITE_HELD(&grp->lg_lock)); 241 } 242 243 rw_enter(&port->lp_lock, RW_WRITER); 244 245 /* link state change? */ 246 link_state = mac_link_get(port->lp_mh); 247 if (port->lp_link_state != link_state) { 248 if (link_state == LINK_STATE_UP) 249 do_attach = (port->lp_link_state != LINK_STATE_UP); 250 else 251 do_detach = (port->lp_link_state == LINK_STATE_UP); 252 } 253 port->lp_link_state = link_state; 254 255 /* link duplex change? */ 256 link_duplex = aggr_port_stat(port, ETHER_STAT_LINK_DUPLEX); 257 if (port->lp_link_duplex != link_duplex) { 258 if (link_duplex == LINK_DUPLEX_FULL) 259 do_attach |= (port->lp_link_duplex != LINK_DUPLEX_FULL); 260 else 261 do_detach |= (port->lp_link_duplex == LINK_DUPLEX_FULL); 262 } 263 port->lp_link_duplex = link_duplex; 264 265 /* link speed changes? */ 266 ifspeed = aggr_port_stat(port, MAC_STAT_IFSPEED); 267 if (port->lp_ifspeed != ifspeed) { 268 if (port->lp_state == AGGR_PORT_STATE_ATTACHED) 269 do_detach |= (ifspeed != grp->lg_ifspeed); 270 else 271 do_attach |= (ifspeed == grp->lg_ifspeed); 272 } 273 port->lp_ifspeed = ifspeed; 274 275 if (do_attach) { 276 /* attempt to attach the port to the aggregation */ 277 link_state_changed = aggr_grp_attach_port(grp, port); 278 } else if (do_detach) { 279 /* detach the port from the aggregation */ 280 link_state_changed = aggr_grp_detach_port(grp, port); 281 } 282 283 rw_exit(&port->lp_lock); 284 285 if (dolock) { 286 rw_exit(&grp->lg_lock); 287 AGGR_LACP_UNLOCK(grp); 288 } 289 290 return (link_state_changed); 291 } 292 293 /* 294 * Invoked upon receiving a MAC_NOTE_UNICST for one of the constituent 295 * ports of a group. 296 */ 297 static void 298 aggr_port_notify_unicst(aggr_grp_t *grp, aggr_port_t *port, 299 boolean_t *mac_addr_changedp, boolean_t *link_state_changedp) 300 { 301 boolean_t mac_addr_changed = B_FALSE; 302 boolean_t link_state_changed = B_FALSE; 303 uint8_t mac_addr[ETHERADDRL]; 304 305 ASSERT(mac_addr_changedp != NULL); 306 ASSERT(link_state_changedp != NULL); 307 308 AGGR_LACP_LOCK(grp); 309 rw_enter(&grp->lg_lock, RW_WRITER); 310 311 rw_enter(&port->lp_lock, RW_WRITER); 312 313 /* 314 * If it is called when setting the MAC address to the 315 * aggregation group MAC address, do nothing. 316 */ 317 mac_unicst_get(port->lp_mh, mac_addr); 318 if (bcmp(mac_addr, grp->lg_addr, ETHERADDRL) == 0) { 319 rw_exit(&port->lp_lock); 320 goto done; 321 } 322 323 /* save the new port MAC address */ 324 bcopy(mac_addr, port->lp_addr, ETHERADDRL); 325 326 aggr_grp_port_mac_changed(grp, port, &mac_addr_changed, 327 &link_state_changed); 328 329 rw_exit(&port->lp_lock); 330 331 if (grp->lg_closing) 332 goto done; 333 334 /* 335 * If this port was used to determine the MAC address of 336 * the group, update the MAC address of the constituent 337 * ports. 338 */ 339 if (mac_addr_changed) { 340 link_state_changed = link_state_changed || 341 aggr_grp_update_ports_mac(grp); 342 } 343 344 done: 345 *mac_addr_changedp = mac_addr_changed; 346 *link_state_changedp = link_state_changed; 347 rw_exit(&grp->lg_lock); 348 AGGR_LACP_UNLOCK(grp); 349 } 350 351 /* 352 * Notification callback invoked by the MAC service module for 353 * a particular MAC port. 354 */ 355 static void 356 aggr_port_notify_cb(void *arg, mac_notify_type_t type) 357 { 358 aggr_port_t *port = arg; 359 aggr_grp_t *grp = port->lp_grp; 360 boolean_t mac_addr_changed, link_state_changed; 361 362 /* 363 * Do nothing if the aggregation or the port is in the deletion 364 * process. Note that this is necessary to avoid deadlock. 365 */ 366 if ((grp->lg_closing) || (port->lp_closing)) 367 return; 368 369 AGGR_PORT_REFHOLD(port); 370 371 switch (type) { 372 case MAC_NOTE_TX: 373 mac_tx_update(grp->lg_mh); 374 break; 375 case MAC_NOTE_LINK: 376 if (aggr_port_notify_link(grp, port, B_TRUE)) 377 mac_link_update(grp->lg_mh, grp->lg_link_state); 378 break; 379 case MAC_NOTE_UNICST: 380 aggr_port_notify_unicst(grp, port, &mac_addr_changed, 381 &link_state_changed); 382 if (mac_addr_changed) 383 mac_unicst_update(grp->lg_mh, grp->lg_addr); 384 if (link_state_changed) 385 mac_link_update(grp->lg_mh, grp->lg_link_state); 386 break; 387 case MAC_NOTE_PROMISC: 388 port->lp_txinfo = mac_tx_get(port->lp_mh); 389 break; 390 default: 391 break; 392 } 393 394 AGGR_PORT_REFRELE(port); 395 } 396 397 int 398 aggr_port_start(aggr_port_t *port) 399 { 400 int rc; 401 402 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 403 404 if (port->lp_started) 405 return (0); 406 407 if ((rc = mac_start(port->lp_mh)) != 0) 408 return (rc); 409 410 /* update the port state */ 411 port->lp_started = B_TRUE; 412 413 return (rc); 414 } 415 416 void 417 aggr_port_stop(aggr_port_t *port) 418 { 419 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 420 421 if (!port->lp_started) 422 return; 423 424 aggr_grp_multicst_port(port, B_FALSE); 425 426 mac_stop(port->lp_mh); 427 428 /* update the port state */ 429 port->lp_started = B_FALSE; 430 } 431 432 int 433 aggr_port_promisc(aggr_port_t *port, boolean_t on) 434 { 435 int rc; 436 437 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 438 439 if (on == port->lp_promisc_on) 440 /* already in desired promiscous mode */ 441 return (0); 442 443 rc = mac_promisc_set(port->lp_mh, on, MAC_DEVPROMISC); 444 445 if (rc == 0) 446 port->lp_promisc_on = on; 447 448 return (rc); 449 } 450 451 /* 452 * Set the MAC address of a port. 453 */ 454 int 455 aggr_port_unicst(aggr_port_t *port, uint8_t *macaddr) 456 { 457 int rc; 458 459 ASSERT(RW_WRITE_HELD(&port->lp_lock)); 460 461 rc = mac_unicst_set(port->lp_mh, macaddr); 462 463 return (rc); 464 } 465 466 /* 467 * Add or remove a multicast address to/from a port. 468 */ 469 int 470 aggr_port_multicst(void *arg, boolean_t add, const uint8_t *addrp) 471 { 472 aggr_port_t *port = arg; 473 474 return (add ? mac_multicst_add(port->lp_mh, addrp) : 475 mac_multicst_remove(port->lp_mh, addrp)); 476 } 477 478 uint64_t 479 aggr_port_stat(aggr_port_t *port, uint_t stat) 480 { 481 return (mac_stat_get(port->lp_mh, stat)); 482 } 483