xref: /onnv-gate/usr/src/uts/common/xen/io/xnbo.c (revision 11878:ac93462db6d7)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
2311588Sdavid.edmondson@sun.com  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  * Xen network backend - mac client edition.
295084Sjohnlev  *
305084Sjohnlev  * A driver that sits above an existing GLDv3/Nemo MAC driver and
315084Sjohnlev  * relays packets to/from that driver from/to a guest domain.
325084Sjohnlev  */
335084Sjohnlev 
3411588Sdavid.edmondson@sun.com #ifdef DEBUG
3511588Sdavid.edmondson@sun.com #define	XNBO_DEBUG 1
3611588Sdavid.edmondson@sun.com #endif /* DEBUG */
3711588Sdavid.edmondson@sun.com 
385084Sjohnlev #include "xnb.h"
395084Sjohnlev 
405084Sjohnlev #include <sys/sunddi.h>
418275SEric Cheng #include <sys/ddi.h>
425084Sjohnlev #include <sys/modctl.h>
435084Sjohnlev #include <sys/strsubr.h>
448275SEric Cheng #include <sys/mac_client.h>
458275SEric Cheng #include <sys/mac_provider.h>
468275SEric Cheng #include <sys/mac_client_priv.h>
475084Sjohnlev #include <sys/mac.h>
485084Sjohnlev #include <net/if.h>
495084Sjohnlev #include <sys/dlpi.h>
505084Sjohnlev #include <sys/pattr.h>
515084Sjohnlev #include <xen/sys/xenbus_impl.h>
525084Sjohnlev #include <xen/sys/xendev.h>
5310958Sdme@sun.com #include <sys/sdt.h>
5410958Sdme@sun.com #include <sys/note.h>
555084Sjohnlev 
5611588Sdavid.edmondson@sun.com #ifdef XNBO_DEBUG
5711588Sdavid.edmondson@sun.com boolean_t xnbo_cksum_offload_to_peer = B_TRUE;
5811588Sdavid.edmondson@sun.com boolean_t xnbo_cksum_offload_from_peer = B_TRUE;
5911588Sdavid.edmondson@sun.com #endif /* XNBO_DEBUG */
6011588Sdavid.edmondson@sun.com 
6110958Sdme@sun.com /* Track multicast addresses. */
6210958Sdme@sun.com typedef struct xmca {
6310958Sdme@sun.com 	struct xmca *next;
6410958Sdme@sun.com 	ether_addr_t addr;
6510958Sdme@sun.com } xmca_t;
6610958Sdme@sun.com 
6710958Sdme@sun.com /* State about this device instance. */
685084Sjohnlev typedef struct xnbo {
695084Sjohnlev 	mac_handle_t		o_mh;
708275SEric Cheng 	mac_client_handle_t	o_mch;
718275SEric Cheng 	mac_unicast_handle_t	o_mah;
728275SEric Cheng 	mac_promisc_handle_t	o_mphp;
735084Sjohnlev 	boolean_t		o_running;
745084Sjohnlev 	boolean_t		o_promiscuous;
755084Sjohnlev 	uint32_t		o_hcksum_capab;
7610958Sdme@sun.com 	xmca_t			*o_mca;
7710958Sdme@sun.com 	char			o_link_name[LIFNAMSIZ];
7810958Sdme@sun.com 	boolean_t		o_need_rx_filter;
7910958Sdme@sun.com 	boolean_t		o_need_setphysaddr;
8010958Sdme@sun.com 	boolean_t		o_multicast_control;
815084Sjohnlev } xnbo_t;
825084Sjohnlev 
8310958Sdme@sun.com static void xnbo_close_mac(xnb_t *);
8411037Sdme@sun.com static void i_xnbo_close_mac(xnb_t *, boolean_t);
855084Sjohnlev 
865084Sjohnlev /*
875084Sjohnlev  * Packets from the peer come here.  We pass them to the mac device.
885084Sjohnlev  */
895084Sjohnlev static void
xnbo_to_mac(xnb_t * xnbp,mblk_t * mp)905084Sjohnlev xnbo_to_mac(xnb_t *xnbp, mblk_t *mp)
915084Sjohnlev {
925741Smrj 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
935084Sjohnlev 
945084Sjohnlev 	ASSERT(mp != NULL);
955084Sjohnlev 
965084Sjohnlev 	if (!xnbop->o_running) {
977615SMax.Zhen@Sun.COM 		xnbp->xnb_stat_tx_too_early++;
985084Sjohnlev 		goto fail;
995084Sjohnlev 	}
1005084Sjohnlev 
1018275SEric Cheng 	if (mac_tx(xnbop->o_mch, mp, 0,
1028275SEric Cheng 	    MAC_DROP_ON_NO_DESC, NULL) != NULL) {
1035741Smrj 		xnbp->xnb_stat_mac_full++;
1045084Sjohnlev 	}
1055084Sjohnlev 
1065084Sjohnlev 	return;
1075084Sjohnlev 
1085084Sjohnlev fail:
1095084Sjohnlev 	freemsgchain(mp);
1105084Sjohnlev }
1115084Sjohnlev 
11210958Sdme@sun.com /*
11310958Sdme@sun.com  * Process the checksum flags `flags' provided by the peer for the
11410958Sdme@sun.com  * packet `mp'.
11510958Sdme@sun.com  */
1165084Sjohnlev static mblk_t *
xnbo_cksum_from_peer(xnb_t * xnbp,mblk_t * mp,uint16_t flags)1175084Sjohnlev xnbo_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags)
1185084Sjohnlev {
1195741Smrj 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
1205084Sjohnlev 
1215084Sjohnlev 	ASSERT(mp->b_next == NULL);
1225084Sjohnlev 
1235084Sjohnlev 	if ((flags & NETTXF_csum_blank) != 0) {
12411588Sdavid.edmondson@sun.com 		uint32_t capab = xnbop->o_hcksum_capab;
12511588Sdavid.edmondson@sun.com 
12611588Sdavid.edmondson@sun.com #ifdef XNBO_DEBUG
12711588Sdavid.edmondson@sun.com 		if (!xnbo_cksum_offload_from_peer)
12811588Sdavid.edmondson@sun.com 			capab = 0;
12911588Sdavid.edmondson@sun.com #endif /* XNBO_DEBUG */
13011588Sdavid.edmondson@sun.com 
1315084Sjohnlev 		/*
1325084Sjohnlev 		 * The checksum in the packet is blank.  Determine
1335084Sjohnlev 		 * whether we can do hardware offload and, if so,
1345084Sjohnlev 		 * update the flags on the mblk according.  If not,
1355084Sjohnlev 		 * calculate and insert the checksum using software.
1365084Sjohnlev 		 */
13711588Sdavid.edmondson@sun.com 		mp = xnb_process_cksum_flags(xnbp, mp, capab);
1385084Sjohnlev 	}
1395084Sjohnlev 
1405084Sjohnlev 	return (mp);
1415084Sjohnlev }
1425084Sjohnlev 
14310958Sdme@sun.com /*
14410958Sdme@sun.com  * Calculate the checksum flags to be relayed to the peer for the
14510958Sdme@sun.com  * packet `mp'.
14610958Sdme@sun.com  */
1475084Sjohnlev static uint16_t
xnbo_cksum_to_peer(xnb_t * xnbp,mblk_t * mp)1485084Sjohnlev xnbo_cksum_to_peer(xnb_t *xnbp, mblk_t *mp)
1495084Sjohnlev {
15010958Sdme@sun.com 	_NOTE(ARGUNUSED(xnbp));
1515084Sjohnlev 	uint16_t r = 0;
15210958Sdme@sun.com 	uint32_t pflags, csum;
1535084Sjohnlev 
15411588Sdavid.edmondson@sun.com #ifdef XNBO_DEBUG
15511588Sdavid.edmondson@sun.com 	if (!xnbo_cksum_offload_to_peer)
15611588Sdavid.edmondson@sun.com 		return (0);
15711588Sdavid.edmondson@sun.com #endif /* XNBO_DEBUG */
15811588Sdavid.edmondson@sun.com 
1595084Sjohnlev 	/*
1605084Sjohnlev 	 * We might also check for HCK_PARTIALCKSUM here and,
1615084Sjohnlev 	 * providing that the partial checksum covers the TCP/UDP
1625084Sjohnlev 	 * payload, return NETRXF_data_validated.
1635084Sjohnlev 	 *
1645084Sjohnlev 	 * It seems that it's probably not worthwhile, as even MAC
1655084Sjohnlev 	 * devices which advertise HCKSUM_INET_PARTIAL in their
1665084Sjohnlev 	 * capabilities tend to use HCK_FULLCKSUM on the receive side
1675084Sjohnlev 	 * - they are actually saying that in the output path the
1685084Sjohnlev 	 * caller must use HCK_PARTIALCKSUM.
16910958Sdme@sun.com 	 *
17010958Sdme@sun.com 	 * Then again, if a NIC supports HCK_PARTIALCKSUM in its'
17110958Sdme@sun.com 	 * output path, the host IP stack will use it. If such packets
17210958Sdme@sun.com 	 * are destined for the peer (i.e. looped around) we would
17310958Sdme@sun.com 	 * gain some advantage.
1745084Sjohnlev 	 */
1755084Sjohnlev 
176*11878SVenu.Iyer@Sun.COM 	mac_hcksum_get(mp, NULL, NULL, NULL, &csum, &pflags);
1775084Sjohnlev 
17810958Sdme@sun.com 	/*
17910958Sdme@sun.com 	 * If the MAC driver has asserted that the checksum is
18010958Sdme@sun.com 	 * good, let the peer know.
18110958Sdme@sun.com 	 */
18210958Sdme@sun.com 	if (((pflags & HCK_FULLCKSUM) != 0) &&
18310958Sdme@sun.com 	    (((pflags & HCK_FULLCKSUM_OK) != 0) ||
18410958Sdme@sun.com 	    (csum == 0xffff)))
18510958Sdme@sun.com 		r |= NETRXF_data_validated;
1865084Sjohnlev 
1875084Sjohnlev 	return (r);
1885084Sjohnlev }
1895084Sjohnlev 
1905084Sjohnlev /*
1915084Sjohnlev  * Packets from the mac device come here.  We pass them to the peer.
1925084Sjohnlev  */
1935084Sjohnlev /*ARGSUSED*/
1945084Sjohnlev static void
xnbo_from_mac(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)1958275SEric Cheng xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
1968275SEric Cheng     boolean_t loopback)
1975084Sjohnlev {
1985084Sjohnlev 	xnb_t *xnbp = arg;
1995084Sjohnlev 
2005741Smrj 	mp = xnb_copy_to_peer(xnbp, mp);
2015084Sjohnlev 
2025084Sjohnlev 	if (mp != NULL)
2035084Sjohnlev 		freemsgchain(mp);
2045084Sjohnlev }
2055084Sjohnlev 
2065084Sjohnlev /*
2075084Sjohnlev  * Packets from the mac device come here. We pass them to the peer if
2085084Sjohnlev  * the destination mac address matches or it's a multicast/broadcast
2095084Sjohnlev  * address.
2105084Sjohnlev  */
2115084Sjohnlev static void
xnbo_from_mac_filter(void * arg,mac_resource_handle_t mrh,mblk_t * mp,boolean_t loopback)2128275SEric Cheng xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
2138275SEric Cheng     boolean_t loopback)
2145084Sjohnlev {
21510958Sdme@sun.com 	_NOTE(ARGUNUSED(loopback));
2165084Sjohnlev 	xnb_t *xnbp = arg;
2175741Smrj 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
2185084Sjohnlev 	mblk_t *next, *keep, *keep_head, *free, *free_head;
2195084Sjohnlev 
2205084Sjohnlev 	keep = keep_head = free = free_head = NULL;
2215084Sjohnlev 
2225084Sjohnlev #define	ADD(list, bp)				\
2235084Sjohnlev 	if (list != NULL)			\
2245084Sjohnlev 		list->b_next = bp;		\
2255084Sjohnlev 	else					\
2265084Sjohnlev 		list##_head = bp;		\
2275084Sjohnlev 	list = bp;
2285084Sjohnlev 
2295084Sjohnlev 	for (; mp != NULL; mp = next) {
2305084Sjohnlev 		mac_header_info_t hdr_info;
2315084Sjohnlev 
2325084Sjohnlev 		next = mp->b_next;
2335084Sjohnlev 		mp->b_next = NULL;
2345084Sjohnlev 
2355084Sjohnlev 		if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) {
2365084Sjohnlev 			ADD(free, mp);
2375084Sjohnlev 			continue;
2385084Sjohnlev 		}
2395084Sjohnlev 
2405084Sjohnlev 		if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) ||
2415084Sjohnlev 		    (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) {
2425084Sjohnlev 			ADD(keep, mp);
2435084Sjohnlev 			continue;
2445084Sjohnlev 		}
2455084Sjohnlev 
2465741Smrj 		if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr,
2475741Smrj 		    sizeof (xnbp->xnb_mac_addr)) == 0) {
2485084Sjohnlev 			ADD(keep, mp);
2495084Sjohnlev 			continue;
2505084Sjohnlev 		}
2515084Sjohnlev 
2525084Sjohnlev 		ADD(free, mp);
2535084Sjohnlev 	}
2545084Sjohnlev #undef	ADD
2555084Sjohnlev 
2565084Sjohnlev 	if (keep_head != NULL)
2578275SEric Cheng 		xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE);
2585084Sjohnlev 
2595084Sjohnlev 	if (free_head != NULL)
2605084Sjohnlev 		freemsgchain(free_head);
2615084Sjohnlev }
2625084Sjohnlev 
2635084Sjohnlev static boolean_t
xnbo_open_mac(xnb_t * xnbp,char * mac)2645084Sjohnlev xnbo_open_mac(xnb_t *xnbp, char *mac)
2655084Sjohnlev {
2665741Smrj 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
26710958Sdme@sun.com 	int err;
2685084Sjohnlev 	const mac_info_t *mi;
2698275SEric Cheng 	void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t);
2708275SEric Cheng 	struct ether_addr ea;
2715903Ssowmini 	uint_t max_sdu;
2728275SEric Cheng 	mac_diag_t diag;
2735084Sjohnlev 
2745895Syz147064 	if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) {
2755084Sjohnlev 		cmn_err(CE_WARN, "xnbo_open_mac: "
2765895Syz147064 		    "cannot open mac for link %s (%d)", mac, err);
2775084Sjohnlev 		return (B_FALSE);
2785084Sjohnlev 	}
2795084Sjohnlev 	ASSERT(xnbop->o_mh != NULL);
2805084Sjohnlev 
2815084Sjohnlev 	mi = mac_info(xnbop->o_mh);
2825084Sjohnlev 	ASSERT(mi != NULL);
2835084Sjohnlev 
2845084Sjohnlev 	if (mi->mi_media != DL_ETHER) {
2855084Sjohnlev 		cmn_err(CE_WARN, "xnbo_open_mac: "
2865895Syz147064 		    "device is not DL_ETHER (%d)", mi->mi_media);
28711037Sdme@sun.com 		i_xnbo_close_mac(xnbp, B_TRUE);
2885084Sjohnlev 		return (B_FALSE);
2895084Sjohnlev 	}
2905084Sjohnlev 	if (mi->mi_media != mi->mi_nativemedia) {
2915084Sjohnlev 		cmn_err(CE_WARN, "xnbo_open_mac: "
2925895Syz147064 		    "device media and native media mismatch (%d != %d)",
2935084Sjohnlev 		    mi->mi_media, mi->mi_nativemedia);
29411037Sdme@sun.com 		i_xnbo_close_mac(xnbp, B_TRUE);
2955084Sjohnlev 		return (B_FALSE);
2965084Sjohnlev 	}
2975903Ssowmini 
2985903Ssowmini 	mac_sdu_get(xnbop->o_mh, NULL, &max_sdu);
2995903Ssowmini 	if (max_sdu > XNBMAXPKT) {
3005903Ssowmini 		cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)",
3015903Ssowmini 		    max_sdu);
30211037Sdme@sun.com 		i_xnbo_close_mac(xnbp, B_TRUE);
3035084Sjohnlev 		return (B_FALSE);
3045084Sjohnlev 	}
3055084Sjohnlev 
3069473SVenu.Iyer@Sun.COM 	/*
3079473SVenu.Iyer@Sun.COM 	 * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a
3089473SVenu.Iyer@Sun.COM 	 * guest on the localhost itself. In this case we would have the MAC
3099473SVenu.Iyer@Sun.COM 	 * client open for the guest being migrated *and* also for the
3109473SVenu.Iyer@Sun.COM 	 * migrated guest (i.e. the former will be active till the migration
3119473SVenu.Iyer@Sun.COM 	 * is complete when the latter will be activated). This flag states
3129473SVenu.Iyer@Sun.COM 	 * that it is OK for mac_unicast_add to add the primary MAC unicast
3139473SVenu.Iyer@Sun.COM 	 * address multiple times.
3149473SVenu.Iyer@Sun.COM 	 */
3158275SEric Cheng 	if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL,
3169473SVenu.Iyer@Sun.COM 	    MAC_OPEN_FLAGS_USE_DATALINK_NAME |
3179473SVenu.Iyer@Sun.COM 	    MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) {
3188275SEric Cheng 		cmn_err(CE_WARN, "xnbo_open_mac: "
3198275SEric Cheng 		    "error (%d) opening mac client", err);
32011037Sdme@sun.com 		i_xnbo_close_mac(xnbp, B_TRUE);
3218275SEric Cheng 		return (B_FALSE);
3228275SEric Cheng 	}
3238275SEric Cheng 
32410958Sdme@sun.com 	if (xnbop->o_need_rx_filter)
3255084Sjohnlev 		rx_fn = xnbo_from_mac_filter;
3265084Sjohnlev 	else
3275084Sjohnlev 		rx_fn = xnbo_from_mac;
3285084Sjohnlev 
3299473SVenu.Iyer@Sun.COM 	err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY,
33010958Sdme@sun.com 	    &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL,
33110958Sdme@sun.com 	    xnbp);
3329473SVenu.Iyer@Sun.COM 	if (err != 0) {
3339473SVenu.Iyer@Sun.COM 		cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary "
3349473SVenu.Iyer@Sun.COM 		    "MAC address of %s: %d", mac, err);
33511037Sdme@sun.com 		i_xnbo_close_mac(xnbp, B_TRUE);
3369473SVenu.Iyer@Sun.COM 		return (B_FALSE);
3379473SVenu.Iyer@Sun.COM 	}
33810958Sdme@sun.com 	if (!xnbop->o_multicast_control) {
3398275SEric Cheng 		err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL,
3408833SVenu.Iyer@Sun.COM 		    rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP |
3418833SVenu.Iyer@Sun.COM 		    MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
3428275SEric Cheng 		if (err != 0) {
3438275SEric Cheng 			cmn_err(CE_WARN, "xnbo_open_mac: "
3448275SEric Cheng 			    "cannot enable promiscuous mode of %s: %d",
3458275SEric Cheng 			    mac, err);
34611037Sdme@sun.com 			i_xnbo_close_mac(xnbp, B_TRUE);
3478275SEric Cheng 			return (B_FALSE);
3488275SEric Cheng 		}
3498275SEric Cheng 		xnbop->o_promiscuous = B_TRUE;
3508275SEric Cheng 	}
3515084Sjohnlev 
35210958Sdme@sun.com 	if (xnbop->o_need_setphysaddr) {
3538275SEric Cheng 		err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr);
3545084Sjohnlev 		/* Warn, but continue on. */
3555084Sjohnlev 		if (err != 0) {
3565741Smrj 			bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet,
3575084Sjohnlev 			    ETHERADDRL);
3585084Sjohnlev 			cmn_err(CE_WARN, "xnbo_open_mac: "
3595084Sjohnlev 			    "cannot set MAC address of %s to "
3608275SEric Cheng 			    "%s: %d", mac, ether_sprintf(&ea), err);
3615084Sjohnlev 		}
3625084Sjohnlev 	}
3635084Sjohnlev 
36410958Sdme@sun.com 	if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM,
36510958Sdme@sun.com 	    &xnbop->o_hcksum_capab))
36610958Sdme@sun.com 		xnbop->o_hcksum_capab = 0;
36710958Sdme@sun.com 
3685084Sjohnlev 	xnbop->o_running = B_TRUE;
3695084Sjohnlev 
3705084Sjohnlev 	return (B_TRUE);
3715084Sjohnlev }
3725084Sjohnlev 
37310958Sdme@sun.com static void
xnbo_close_mac(xnb_t * xnbp)37410958Sdme@sun.com xnbo_close_mac(xnb_t *xnbp)
3755084Sjohnlev {
37611037Sdme@sun.com 	i_xnbo_close_mac(xnbp, B_FALSE);
37711037Sdme@sun.com }
37811037Sdme@sun.com 
37911037Sdme@sun.com static void
i_xnbo_close_mac(xnb_t * xnbp,boolean_t locked)38011037Sdme@sun.com i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked)
38111037Sdme@sun.com {
38210958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
38310958Sdme@sun.com 	xmca_t *loop;
3845084Sjohnlev 
38511037Sdme@sun.com 	ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock));
38611037Sdme@sun.com 
3875084Sjohnlev 	if (xnbop->o_mh == NULL)
3885084Sjohnlev 		return;
3895084Sjohnlev 
39010958Sdme@sun.com 	if (xnbop->o_running)
3915084Sjohnlev 		xnbop->o_running = B_FALSE;
39210958Sdme@sun.com 
39311037Sdme@sun.com 	if (!locked)
39411037Sdme@sun.com 		mutex_enter(&xnbp->xnb_state_lock);
39510958Sdme@sun.com 	loop = xnbop->o_mca;
39610958Sdme@sun.com 	xnbop->o_mca = NULL;
39711037Sdme@sun.com 	if (!locked)
39811037Sdme@sun.com 		mutex_exit(&xnbp->xnb_state_lock);
39910958Sdme@sun.com 
40010958Sdme@sun.com 	while (loop != NULL) {
40110958Sdme@sun.com 		xmca_t *next = loop->next;
40210958Sdme@sun.com 
40310958Sdme@sun.com 		DTRACE_PROBE3(mcast_remove,
40410958Sdme@sun.com 		    (char *), "close",
40510958Sdme@sun.com 		    (void *), xnbp,
40610958Sdme@sun.com 		    (etheraddr_t *), loop->addr);
40710958Sdme@sun.com 		(void) mac_multicast_remove(xnbop->o_mch, loop->addr);
40810958Sdme@sun.com 		kmem_free(loop, sizeof (*loop));
40910958Sdme@sun.com 		loop = next;
4105084Sjohnlev 	}
4115084Sjohnlev 
4125084Sjohnlev 	if (xnbop->o_promiscuous) {
4138826Sdme@sun.com 		if (xnbop->o_mphp != NULL) {
4149044SGirish.Moodalbail@Sun.COM 			mac_promisc_remove(xnbop->o_mphp);
4158826Sdme@sun.com 			xnbop->o_mphp = NULL;
4168826Sdme@sun.com 		}
4175084Sjohnlev 		xnbop->o_promiscuous = B_FALSE;
4188275SEric Cheng 	} else {
4198826Sdme@sun.com 		if (xnbop->o_mch != NULL)
4208826Sdme@sun.com 			mac_rx_clear(xnbop->o_mch);
4215084Sjohnlev 	}
4225084Sjohnlev 
4238275SEric Cheng 	if (xnbop->o_mah != NULL) {
4248275SEric Cheng 		(void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah);
4258275SEric Cheng 		xnbop->o_mah = NULL;
4265084Sjohnlev 	}
4275084Sjohnlev 
4288275SEric Cheng 	if (xnbop->o_mch != NULL) {
4298275SEric Cheng 		mac_client_close(xnbop->o_mch, 0);
4308275SEric Cheng 		xnbop->o_mch = NULL;
4315084Sjohnlev 	}
4325084Sjohnlev 
4335084Sjohnlev 	mac_close(xnbop->o_mh);
4345084Sjohnlev 	xnbop->o_mh = NULL;
4355084Sjohnlev }
4365084Sjohnlev 
4375084Sjohnlev /*
43810958Sdme@sun.com  * Hotplug has completed and we are connected to the peer. We have all
43910958Sdme@sun.com  * the information we need to exchange traffic, so open the MAC device
44010958Sdme@sun.com  * and configure it appropriately.
44110958Sdme@sun.com  */
44210958Sdme@sun.com static boolean_t
xnbo_start_connect(xnb_t * xnbp)44310958Sdme@sun.com xnbo_start_connect(xnb_t *xnbp)
44410958Sdme@sun.com {
44510958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
44610958Sdme@sun.com 
44710958Sdme@sun.com 	return (xnbo_open_mac(xnbp, xnbop->o_link_name));
44810958Sdme@sun.com }
44910958Sdme@sun.com 
45010958Sdme@sun.com /*
45110958Sdme@sun.com  * The guest has successfully synchronize with this instance. We read
45210958Sdme@sun.com  * the configuration of the guest from xenstore to check whether the
45310958Sdme@sun.com  * guest requests multicast control. If not (the default) we make a
45410958Sdme@sun.com  * note that the MAC device needs to be used in promiscious mode.
45510958Sdme@sun.com  */
45610958Sdme@sun.com static boolean_t
xnbo_peer_connected(xnb_t * xnbp)45710958Sdme@sun.com xnbo_peer_connected(xnb_t *xnbp)
45810958Sdme@sun.com {
45910958Sdme@sun.com 	char *oename;
46010958Sdme@sun.com 	int request;
46110958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
46210958Sdme@sun.com 
46310958Sdme@sun.com 	oename = xvdi_get_oename(xnbp->xnb_devinfo);
46410958Sdme@sun.com 
46510958Sdme@sun.com 	if (xenbus_scanf(XBT_NULL, oename,
46610958Sdme@sun.com 	    "request-multicast-control", "%d", &request) != 0)
46710958Sdme@sun.com 		request = 0;
46810958Sdme@sun.com 	xnbop->o_multicast_control = (request > 0);
46910958Sdme@sun.com 
47010958Sdme@sun.com 	return (B_TRUE);
47110958Sdme@sun.com }
47210958Sdme@sun.com 
47310958Sdme@sun.com /*
47410958Sdme@sun.com  * The guest domain has closed down the inter-domain connection. We
47510958Sdme@sun.com  * close the underlying MAC device.
4765084Sjohnlev  */
4775084Sjohnlev static void
xnbo_peer_disconnected(xnb_t * xnbp)47810958Sdme@sun.com xnbo_peer_disconnected(xnb_t *xnbp)
47910958Sdme@sun.com {
48010958Sdme@sun.com 	xnbo_close_mac(xnbp);
48110958Sdme@sun.com }
48210958Sdme@sun.com 
48310958Sdme@sun.com /*
48410958Sdme@sun.com  * The hotplug script has completed. We read information from xenstore
48510958Sdme@sun.com  * about our configuration, most notably the name of the MAC device we
48610958Sdme@sun.com  * should use.
48710958Sdme@sun.com  */
48810958Sdme@sun.com static boolean_t
xnbo_hotplug_connected(xnb_t * xnbp)48910958Sdme@sun.com xnbo_hotplug_connected(xnb_t *xnbp)
4905084Sjohnlev {
49110958Sdme@sun.com 	char *xsname;
49210958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
49310958Sdme@sun.com 	int need;
49410958Sdme@sun.com 
49510958Sdme@sun.com 	xsname = xvdi_get_xsname(xnbp->xnb_devinfo);
49610958Sdme@sun.com 
49710958Sdme@sun.com 	if (xenbus_scanf(XBT_NULL, xsname,
49810958Sdme@sun.com 	    "nic", "%s", xnbop->o_link_name) != 0) {
49910958Sdme@sun.com 		cmn_err(CE_WARN, "xnbo_connect: "
50010958Sdme@sun.com 		    "cannot read nic name from %s", xsname);
50110958Sdme@sun.com 		return (B_FALSE);
50210958Sdme@sun.com 	}
50310958Sdme@sun.com 
50410958Sdme@sun.com 	if (xenbus_scanf(XBT_NULL, xsname,
50510958Sdme@sun.com 	    "SUNW-need-rx-filter", "%d", &need) != 0)
50610958Sdme@sun.com 		need = 0;
50710958Sdme@sun.com 	xnbop->o_need_rx_filter = (need > 0);
50810958Sdme@sun.com 
50910958Sdme@sun.com 	if (xenbus_scanf(XBT_NULL, xsname,
51010958Sdme@sun.com 	    "SUNW-need-set-physaddr", "%d", &need) != 0)
51110958Sdme@sun.com 		need = 0;
51210958Sdme@sun.com 	xnbop->o_need_setphysaddr = (need > 0);
51310958Sdme@sun.com 
51410958Sdme@sun.com 	return (B_TRUE);
5155084Sjohnlev }
5165084Sjohnlev 
5175084Sjohnlev /*
51810958Sdme@sun.com  * Find the multicast address `addr', return B_TRUE if it is one that
51910958Sdme@sun.com  * we receive. If `remove', remove it from the set received.
5205084Sjohnlev  */
52110958Sdme@sun.com static boolean_t
xnbo_mcast_find(xnb_t * xnbp,ether_addr_t * addr,boolean_t remove)52210958Sdme@sun.com xnbo_mcast_find(xnb_t *xnbp, ether_addr_t *addr, boolean_t remove)
5235084Sjohnlev {
52410958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
52510958Sdme@sun.com 	xmca_t *prev, *del, *this;
52610958Sdme@sun.com 
52710958Sdme@sun.com 	ASSERT(MUTEX_HELD(&xnbp->xnb_state_lock));
52810958Sdme@sun.com 	ASSERT(xnbop->o_promiscuous == B_FALSE);
52910958Sdme@sun.com 
53010958Sdme@sun.com 	prev = del = NULL;
53110958Sdme@sun.com 
53210958Sdme@sun.com 	this = xnbop->o_mca;
53310958Sdme@sun.com 
53410958Sdme@sun.com 	while (this != NULL) {
53510958Sdme@sun.com 		if (bcmp(&this->addr, addr, sizeof (this->addr)) == 0) {
53610958Sdme@sun.com 			del = this;
53710958Sdme@sun.com 			if (remove) {
53810958Sdme@sun.com 				if (prev == NULL)
53910958Sdme@sun.com 					xnbop->o_mca = this->next;
54010958Sdme@sun.com 				else
54110958Sdme@sun.com 					prev->next = this->next;
54210958Sdme@sun.com 			}
54310958Sdme@sun.com 			break;
54410958Sdme@sun.com 		}
54510958Sdme@sun.com 
54610958Sdme@sun.com 		prev = this;
54710958Sdme@sun.com 		this = this->next;
54810958Sdme@sun.com 	}
54910958Sdme@sun.com 
55010958Sdme@sun.com 	if (del == NULL)
55110958Sdme@sun.com 		return (B_FALSE);
55210958Sdme@sun.com 
55310958Sdme@sun.com 	if (remove) {
55410958Sdme@sun.com 		DTRACE_PROBE3(mcast_remove,
55510958Sdme@sun.com 		    (char *), "remove",
55610958Sdme@sun.com 		    (void *), xnbp,
55710958Sdme@sun.com 		    (etheraddr_t *), del->addr);
55810958Sdme@sun.com 		mac_multicast_remove(xnbop->o_mch, del->addr);
55910958Sdme@sun.com 		kmem_free(del, sizeof (*del));
56010958Sdme@sun.com 	}
56110958Sdme@sun.com 
56210958Sdme@sun.com 	return (B_TRUE);
56310958Sdme@sun.com }
56410958Sdme@sun.com 
56510958Sdme@sun.com /*
56610958Sdme@sun.com  * Add the multicast address `addr' to the set received.
56710958Sdme@sun.com  */
56810958Sdme@sun.com static boolean_t
xnbo_mcast_add(xnb_t * xnbp,ether_addr_t * addr)56910958Sdme@sun.com xnbo_mcast_add(xnb_t *xnbp, ether_addr_t *addr)
57010958Sdme@sun.com {
57110958Sdme@sun.com 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
57210958Sdme@sun.com 	boolean_t r = B_FALSE;
57310958Sdme@sun.com 
57410958Sdme@sun.com 	ASSERT(xnbop->o_promiscuous == B_FALSE);
57510958Sdme@sun.com 
57610958Sdme@sun.com 	mutex_enter(&xnbp->xnb_state_lock);
57710958Sdme@sun.com 
57810958Sdme@sun.com 	if (xnbo_mcast_find(xnbp, addr, B_FALSE)) {
57910958Sdme@sun.com 		r = B_TRUE;
58010958Sdme@sun.com 	} else if (mac_multicast_add(xnbop->o_mch,
58110958Sdme@sun.com 	    (const uint8_t *)addr) == 0) {
58210958Sdme@sun.com 		xmca_t *mca;
58310958Sdme@sun.com 
58410958Sdme@sun.com 		DTRACE_PROBE3(mcast_add,
58510958Sdme@sun.com 		    (char *), "add",
58610958Sdme@sun.com 		    (void *), xnbp,
58710958Sdme@sun.com 		    (etheraddr_t *), addr);
58810958Sdme@sun.com 
58910958Sdme@sun.com 		mca = kmem_alloc(sizeof (*mca), KM_SLEEP);
59010958Sdme@sun.com 		bcopy(addr, &mca->addr, sizeof (mca->addr));
59110958Sdme@sun.com 
59210958Sdme@sun.com 		mca->next = xnbop->o_mca;
59310958Sdme@sun.com 		xnbop->o_mca = mca;
59410958Sdme@sun.com 
59510958Sdme@sun.com 		r = B_TRUE;
59610958Sdme@sun.com 	}
59710958Sdme@sun.com 
59810958Sdme@sun.com 	mutex_exit(&xnbp->xnb_state_lock);
59910958Sdme@sun.com 
60010958Sdme@sun.com 	return (r);
60110958Sdme@sun.com }
60210958Sdme@sun.com 
60310958Sdme@sun.com /*
60410958Sdme@sun.com  * Remove the multicast address `addr' from the set received.
60510958Sdme@sun.com  */
60610958Sdme@sun.com static boolean_t
xnbo_mcast_del(xnb_t * xnbp,ether_addr_t * addr)60710958Sdme@sun.com xnbo_mcast_del(xnb_t *xnbp, ether_addr_t *addr)
60810958Sdme@sun.com {
60910958Sdme@sun.com 	boolean_t r;
61010958Sdme@sun.com 
61110958Sdme@sun.com 	mutex_enter(&xnbp->xnb_state_lock);
61210958Sdme@sun.com 	r = xnbo_mcast_find(xnbp, addr, B_TRUE);
61310958Sdme@sun.com 	mutex_exit(&xnbp->xnb_state_lock);
61410958Sdme@sun.com 
61510958Sdme@sun.com 	return (r);
6165084Sjohnlev }
6175084Sjohnlev 
6185084Sjohnlev static int
xnbo_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6195084Sjohnlev xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6205084Sjohnlev {
6215084Sjohnlev 	static xnb_flavour_t flavour = {
62210958Sdme@sun.com 		xnbo_to_mac, xnbo_peer_connected, xnbo_peer_disconnected,
62310958Sdme@sun.com 		xnbo_hotplug_connected, xnbo_start_connect,
6245084Sjohnlev 		xnbo_cksum_from_peer, xnbo_cksum_to_peer,
62510958Sdme@sun.com 		xnbo_mcast_add, xnbo_mcast_del,
6265084Sjohnlev 	};
6275084Sjohnlev 	xnbo_t *xnbop;
6285084Sjohnlev 
6295084Sjohnlev 	switch (cmd) {
6305084Sjohnlev 	case DDI_ATTACH:
6315084Sjohnlev 		break;
6325084Sjohnlev 	case DDI_RESUME:
6335084Sjohnlev 		return (DDI_SUCCESS);
6345084Sjohnlev 	default:
6355084Sjohnlev 		return (DDI_FAILURE);
6365084Sjohnlev 	}
6375084Sjohnlev 
6385084Sjohnlev 	xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP);
6395084Sjohnlev 
6405084Sjohnlev 	if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) {
6415084Sjohnlev 		kmem_free(xnbop, sizeof (*xnbop));
6425084Sjohnlev 		return (DDI_FAILURE);
6435084Sjohnlev 	}
6445084Sjohnlev 
6455084Sjohnlev 	return (DDI_SUCCESS);
6465084Sjohnlev }
6475084Sjohnlev 
6485084Sjohnlev static int
xnbo_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)6495084Sjohnlev xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
6505084Sjohnlev {
6515084Sjohnlev 	xnb_t *xnbp = ddi_get_driver_private(dip);
6525741Smrj 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
6535084Sjohnlev 
6545084Sjohnlev 	switch (cmd) {
6555084Sjohnlev 	case DDI_DETACH:
6565084Sjohnlev 		break;
6575084Sjohnlev 	case DDI_SUSPEND:
6585084Sjohnlev 		return (DDI_SUCCESS);
6595084Sjohnlev 	default:
6605084Sjohnlev 		return (DDI_FAILURE);
6615084Sjohnlev 	}
6625084Sjohnlev 
6635741Smrj 	mutex_enter(&xnbp->xnb_tx_lock);
6645741Smrj 	mutex_enter(&xnbp->xnb_rx_lock);
6655084Sjohnlev 
6665741Smrj 	if (!xnbp->xnb_detachable || xnbp->xnb_connected ||
6677615SMax.Zhen@Sun.COM 	    (xnbp->xnb_tx_buf_count > 0)) {
6685741Smrj 		mutex_exit(&xnbp->xnb_rx_lock);
6695741Smrj 		mutex_exit(&xnbp->xnb_tx_lock);
6705084Sjohnlev 
6715084Sjohnlev 		return (DDI_FAILURE);
6725084Sjohnlev 	}
6735084Sjohnlev 
6745741Smrj 	mutex_exit(&xnbp->xnb_rx_lock);
6755741Smrj 	mutex_exit(&xnbp->xnb_tx_lock);
6765084Sjohnlev 
67710958Sdme@sun.com 	xnbo_close_mac(xnbp);
6785084Sjohnlev 	kmem_free(xnbop, sizeof (*xnbop));
6795084Sjohnlev 
6805084Sjohnlev 	xnb_detach(dip);
6815084Sjohnlev 
6825084Sjohnlev 	return (DDI_SUCCESS);
6835084Sjohnlev }
6845084Sjohnlev 
6855084Sjohnlev static struct cb_ops cb_ops = {
6865084Sjohnlev 	nulldev,		/* open */
6875084Sjohnlev 	nulldev,		/* close */
6885084Sjohnlev 	nodev,			/* strategy */
6895084Sjohnlev 	nodev,			/* print */
6905084Sjohnlev 	nodev,			/* dump */
6915084Sjohnlev 	nodev,			/* read */
6925084Sjohnlev 	nodev,			/* write */
6935084Sjohnlev 	nodev,			/* ioctl */
6945084Sjohnlev 	nodev,			/* devmap */
6955084Sjohnlev 	nodev,			/* mmap */
6965084Sjohnlev 	nodev,			/* segmap */
6975084Sjohnlev 	nochpoll,		/* poll */
6985084Sjohnlev 	ddi_prop_op,		/* cb_prop_op */
6995084Sjohnlev 	0,			/* streamtab  */
7005084Sjohnlev 	D_NEW | D_MP | D_64BIT	/* Driver compatibility flag */
7015084Sjohnlev };
7025084Sjohnlev 
7035084Sjohnlev static struct dev_ops ops = {
7045084Sjohnlev 	DEVO_REV,		/* devo_rev */
7055084Sjohnlev 	0,			/* devo_refcnt  */
7065084Sjohnlev 	nulldev,		/* devo_getinfo */
7075084Sjohnlev 	nulldev,		/* devo_identify */
7085084Sjohnlev 	nulldev,		/* devo_probe */
7095084Sjohnlev 	xnbo_attach,		/* devo_attach */
7105084Sjohnlev 	xnbo_detach,		/* devo_detach */
7115084Sjohnlev 	nodev,			/* devo_reset */
7125084Sjohnlev 	&cb_ops,		/* devo_cb_ops */
7135084Sjohnlev 	(struct bus_ops *)0,	/* devo_bus_ops */
7147656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
7157656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
7165084Sjohnlev };
7175084Sjohnlev 
7185084Sjohnlev static struct modldrv modldrv = {
7197615SMax.Zhen@Sun.COM 	&mod_driverops, "xnbo driver", &ops,
7205084Sjohnlev };
7215084Sjohnlev 
7225084Sjohnlev static struct modlinkage modlinkage = {
7235084Sjohnlev 	MODREV_1, &modldrv, NULL
7245084Sjohnlev };
7255084Sjohnlev 
7265084Sjohnlev int
_init(void)7275084Sjohnlev _init(void)
7285084Sjohnlev {
7295084Sjohnlev 	return (mod_install(&modlinkage));
7305084Sjohnlev }
7315084Sjohnlev 
7325084Sjohnlev int
_info(struct modinfo * modinfop)7335084Sjohnlev _info(struct modinfo *modinfop)
7345084Sjohnlev {
7355084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
7365084Sjohnlev }
7375084Sjohnlev 
7385084Sjohnlev int
_fini(void)7395084Sjohnlev _fini(void)
7405084Sjohnlev {
7415084Sjohnlev 	return (mod_remove(&modlinkage));
7425084Sjohnlev }
743