1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/errno.h>
29 #include <sys/debug.h>
30 #include <sys/time.h>
31 #include <sys/sysmacros.h>
32 #include <sys/systm.h>
33 #include <sys/user.h>
34 #include <sys/stropts.h>
35 #include <sys/stream.h>
36 #include <sys/strlog.h>
37 #include <sys/strsubr.h>
38 #include <sys/cmn_err.h>
39 #include <sys/cpu.h>
40 #include <sys/kmem.h>
41 #include <sys/conf.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <sys/ksynch.h>
45 #include <sys/stat.h>
46 #include <sys/kstat.h>
47 #include <sys/vtrace.h>
48 #include <sys/strsun.h>
49 #include <sys/dlpi.h>
50 #include <sys/ethernet.h>
51 #include <net/if.h>
52 #include <netinet/arp.h>
53 #include <inet/arp.h>
54 #include <sys/varargs.h>
55 #include <sys/machsystm.h>
56 #include <sys/modctl.h>
57 #include <sys/modhash.h>
58 #include <sys/mac_client.h>
59 #include <sys/mac_provider.h>
60 #include <sys/mac_client_priv.h>
61 #include <sys/mac_ether.h>
62 #include <sys/taskq.h>
63 #include <sys/note.h>
64 #include <sys/mach_descrip.h>
65 #include <sys/mac.h>
66 #include <sys/mac_flow.h>
67 #include <sys/mdeg.h>
68 #include <sys/vsw.h>
69 #include <sys/vlan.h>
70
71 /* MAC Ring table functions. */
72 static void vsw_port_rx_cb(void *, mac_resource_handle_t, mblk_t *,
73 boolean_t);
74 static void vsw_if_rx_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t);
75
76 /* MAC layer routines */
77 static int vsw_set_port_hw_addr(vsw_port_t *port);
78 static int vsw_set_if_hw_addr(vsw_t *vswp);
79 static void vsw_unset_hw_addr(vsw_t *, vsw_port_t *, int);
80 static int vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type);
81 static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type);
82 static void vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type);
83 static void vsw_mac_multicast_remove_all(vsw_t *vswp,
84 vsw_port_t *portp, int type);
85 static void vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch,
86 uint8_t *macaddr, uint16_t flags, vsw_vlanid_t *vids, int nvids);
87 static void vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids,
88 int nvids);
89 static void vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu);
90 static void vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type,
91 uint64_t maxbw);
92 static int vsw_notify_add(vsw_t *vswp);
93 static int vsw_notify_rem(vsw_t *vswp);
94 static void vsw_notify_cb(void *arg, mac_notify_type_t type);
95 static void vsw_notify_link(vsw_t *vswp);
96
97 /* Support functions */
98 int vsw_set_hw(vsw_t *, vsw_port_t *, int);
99 void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
100 void vsw_reconfig_hw(vsw_t *);
101 int vsw_mac_open(vsw_t *vswp);
102 void vsw_mac_close(vsw_t *vswp);
103 int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
104 int type);
105 void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
106 mcst_addr_t *mcst_p, int type);
107 int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
108 void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
109 void vsw_mac_cleanup_ports(vsw_t *vswp);
110 void vsw_unset_addrs(vsw_t *vswp);
111 void vsw_set_addrs(vsw_t *vswp);
112 mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
113 void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
114 void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
115 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
116 void vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
117 vsw_vlanid_t *new_vids, int new_nvids);
118 void vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
119 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
120 void vsw_update_bandwidth(vsw_t *vswp, vsw_port_t *port, int type,
121 uint64_t maxbw);
122
123 /*
124 * Functions imported from other files.
125 */
126 extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
127 extern void vsw_hio_stop_port(vsw_port_t *portp);
128 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
129 extern uint32_t vsw_publish_macaddr_count;
130 extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
131 mblk_t **npt);
132 extern void vsw_physlink_state_update(vsw_t *vswp);
133 static char mac_mtu_propname[] = "mtu";
134
135 /*
136 * Tunables used in this file.
137 */
138 extern int vsw_mac_open_retries;
139
140 #define WRITE_MACCL_ENTER(vswp, port, type) \
141 (type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_WRITER) :\
142 rw_enter(&port->maccl_rwlock, RW_WRITER)
143
144 #define READ_MACCL_ENTER(vswp, port, type) \
145 (type == VSW_LOCALDEV) ? rw_enter(&vswp->maccl_rwlock, RW_READER) :\
146 rw_enter(&port->maccl_rwlock, RW_READER)
147
148 #define RW_MACCL_EXIT(vswp, port, type) \
149 (type == VSW_LOCALDEV) ? rw_exit(&vswp->maccl_rwlock) : \
150 rw_exit(&port->maccl_rwlock)
151
152
153 /*
154 * Locking strategy in this file is explained as follows:
155 * - A global lock(vswp->mac_lock) is used to protect the
156 * MAC calls that deal with entire device. That is, the
157 * operations that deal with mac_handle which include
158 * mac_open()/close() and mac_client_open().
159 *
160 * - A per port/interface RW lock(maccl_rwlock) is used protect
161 * the operations that deal with the MAC client.
162 *
163 * When both mac_lock and maccl_rwlock need to be held, the
164 * mac_lock need be acquired first and then maccl_rwlock. That is,
165 * mac_lock---->maccl_rwlock
166 *
167 * The 'mca_lock' that protects the mcast list is also acquired
168 * within the context of maccl_rwlock. The hierarchy for this
169 * one is as below:
170 * maccl_rwlock---->mca_lock
171 */
172
173
174 /*
175 * Program unicast and multicast addresses of vsw interface and the ports
176 * into the network device.
177 */
178 void
vsw_set_addrs(vsw_t * vswp)179 vsw_set_addrs(vsw_t *vswp)
180 {
181 vsw_port_list_t *plist = &vswp->plist;
182 vsw_port_t *port;
183 int rv;
184
185 READ_ENTER(&vswp->if_lockrw);
186
187 if (vswp->if_state & VSW_IF_UP) {
188
189 /* Open a mac client and program addresses */
190 rv = vsw_mac_client_init(vswp, NULL, VSW_LOCALDEV);
191 if (rv != 0) {
192 cmn_err(CE_NOTE,
193 "!vsw%d: failed to program interface "
194 "unicast address\n", vswp->instance);
195 }
196
197 /*
198 * Notify the MAC layer of the changed address.
199 */
200 if (rv == 0) {
201 mac_unicst_update(vswp->if_mh,
202 (uint8_t *)&vswp->if_addr);
203 }
204
205 }
206
207 RW_EXIT(&vswp->if_lockrw);
208
209 WRITE_ENTER(&plist->lockrw);
210
211 /* program unicast address of ports in the network device */
212 for (port = plist->head; port != NULL; port = port->p_next) {
213 if (port->addr_set) /* addr already set */
214 continue;
215
216 /* Open a mac client and program addresses */
217 rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT);
218 if (rv != 0) {
219 cmn_err(CE_NOTE,
220 "!vsw%d: failed to program port(%d) "
221 "unicast address\n", vswp->instance,
222 port->p_instance);
223 }
224 }
225 /* announce macaddr of vnets to the physical switch */
226 if (vsw_publish_macaddr_count != 0) { /* enabled */
227 for (port = plist->head; port != NULL; port = port->p_next) {
228 vsw_publish_macaddr(vswp, port);
229 }
230 }
231
232 RW_EXIT(&plist->lockrw);
233 }
234
235 /*
236 * Remove unicast, multicast addresses and close mac clients
237 * for the vsw interface and all ports.
238 */
239 void
vsw_unset_addrs(vsw_t * vswp)240 vsw_unset_addrs(vsw_t *vswp)
241 {
242 READ_ENTER(&vswp->if_lockrw);
243 if (vswp->if_state & VSW_IF_UP) {
244
245 /* Cleanup and close the mac client for the interface */
246 vsw_mac_client_cleanup(vswp, NULL, VSW_LOCALDEV);
247 }
248 RW_EXIT(&vswp->if_lockrw);
249
250 /* Cleanup and close the mac clients for all ports */
251 vsw_mac_cleanup_ports(vswp);
252 }
253
254 /*
255 * Open the underlying network device for access in layer2 mode.
256 * Returns:
257 * 0 on success
258 * EAGAIN if mac_open() fails due to the device being not available yet.
259 * EIO on any other failures.
260 */
261 int
vsw_mac_open(vsw_t * vswp)262 vsw_mac_open(vsw_t *vswp)
263 {
264 int rv;
265
266 ASSERT(MUTEX_HELD(&vswp->mac_lock));
267
268 if (vswp->mh != NULL) {
269 /* already open */
270 return (0);
271 }
272
273 if (vswp->mac_open_retries++ >= vsw_mac_open_retries) {
274 /* exceeded max retries */
275 return (EIO);
276 }
277
278 if ((rv = mac_open_by_linkname(vswp->physname, &vswp->mh)) != 0) {
279 /*
280 * If mac_open() failed and the error indicates that either
281 * the dlmgmtd door or the device is not available yet, we
282 * return EAGAIN to indicate that mac_open() needs to be
283 * retried. For example, this may happen during boot up, if
284 * the required link aggregation groups(devices) have not
285 * been created yet.
286 */
287 if (rv == ENOENT || rv == EBADF) {
288 return (EAGAIN);
289 } else {
290 cmn_err(CE_WARN, "!vsw%d: mac_open %s failed rv:%x\n",
291 vswp->instance, vswp->physname, rv);
292 return (EIO);
293 }
294 }
295 vswp->mac_open_retries = 0;
296
297 vsw_mac_set_mtu(vswp, vswp->mtu);
298
299 rv = vsw_notify_add(vswp);
300 if (rv != 0) {
301 cmn_err(CE_CONT, "!vsw%d: mac_notify_add %s failed rv:%x\n",
302 vswp->instance, vswp->physname, rv);
303 }
304
305 return (0);
306 }
307
308 /*
309 * Close the underlying physical device.
310 */
311 void
vsw_mac_close(vsw_t * vswp)312 vsw_mac_close(vsw_t *vswp)
313 {
314 ASSERT(MUTEX_HELD(&vswp->mac_lock));
315
316 if (vswp->mh != NULL) {
317 if (vswp->mnh != 0) {
318 (void) vsw_notify_rem(vswp);
319 vswp->mnh = 0;
320 }
321 if (vswp->mtu != vswp->mtu_physdev_orig) {
322 vsw_mac_set_mtu(vswp, vswp->mtu_physdev_orig);
323 }
324 mac_close(vswp->mh);
325 vswp->mh = NULL;
326 }
327 }
328
329 /*
330 * Add multicast addr.
331 */
332 int
vsw_mac_multicast_add(vsw_t * vswp,vsw_port_t * port,mcst_addr_t * mcst_p,int type)333 vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
334 int type)
335 {
336 int ret = 0;
337 mac_client_handle_t mch;
338
339 WRITE_MACCL_ENTER(vswp, port, type);
340
341 mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
342
343 if (mch != NULL) {
344 ret = mac_multicast_add(mch, mcst_p->mca.ether_addr_octet);
345 if (ret != 0) {
346 cmn_err(CE_WARN, "!vsw%d: unable to "
347 "program multicast address(%s) err=%d",
348 vswp->instance,
349 ether_sprintf((void *)&mcst_p->mca), ret);
350 RW_MACCL_EXIT(vswp, port, type);
351 return (ret);
352 }
353 mcst_p->mac_added = B_TRUE;
354 }
355
356 RW_MACCL_EXIT(vswp, port, type);
357 return (ret);
358 }
359
360 /*
361 * Remove multicast addr.
362 */
363 void
vsw_mac_multicast_remove(vsw_t * vswp,vsw_port_t * port,mcst_addr_t * mcst_p,int type)364 vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
365 int type)
366 {
367 mac_client_handle_t mch;
368
369 WRITE_MACCL_ENTER(vswp, port, type);
370 mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
371
372 if (mch != NULL && mcst_p->mac_added) {
373 mac_multicast_remove(mch, mcst_p->mca.ether_addr_octet);
374 mcst_p->mac_added = B_FALSE;
375 }
376 RW_MACCL_EXIT(vswp, port, type);
377 }
378
379
380 /*
381 * Add all multicast addresses of the port.
382 */
383 static void
vsw_mac_multicast_add_all(vsw_t * vswp,vsw_port_t * portp,int type)384 vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type)
385 {
386 mcst_addr_t *mcap;
387 mac_client_handle_t mch;
388 kmutex_t *mca_lockp;
389 int rv;
390
391 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
392 if (type == VSW_LOCALDEV) {
393 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
394 mch = vswp->mch;
395 mcap = vswp->mcap;
396 mca_lockp = &vswp->mca_lock;
397 } else {
398 ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
399 mch = portp->p_mch;
400 mcap = portp->mcap;
401 mca_lockp = &portp->mca_lock;
402 }
403
404 if (mch == NULL)
405 return;
406
407 mutex_enter(mca_lockp);
408 for (mcap = mcap; mcap != NULL; mcap = mcap->nextp) {
409 if (mcap->mac_added)
410 continue;
411 rv = mac_multicast_add(mch, (uchar_t *)&mcap->mca);
412 if (rv == 0) {
413 mcap->mac_added = B_TRUE;
414 } else {
415 cmn_err(CE_WARN, "!vsw%d: unable to program "
416 "multicast address(%s) err=%d", vswp->instance,
417 ether_sprintf((void *)&mcap->mca), rv);
418 }
419 }
420 mutex_exit(mca_lockp);
421 }
422
423 /*
424 * Remove all multicast addresses of the port.
425 */
426 static void
vsw_mac_multicast_remove_all(vsw_t * vswp,vsw_port_t * portp,int type)427 vsw_mac_multicast_remove_all(vsw_t *vswp, vsw_port_t *portp, int type)
428 {
429 mac_client_handle_t mch;
430 mcst_addr_t *mcap;
431 kmutex_t *mca_lockp;
432
433 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
434 if (type == VSW_LOCALDEV) {
435 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
436 mch = vswp->mch;
437 mcap = vswp->mcap;
438 mca_lockp = &vswp->mca_lock;
439 } else {
440 ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
441 mch = portp->p_mch;
442 mcap = portp->mcap;
443 mca_lockp = &portp->mca_lock;
444 }
445
446 if (mch == NULL)
447 return;
448
449 mutex_enter(mca_lockp);
450 for (; mcap != NULL; mcap = mcap->nextp) {
451 if (!mcap->mac_added)
452 continue;
453 (void) mac_multicast_remove(mch, (uchar_t *)&mcap->mca);
454 mcap->mac_added = B_FALSE;
455 }
456 mutex_exit(mca_lockp);
457 }
458
459 void
vsw_update_bandwidth(vsw_t * vswp,vsw_port_t * port,int type,uint64_t maxbw)460 vsw_update_bandwidth(vsw_t *vswp, vsw_port_t *port, int type, uint64_t maxbw)
461 {
462 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
463
464 WRITE_MACCL_ENTER(vswp, port, type);
465 vsw_maccl_set_bandwidth(vswp, port, type, maxbw);
466 RW_MACCL_EXIT(vswp, port, type);
467 }
468
469 /*
470 * Open a mac client and program uncast and multicast addresses
471 * for a port or the interface.
472 * Returns:
473 * 0 on success
474 * non-zero for failure.
475 */
476 int
vsw_mac_client_init(vsw_t * vswp,vsw_port_t * port,int type)477 vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type)
478 {
479 int rv;
480
481 mutex_enter(&vswp->mac_lock);
482 WRITE_MACCL_ENTER(vswp, port, type);
483 rv = vsw_maccl_open(vswp, port, type);
484
485 /* Release mac_lock now */
486 mutex_exit(&vswp->mac_lock);
487
488 if (rv == 0) {
489 (void) vsw_set_hw(vswp, port, type);
490 vsw_mac_multicast_add_all(vswp, port, type);
491 }
492 RW_MACCL_EXIT(vswp, port, type);
493 return (rv);
494 }
495
496 /*
497 * Open a MAC client for a port or an interface.
498 * The flags and their purpose as below:
499 *
500 * MAC_OPEN_FLAGS_SHARES_DESIRED -- This flag is used to indicate
501 * that a port desires a Share. This will be the case with the
502 * the ports that have hybrid mode enabled. This will only cause
503 * MAC layer to allocate a share and corresponding resources
504 * ahead of time. Ports that are not HybridIO enabled are
505 * associated with default group & resources.
506 *
507 * MAC_UNICAST_TAG_DISABLE -- This flag is used for VLAN
508 * support. It will cause MAC to not add any tags, but expect
509 * vsw to tag the packets.
510 *
511 * MAC_UNICAST_STRIP_DISABLE -- This flag is used for VLAN
512 * support. It will case the MAC layer to not strip the tags.
513 * Vsw may have to strip the tag for pvid case.
514 */
515 static int
vsw_maccl_open(vsw_t * vswp,vsw_port_t * port,int type)516 vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type)
517 {
518 int rv = 0;
519 int instance;
520 char mac_cl_name[MAXNAMELEN];
521 const char *dev_name;
522 mac_client_handle_t *mchp;
523 uint64_t flags = 0;
524
525 ASSERT(MUTEX_HELD(&vswp->mac_lock));
526 if (vswp->mh == NULL) {
527 /*
528 * In case net-dev is changed (either set to nothing or
529 * using aggregation device), return success here as the
530 * timeout mechanism will handle it.
531 */
532 return (0);
533 }
534
535 mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
536 if (*mchp != NULL) {
537 /* already open */
538 return (0);
539 }
540 dev_name = ddi_driver_name(vswp->dip);
541 instance = ddi_get_instance(vswp->dip);
542 if (type == VSW_VNETPORT) {
543 if (port->p_hio_enabled)
544 flags |= MAC_OPEN_FLAGS_SHARES_DESIRED;
545 (void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name,
546 instance, "_port", port->p_instance);
547 } else {
548 (void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d",
549 dev_name, "_if", instance);
550 }
551
552 rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags);
553 if (rv != 0) {
554 cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n",
555 vswp->instance, mac_cl_name);
556 }
557
558 if (type != VSW_VNETPORT || !port->p_hio_enabled)
559 mac_client_set_rings(*mchp, MAC_RXRINGS_NONE, MAC_TXRINGS_NONE);
560
561 return (rv);
562 }
563
564 /*
565 * Clean up by removing uncast, multicast addresses and
566 * closing the MAC client for a port or the interface.
567 */
568 void
vsw_mac_client_cleanup(vsw_t * vswp,vsw_port_t * port,int type)569 vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type)
570 {
571 WRITE_MACCL_ENTER(vswp, port, type);
572 vsw_mac_multicast_remove_all(vswp, port, type);
573 vsw_unset_hw(vswp, port, type);
574 vsw_maccl_close(vswp, port, type);
575 RW_MACCL_EXIT(vswp, port, type);
576 }
577
578 /*
579 * Close a MAC client for a port or an interface.
580 */
581 static void
vsw_maccl_close(vsw_t * vswp,vsw_port_t * port,int type)582 vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type)
583 {
584 mac_client_handle_t *mchp;
585
586 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
587
588 mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
589 if (*mchp != NULL) {
590 mac_client_close(*mchp, 0);
591 *mchp = NULL;
592 }
593 }
594
595 /*
596 * Cleanup MAC client related stuff for all ports.
597 */
598 void
vsw_mac_cleanup_ports(vsw_t * vswp)599 vsw_mac_cleanup_ports(vsw_t *vswp)
600 {
601 vsw_port_list_t *plist = &vswp->plist;
602 vsw_port_t *port;
603
604 READ_ENTER(&plist->lockrw);
605 for (port = plist->head; port != NULL; port = port->p_next) {
606 vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
607 }
608 RW_EXIT(&plist->lockrw);
609 }
610
611 /*
612 * Depending on the mode specified, the capabilites and capacity
613 * of the underlying device setup the physical device.
614 *
615 * If in layer 3 mode, then do nothing.
616 *
617 * If in layer 2 mode, open a mac client and program the mac-address
618 * and vlan-ids. The MAC layer will take care of programming
619 * the address into h/w or set the h/w into promiscuous mode.
620 *
621 * Returns 0 success, 1 on failure.
622 */
623 int
vsw_set_hw(vsw_t * vswp,vsw_port_t * port,int type)624 vsw_set_hw(vsw_t *vswp, vsw_port_t *port, int type)
625 {
626 int err = 1;
627
628 D1(vswp, "%s: enter", __func__);
629
630 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
631
632 if (vswp->smode == VSW_LAYER3)
633 return (0);
634
635 if (type == VSW_VNETPORT) {
636 ASSERT(port != NULL);
637 err = vsw_set_port_hw_addr(port);
638 } else {
639 err = vsw_set_if_hw_addr(vswp);
640 }
641
642 D1(vswp, "%s: exit", __func__);
643 return (err);
644 }
645
646 /*
647 * If in layer 3 mode do nothing.
648 *
649 * If in layer 2 switched mode remove the address from the physical
650 * device.
651 *
652 * If in layer 2 promiscuous mode disable promisc mode.
653 *
654 * Returns 0 on success.
655 */
656 void
vsw_unset_hw(vsw_t * vswp,vsw_port_t * port,int type)657 vsw_unset_hw(vsw_t *vswp, vsw_port_t *port, int type)
658 {
659 D1(vswp, "%s: enter", __func__);
660
661 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
662
663 if (vswp->smode == VSW_LAYER3)
664 return;
665
666 if (type == VSW_VNETPORT) {
667 ASSERT(port != NULL);
668 vsw_unset_hw_addr(vswp, port, type);
669 } else {
670 vsw_unset_hw_addr(vswp, NULL, type);
671 }
672
673 D1(vswp, "%s: exit", __func__);
674 }
675
676 /*
677 * Program the macaddress and vlans of a port.
678 *
679 * Returns 0 on sucess, 1 on failure.
680 */
681 static int
vsw_set_port_hw_addr(vsw_port_t * port)682 vsw_set_port_hw_addr(vsw_port_t *port)
683 {
684 vsw_t *vswp = port->p_vswp;
685 mac_diag_t diag;
686 uint8_t *macaddr;
687 uint16_t vid = VLAN_ID_NONE;
688 int rv;
689 uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE |
690 MAC_UNICAST_STRIP_DISABLE;
691
692 D1(vswp, "%s: enter", __func__);
693
694 ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
695 if (port->p_mch == NULL)
696 return (0);
697
698 /*
699 * If the port has a specific 'pvid', then
700 * register with that vlan-id, otherwise register
701 * with VLAN_ID_NONE.
702 */
703 if (port->pvid != vswp->default_vlan_id) {
704 vid = port->pvid;
705 }
706 macaddr = (uint8_t *)port->p_macaddr.ether_addr_octet;
707
708 if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
709 mac_flags |= MAC_UNICAST_HW;
710 }
711
712 if (port->addr_set == B_FALSE) {
713 port->p_muh = NULL;
714 rv = mac_unicast_add(port->p_mch, macaddr, mac_flags,
715 &port->p_muh, vid, &diag);
716
717 if (rv != 0) {
718 cmn_err(CE_WARN, "vsw%d: Failed to program"
719 "macaddr,vid(%s, %d) err=%d",
720 vswp->instance, ether_sprintf((void *)macaddr),
721 vid, rv);
722 return (rv);
723 }
724 port->addr_set = B_TRUE;
725
726 D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
727 __func__, ether_sprintf((void *)macaddr), vid,
728 vswp->physname);
729 }
730
731 /* Add vlans to the MAC layer */
732 vsw_mac_add_vlans(vswp, port->p_mch, macaddr,
733 mac_flags, port->vids, port->nvids);
734
735 /* Configure bandwidth to the MAC layer */
736 vsw_maccl_set_bandwidth(NULL, port, VSW_VNETPORT, port->p_bandwidth);
737
738 mac_rx_set(port->p_mch, vsw_port_rx_cb, (void *)port);
739
740 D1(vswp, "%s: exit", __func__);
741 return (rv);
742 }
743
744 /*
745 * Program the macaddress and vlans of a port.
746 *
747 * Returns 0 on sucess, 1 on failure.
748 */
749 static int
vsw_set_if_hw_addr(vsw_t * vswp)750 vsw_set_if_hw_addr(vsw_t *vswp)
751 {
752 mac_diag_t diag;
753 uint8_t *macaddr;
754 uint8_t primary_addr[ETHERADDRL];
755 uint16_t vid = VLAN_ID_NONE;
756 int rv;
757 uint16_t mac_flags = MAC_UNICAST_TAG_DISABLE |
758 MAC_UNICAST_STRIP_DISABLE;
759
760 D1(vswp, "%s: enter", __func__);
761
762 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
763 if (vswp->mch == NULL)
764 return (0);
765
766 macaddr = (uint8_t *)vswp->if_addr.ether_addr_octet;
767
768 /* check if it is the primary macaddr of the card. */
769 mac_unicast_primary_get(vswp->mh, primary_addr);
770 if (ether_cmp((void *)primary_addr, (void*)macaddr) == 0) {
771 mac_flags |= MAC_UNICAST_PRIMARY;
772 }
773
774 /*
775 * If the interface has a specific 'pvid', then
776 * register with that vlan-id, otherwise register
777 * with VLAN_ID_NONE.
778 */
779 if (vswp->pvid != vswp->default_vlan_id) {
780 vid = vswp->pvid;
781 }
782
783 if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
784 mac_flags |= MAC_UNICAST_HW;
785 }
786
787 if (vswp->addr_set == B_FALSE) {
788 vswp->muh = NULL;
789 rv = mac_unicast_add(vswp->mch, macaddr, mac_flags,
790 &vswp->muh, vid, &diag);
791
792 if (rv != 0) {
793 cmn_err(CE_WARN, "vsw%d: Failed to program"
794 "macaddr,vid(%s, %d) err=%d",
795 vswp->instance, ether_sprintf((void *)macaddr),
796 vid, rv);
797 return (rv);
798 }
799 vswp->addr_set = B_TRUE;
800
801 D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
802 __func__, ether_sprintf((void *)macaddr), vid,
803 vswp->physname);
804 }
805
806 vsw_mac_add_vlans(vswp, vswp->mch, macaddr, mac_flags,
807 vswp->vids, vswp->nvids);
808
809 vsw_maccl_set_bandwidth(vswp, NULL, VSW_LOCALDEV, vswp->bandwidth);
810
811 mac_rx_set(vswp->mch, vsw_if_rx_cb, (void *)vswp);
812
813 D1(vswp, "%s: exit", __func__);
814 return (rv);
815 }
816
817 /*
818 * Remove a unicast mac address which has previously been programmed
819 * into HW.
820 *
821 * Returns 0 on sucess, 1 on failure.
822 */
823 static void
vsw_unset_hw_addr(vsw_t * vswp,vsw_port_t * port,int type)824 vsw_unset_hw_addr(vsw_t *vswp, vsw_port_t *port, int type)
825 {
826 vsw_vlanid_t *vids;
827 int nvids;
828 mac_client_handle_t mch = NULL;
829
830 D1(vswp, "%s: enter", __func__);
831
832 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
833
834 if (type == VSW_VNETPORT) {
835 ASSERT(port != NULL);
836 ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
837 vids = port->vids;
838 nvids = port->nvids;
839 } else {
840 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
841 vids = vswp->vids;
842 nvids = vswp->nvids;
843 }
844
845 /* First clear the callback */
846 if (type == VSW_LOCALDEV) {
847 mch = vswp->mch;
848 } else if (type == VSW_VNETPORT) {
849 mch = port->p_mch;
850 }
851
852
853 if (mch == NULL) {
854 return;
855 }
856
857 mac_rx_clear(mch);
858
859 /* Remove vlans */
860 vsw_mac_remove_vlans(mch, vids, nvids);
861
862 if ((type == VSW_LOCALDEV) && (vswp->addr_set == B_TRUE)) {
863 (void) mac_unicast_remove(vswp->mch, vswp->muh);
864 vswp->muh = NULL;
865 D2(vswp, "removed vsw interface mac-addr from "
866 "the device %s", vswp->physname);
867 vswp->addr_set = B_FALSE;
868
869 } else if ((type == VSW_VNETPORT) && (port->addr_set == B_TRUE)) {
870 (void) mac_unicast_remove(port->p_mch, port->p_muh);
871 port->p_muh = NULL;
872 D2(vswp, "removed port(0x%p) mac-addr from "
873 "the device %s", port, vswp->physname);
874 port->addr_set = B_FALSE;
875 }
876
877 D1(vswp, "%s: exit", __func__);
878 }
879
880 /*
881 * receive callback routine for vsw interface. Invoked by MAC layer when there
882 * are pkts being passed up from physical device for this vsw interface.
883 */
884 /* ARGSUSED */
885 static void
vsw_if_rx_cb(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)886 vsw_if_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
887 boolean_t loopback)
888 {
889 _NOTE(ARGUNUSED(mrh))
890
891 vsw_t *vswp = (vsw_t *)arg;
892 mblk_t *mpt;
893 int count;
894
895 ASSERT(vswp != NULL);
896
897 D1(vswp, "%s: enter", __func__);
898
899 READ_ENTER(&vswp->if_lockrw);
900 if (vswp->if_state & VSW_IF_UP) {
901 RW_EXIT(&vswp->if_lockrw);
902 count = vsw_vlan_frame_untag(vswp, VSW_LOCALDEV, &mp, &mpt);
903 if (count != 0) {
904 mac_rx(vswp->if_mh, NULL, mp);
905 }
906 } else {
907 RW_EXIT(&vswp->if_lockrw);
908 freemsgchain(mp);
909 }
910
911 D1(vswp, "%s: exit", __func__);
912 }
913
914 /*
915 * receive callback routine for port. Invoked by MAC layer when there
916 * are pkts being passed up from physical device for this port.
917 */
918 /* ARGSUSED */
919 static void
vsw_port_rx_cb(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)920 vsw_port_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
921 boolean_t loopback)
922 {
923 _NOTE(ARGUNUSED(mrh))
924
925 vsw_t *vswp;
926 vsw_port_t *port = arg;
927
928 ASSERT(port != NULL);
929
930 vswp = port->p_vswp;
931
932 D1(vswp, "vsw_port_rx_cb: enter");
933
934 /*
935 * Send the packets to the peer directly.
936 */
937 (void) vsw_portsend(port, mp);
938
939 D1(vswp, "vsw_port_rx_cb: exit");
940 }
941
942 /*
943 * Send a message out over the physical device
944 * via the MAC layer.
945 *
946 * Returns any mblks that it was unable to transmit.
947 */
948 mblk_t *
vsw_tx_msg(vsw_t * vswp,mblk_t * mp,int caller,vsw_port_t * port)949 vsw_tx_msg(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port)
950 {
951 mac_client_handle_t mch;
952 mac_unicast_handle_t muh;
953
954 READ_MACCL_ENTER(vswp, port, caller);
955
956 mch = (caller == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
957 muh = (caller == VSW_LOCALDEV) ? vswp->muh : port->p_muh;
958
959 if (mch == NULL || muh == NULL) {
960 RW_MACCL_EXIT(vswp, port, caller);
961 return (mp);
962 }
963
964 /* packets are sent or dropped */
965 (void) mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
966 RW_MACCL_EXIT(vswp, port, caller);
967 return (NULL);
968 }
969
970 /*
971 * vsw_port_mac_reconfig -- Cleanup and close the MAC client
972 * and reopen and re-configure the MAC client with new flags etc.
973 * This function is useful for two different purposes:
974 * 1) To update the MAC client with new vlan-ids. This is done
975 * by freeing the existing vlan-ids and reopen with the new
976 * vlan-ids.
977 *
978 * 2) If the Hybrid mode status of a port changes, then the
979 * MAC client need to be closed and re-opened, otherwise,
980 * Share related resources may not be freed(hybird mode disabled)
981 * or assigned(hybrid mode enabled). To accomplish this,
982 * this function simply closes and reopens the MAC client.
983 * The reopen will result in using the flags based on the
984 * new hybrid mode of the port.
985 */
986 void
vsw_port_mac_reconfig(vsw_port_t * portp,boolean_t update_vlans,uint16_t new_pvid,vsw_vlanid_t * new_vids,int new_nvids)987 vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
988 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
989 {
990 vsw_t *vswp = portp->p_vswp;
991 int rv;
992
993 D1(vswp, "%s: enter", __func__);
994 /*
995 * Remove the multi-cast addresses, unicast address
996 * and close the mac-client.
997 */
998 mutex_enter(&vswp->mac_lock);
999 WRITE_ENTER(&portp->maccl_rwlock);
1000 vsw_mac_multicast_remove_all(vswp, portp, VSW_VNETPORT);
1001 vsw_unset_hw(vswp, portp, VSW_VNETPORT);
1002 vsw_maccl_close(vswp, portp, VSW_VNETPORT);
1003
1004 if (update_vlans == B_TRUE) {
1005 if (portp->nvids != 0) {
1006 kmem_free(portp->vids,
1007 sizeof (vsw_vlanid_t) * portp->nvids);
1008 portp->vids = NULL;
1009 portp->nvids = 0;
1010 }
1011 portp->vids = new_vids;
1012 portp->nvids = new_nvids;
1013 portp->pvid = new_pvid;
1014 }
1015
1016 /*
1017 * Now re-open the mac-client and
1018 * configure unicast addr and multicast addrs.
1019 */
1020 rv = vsw_maccl_open(vswp, portp, VSW_VNETPORT);
1021 if (rv != 0) {
1022 goto recret;
1023 }
1024
1025 if (vsw_set_hw(vswp, portp, VSW_VNETPORT)) {
1026 cmn_err(CE_NOTE, "!vsw%d: port:%d failed to "
1027 "set unicast address\n", vswp->instance, portp->p_instance);
1028 goto recret;
1029 }
1030
1031 vsw_mac_multicast_add_all(vswp, portp, VSW_VNETPORT);
1032
1033 recret:
1034 RW_EXIT(&portp->maccl_rwlock);
1035 mutex_exit(&vswp->mac_lock);
1036 D1(vswp, "%s: exit", __func__);
1037 }
1038
1039 /*
1040 * vsw_if_mac_reconfig -- Reconfigure the vsw interfaace's mac-client
1041 * by closing and re-opening it. This function is used handle the
1042 * following two cases:
1043 *
1044 * 1) Handle the MAC address change for the interface.
1045 * 2) Handle vlan update.
1046 */
1047 void
vsw_if_mac_reconfig(vsw_t * vswp,boolean_t update_vlans,uint16_t new_pvid,vsw_vlanid_t * new_vids,int new_nvids)1048 vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
1049 uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
1050 {
1051 int rv;
1052
1053 D1(vswp, "%s: enter", __func__);
1054 /*
1055 * Remove the multi-cast addresses, unicast address
1056 * and close the mac-client.
1057 */
1058 mutex_enter(&vswp->mac_lock);
1059 WRITE_ENTER(&vswp->maccl_rwlock);
1060 vsw_mac_multicast_remove_all(vswp, NULL, VSW_LOCALDEV);
1061 vsw_unset_hw(vswp, NULL, VSW_LOCALDEV);
1062 vsw_maccl_close(vswp, NULL, VSW_LOCALDEV);
1063
1064 if (update_vlans == B_TRUE) {
1065 if (vswp->nvids != 0) {
1066 kmem_free(vswp->vids,
1067 sizeof (vsw_vlanid_t) * vswp->nvids);
1068 vswp->vids = NULL;
1069 vswp->nvids = 0;
1070 }
1071 vswp->vids = new_vids;
1072 vswp->nvids = new_nvids;
1073 vswp->pvid = new_pvid;
1074 }
1075
1076 /*
1077 * Now re-open the mac-client and
1078 * configure unicast addr and multicast addrs.
1079 */
1080 rv = vsw_maccl_open(vswp, NULL, VSW_LOCALDEV);
1081 if (rv != 0) {
1082 goto ifrecret;
1083 }
1084
1085 if (vsw_set_hw(vswp, NULL, VSW_LOCALDEV)) {
1086 cmn_err(CE_NOTE, "!vsw%d:failed to set unicast address\n",
1087 vswp->instance);
1088 goto ifrecret;
1089 }
1090
1091 vsw_mac_multicast_add_all(vswp, NULL, VSW_LOCALDEV);
1092
1093 ifrecret:
1094 RW_EXIT(&vswp->maccl_rwlock);
1095 mutex_exit(&vswp->mac_lock);
1096 D1(vswp, "%s: exit", __func__);
1097 }
1098
1099 /*
1100 * vsw_mac_port_reconfig_vlans -- Reconfigure a port to handle
1101 * vlan configuration update. As the removal of the last unicast-address,vid
1102 * from the MAC client results in releasing all resources, it expects
1103 * no Shares to be associated with such MAC client.
1104 *
1105 * To handle vlan configuration update for a port that already has
1106 * a Share bound, then we need to free that share prior to reconfiguration.
1107 * Initiate the hybrdIO setup again after the completion of reconfiguration.
1108 */
1109 void
vsw_mac_port_reconfig_vlans(vsw_port_t * portp,uint16_t new_pvid,vsw_vlanid_t * new_vids,int new_nvids)1110 vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
1111 vsw_vlanid_t *new_vids, int new_nvids)
1112 {
1113 /*
1114 * As the reconfiguration involves the close of
1115 * mac client, cleanup HybridIO and later restart
1116 * HybridIO setup again.
1117 */
1118 if (portp->p_hio_enabled == B_TRUE) {
1119 vsw_hio_stop_port(portp);
1120 }
1121 vsw_port_mac_reconfig(portp, B_TRUE, new_pvid, new_vids, new_nvids);
1122 if (portp->p_hio_enabled == B_TRUE) {
1123 /* reset to setup the HybridIO again. */
1124 vsw_hio_port_reset(portp, B_FALSE);
1125 }
1126 }
1127
1128 /* Add vlans to MAC client */
1129 static void
vsw_mac_add_vlans(vsw_t * vswp,mac_client_handle_t mch,uint8_t * macaddr,uint16_t flags,vsw_vlanid_t * vids,int nvids)1130 vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, uint8_t *macaddr,
1131 uint16_t flags, vsw_vlanid_t *vids, int nvids)
1132 {
1133 vsw_vlanid_t *vidp;
1134 mac_diag_t diag;
1135 int rv;
1136 int i;
1137
1138 flags |= MAC_UNICAST_TAG_DISABLE | MAC_UNICAST_STRIP_DISABLE;
1139
1140 /* Add vlans to the MAC layer */
1141 for (i = 0; i < nvids; i++) {
1142 vidp = &vids[i];
1143
1144 if (vidp->vl_set == B_TRUE) {
1145 continue;
1146 }
1147
1148 rv = mac_unicast_add(mch, macaddr, flags,
1149 &vidp->vl_muh, vidp->vl_vid, &diag);
1150 if (rv != 0) {
1151 cmn_err(CE_WARN, "vsw%d: Failed to program"
1152 "macaddr,vid(%s, %d) err=%d",
1153 vswp->instance, ether_sprintf((void *)macaddr),
1154 vidp->vl_vid, rv);
1155 } else {
1156 vidp->vl_set = B_TRUE;
1157 D2(vswp, "%s:programmed macaddr(%s) vid(%d) "
1158 "into device %s", __func__,
1159 ether_sprintf((void *)macaddr),
1160 vidp->vl_vid, vswp->physname);
1161 }
1162 }
1163 }
1164
1165 /* Remove vlans from the MAC client */
1166 static void
vsw_mac_remove_vlans(mac_client_handle_t mch,vsw_vlanid_t * vids,int nvids)1167 vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, int nvids)
1168 {
1169 int i;
1170 vsw_vlanid_t *vidp;
1171
1172 for (i = 0; i < nvids; i++) {
1173 vidp = &vids[i];
1174 if (vidp->vl_set == B_FALSE) {
1175 continue;
1176 }
1177 (void) mac_unicast_remove(mch, vidp->vl_muh);
1178 vidp->vl_set = B_FALSE;
1179 }
1180 }
1181
1182 #define ARH_FIXED_LEN 8 /* Length of fixed part of ARP header(see arp.h) */
1183
1184 /*
1185 * Send a gratuitous RARP packet to notify the physical switch to update its
1186 * Layer2 forwarding table for the given mac address. This is done to allow the
1187 * switch to quickly learn the macaddr-port association when a guest is live
1188 * migrated or when vsw's physical device is changed dynamically. Any protocol
1189 * packet would serve this purpose, but we choose RARP, as it allows us to
1190 * accomplish this within L2 (ie, no need to specify IP addr etc in the packet)
1191 * The macaddr of vnet is retained across migration. Hence, we don't need to
1192 * update the arp cache of other hosts within the broadcast domain. Note that
1193 * it is harmless to send these RARP packets during normal port attach of a
1194 * client vnet. This can can be turned off if needed, by setting
1195 * vsw_publish_macaddr_count to zero in /etc/system.
1196 */
1197 void
vsw_publish_macaddr(vsw_t * vswp,vsw_port_t * portp)1198 vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp)
1199 {
1200 mblk_t *mp;
1201 mblk_t *bp;
1202 struct arphdr *arh;
1203 struct ether_header *ehp;
1204 int count = 0;
1205 int plen = 4;
1206 uint8_t *cp;
1207
1208 mp = allocb(ETHERMIN, BPRI_MED);
1209 if (mp == NULL) {
1210 return;
1211 }
1212
1213 /* Initialize eth header */
1214 ehp = (struct ether_header *)mp->b_rptr;
1215 bcopy(ðerbroadcastaddr, &ehp->ether_dhost, ETHERADDRL);
1216 bcopy(&portp->p_macaddr, &ehp->ether_shost, ETHERADDRL);
1217 ehp->ether_type = htons(ETHERTYPE_REVARP);
1218
1219 /* Initialize arp packet */
1220 arh = (struct arphdr *)(mp->b_rptr + sizeof (struct ether_header));
1221 cp = (uint8_t *)arh;
1222
1223 arh->ar_hrd = htons(ARPHRD_ETHER); /* Hardware type: ethernet */
1224 arh->ar_pro = htons(ETHERTYPE_IP); /* Protocol type: IP */
1225 arh->ar_hln = ETHERADDRL; /* Length of hardware address: 6 */
1226 arh->ar_pln = plen; /* Length of protocol address: 4 */
1227 arh->ar_op = htons(REVARP_REQUEST); /* Opcode: REVARP Request */
1228
1229 cp += ARH_FIXED_LEN;
1230
1231 /* Sender's hardware address and protocol address */
1232 bcopy(&portp->p_macaddr, cp, ETHERADDRL);
1233 cp += ETHERADDRL;
1234 bzero(cp, plen); /* INADDR_ANY */
1235 cp += plen;
1236
1237 /* Target hardware address and protocol address */
1238 bcopy(&portp->p_macaddr, cp, ETHERADDRL);
1239 cp += ETHERADDRL;
1240 bzero(cp, plen); /* INADDR_ANY */
1241 cp += plen;
1242
1243 mp->b_wptr += ETHERMIN; /* total size is 42; round up to ETHERMIN */
1244
1245 for (count = 0; count < vsw_publish_macaddr_count; count++) {
1246
1247 bp = dupmsg(mp);
1248 if (bp == NULL) {
1249 continue;
1250 }
1251
1252 /* transmit the packet */
1253 bp = vsw_tx_msg(vswp, bp, VSW_VNETPORT, portp);
1254 if (bp != NULL) {
1255 freemsg(bp);
1256 }
1257 }
1258
1259 freemsg(mp);
1260 }
1261
1262 static void
vsw_mac_set_mtu(vsw_t * vswp,uint32_t mtu)1263 vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu)
1264 {
1265 uint_t mtu_orig;
1266 int rv;
1267
1268 rv = mac_set_mtu(vswp->mh, mtu, &mtu_orig);
1269 if (rv != 0) {
1270 cmn_err(CE_NOTE,
1271 "!vsw%d: Unable to set the mtu:%d, in the "
1272 "physical device:%s\n",
1273 vswp->instance, mtu, vswp->physname);
1274 return;
1275 }
1276
1277 /* save the original mtu of physdev to reset it back later if needed */
1278 vswp->mtu_physdev_orig = mtu_orig;
1279 }
1280
1281 /*
1282 * Register a callback with underlying mac layer for notifications.
1283 * We are currently interested in only link-state events.
1284 */
1285 static int
vsw_notify_add(vsw_t * vswp)1286 vsw_notify_add(vsw_t *vswp)
1287 {
1288 mac_notify_handle_t mnh;
1289 uint32_t note;
1290
1291 /*
1292 * Check if the underlying MAC supports link update notification.
1293 */
1294 note = mac_no_notification(vswp->mh);
1295 if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
1296 vswp->phys_no_link_update = B_TRUE;
1297 } else {
1298 vswp->phys_no_link_update = B_FALSE;
1299 }
1300
1301 /*
1302 * Read the current link state of the device and cache it.
1303 */
1304 vswp->phys_link_state = vswp->phys_no_link_update ? LINK_STATE_UP :
1305 mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
1306
1307 /*
1308 * Add notify callback function, if link update is supported.
1309 */
1310 if (vswp->phys_no_link_update == B_TRUE) {
1311 return (0);
1312 }
1313
1314 mnh = mac_notify_add(vswp->mh, vsw_notify_cb, vswp);
1315 if (mnh == 0) {
1316 /* failed */
1317 return (1);
1318 }
1319
1320 vswp->mnh = mnh;
1321 return (0);
1322 }
1323
1324 /*
1325 * Remove notify callback.
1326 */
1327 static int
vsw_notify_rem(vsw_t * vswp)1328 vsw_notify_rem(vsw_t *vswp)
1329 {
1330 int rv;
1331
1332 rv = mac_notify_remove(vswp->mnh, B_FALSE);
1333 return (rv);
1334 }
1335
1336 /*
1337 * Notification callback invoked by the MAC service
1338 * module. Note that we process only link state updates.
1339 */
1340 static void
vsw_notify_cb(void * arg,mac_notify_type_t type)1341 vsw_notify_cb(void *arg, mac_notify_type_t type)
1342 {
1343 vsw_t *vswp = arg;
1344
1345 switch (type) {
1346
1347 case MAC_NOTE_LINK:
1348 vsw_notify_link(vswp);
1349 break;
1350
1351 default:
1352 break;
1353
1354 }
1355 }
1356
1357 /*
1358 * Invoked upon receiving a MAC_NOTE_LINK
1359 * notification for the underlying physical device.
1360 */
1361 static void
vsw_notify_link(vsw_t * vswp)1362 vsw_notify_link(vsw_t *vswp)
1363 {
1364 link_state_t link_state;
1365
1366 /* link state change notification */
1367 link_state = mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
1368
1369 if (vswp->phys_link_state != link_state) {
1370 D3(vswp, "%s: phys_link_state(%d)\n",
1371 __func__, vswp->phys_link_state);
1372
1373 vswp->phys_link_state = link_state;
1374 vsw_physlink_state_update(vswp);
1375 }
1376 }
1377
1378 /*
1379 * Configure the bandwidth limit on the vsw or vnet devices via the MAC layer.
1380 * Note that bandwidth limit is not supported on a HybridIO enabled
1381 * vnet, as the HybridIO assigns a specific unit of hardware resource
1382 * that cannot be changed to limit bandwidth.
1383 */
1384 static void
vsw_maccl_set_bandwidth(vsw_t * vswp,vsw_port_t * port,int type,uint64_t maxbw)1385 vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type, uint64_t maxbw)
1386 {
1387 int rv = 0;
1388 uint64_t *bw;
1389 mac_resource_props_t *mrp;
1390 mac_client_handle_t mch;
1391
1392 ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
1393
1394 if (type == VSW_VNETPORT) {
1395 ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
1396 mch = port->p_mch;
1397 bw = &port->p_bandwidth;
1398 } else {
1399 ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
1400 mch = vswp->mch;
1401 bw = &vswp->bandwidth;
1402 }
1403
1404 if (mch == NULL) {
1405 return;
1406 }
1407
1408 if (maxbw >= MRP_MAXBW_MINVAL || maxbw == 0) {
1409 mrp = kmem_zalloc(sizeof (*mrp), KM_SLEEP);
1410 if (maxbw == 0) {
1411 mrp->mrp_maxbw = MRP_MAXBW_RESETVAL;
1412 } else {
1413 mrp->mrp_maxbw = maxbw;
1414 }
1415 mrp->mrp_mask |= MRP_MAXBW;
1416
1417 rv = mac_client_set_resources(mch, mrp);
1418 if (rv != 0) {
1419 if (type == VSW_VNETPORT) {
1420 cmn_err(CE_NOTE, "!port%d: cannot set "
1421 "bandwidth limit to (%ld), error(%d)\n",
1422 port->p_instance, maxbw, rv);
1423 } else {
1424 cmn_err(CE_NOTE, "!vsw%d: cannot set "
1425 "bandwidth limit to (%ld), error(%d)\n",
1426 vswp->instance, maxbw, rv);
1427 }
1428 } else {
1429 /*
1430 * update with successfully configured bandwidth.
1431 */
1432 *bw = maxbw;
1433 }
1434 kmem_free(mrp, sizeof (*mrp));
1435 }
1436 }
1437