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