1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* 23*fd0939efSDavid Edmondson * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26843e1988Sjohnlev 27843e1988Sjohnlev /* 28843e1988Sjohnlev * Xen network backend - mac client edition. 29843e1988Sjohnlev * 30843e1988Sjohnlev * A driver that sits above an existing GLDv3/Nemo MAC driver and 31843e1988Sjohnlev * relays packets to/from that driver from/to a guest domain. 32843e1988Sjohnlev */ 33843e1988Sjohnlev 34*fd0939efSDavid Edmondson #ifdef DEBUG 35*fd0939efSDavid Edmondson #define XNBO_DEBUG 1 36*fd0939efSDavid Edmondson #endif /* DEBUG */ 37*fd0939efSDavid Edmondson 38843e1988Sjohnlev #include "xnb.h" 39843e1988Sjohnlev 40843e1988Sjohnlev #include <sys/sunddi.h> 41da14cebeSEric Cheng #include <sys/ddi.h> 42843e1988Sjohnlev #include <sys/modctl.h> 43843e1988Sjohnlev #include <sys/strsubr.h> 44da14cebeSEric Cheng #include <sys/mac_client.h> 45da14cebeSEric Cheng #include <sys/mac_provider.h> 46da14cebeSEric Cheng #include <sys/mac_client_priv.h> 47843e1988Sjohnlev #include <sys/mac.h> 48843e1988Sjohnlev #include <net/if.h> 49843e1988Sjohnlev #include <sys/dlpi.h> 50843e1988Sjohnlev #include <sys/pattr.h> 51843e1988Sjohnlev #include <xen/sys/xenbus_impl.h> 52843e1988Sjohnlev #include <xen/sys/xendev.h> 5356567907SDavid Edmondson #include <sys/sdt.h> 5456567907SDavid Edmondson #include <sys/note.h> 55843e1988Sjohnlev 56*fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 57*fd0939efSDavid Edmondson boolean_t xnbo_cksum_offload_to_peer = B_TRUE; 58*fd0939efSDavid Edmondson boolean_t xnbo_cksum_offload_from_peer = B_TRUE; 59*fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 60*fd0939efSDavid Edmondson 6156567907SDavid Edmondson /* Track multicast addresses. */ 6256567907SDavid Edmondson typedef struct xmca { 6356567907SDavid Edmondson struct xmca *next; 6456567907SDavid Edmondson ether_addr_t addr; 6556567907SDavid Edmondson } xmca_t; 6656567907SDavid Edmondson 6756567907SDavid Edmondson /* State about this device instance. */ 68843e1988Sjohnlev typedef struct xnbo { 69843e1988Sjohnlev mac_handle_t o_mh; 70da14cebeSEric Cheng mac_client_handle_t o_mch; 71da14cebeSEric Cheng mac_unicast_handle_t o_mah; 72da14cebeSEric Cheng mac_promisc_handle_t o_mphp; 73843e1988Sjohnlev boolean_t o_running; 74843e1988Sjohnlev boolean_t o_promiscuous; 75843e1988Sjohnlev uint32_t o_hcksum_capab; 7656567907SDavid Edmondson xmca_t *o_mca; 7756567907SDavid Edmondson char o_link_name[LIFNAMSIZ]; 7856567907SDavid Edmondson boolean_t o_need_rx_filter; 7956567907SDavid Edmondson boolean_t o_need_setphysaddr; 8056567907SDavid Edmondson boolean_t o_multicast_control; 81843e1988Sjohnlev } xnbo_t; 82843e1988Sjohnlev 8356567907SDavid Edmondson static void xnbo_close_mac(xnb_t *); 840324f02aSDavid Edmondson static void i_xnbo_close_mac(xnb_t *, boolean_t); 85843e1988Sjohnlev 86843e1988Sjohnlev /* 87843e1988Sjohnlev * Packets from the peer come here. We pass them to the mac device. 88843e1988Sjohnlev */ 89843e1988Sjohnlev static void 90843e1988Sjohnlev xnbo_to_mac(xnb_t *xnbp, mblk_t *mp) 91843e1988Sjohnlev { 92551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 93843e1988Sjohnlev 94843e1988Sjohnlev ASSERT(mp != NULL); 95843e1988Sjohnlev 96843e1988Sjohnlev if (!xnbop->o_running) { 97024c26efSMax zhen xnbp->xnb_stat_tx_too_early++; 98843e1988Sjohnlev goto fail; 99843e1988Sjohnlev } 100843e1988Sjohnlev 101da14cebeSEric Cheng if (mac_tx(xnbop->o_mch, mp, 0, 102da14cebeSEric Cheng MAC_DROP_ON_NO_DESC, NULL) != NULL) { 103551bc2a6Smrj xnbp->xnb_stat_mac_full++; 104843e1988Sjohnlev } 105843e1988Sjohnlev 106843e1988Sjohnlev return; 107843e1988Sjohnlev 108843e1988Sjohnlev fail: 109843e1988Sjohnlev freemsgchain(mp); 110843e1988Sjohnlev } 111843e1988Sjohnlev 11256567907SDavid Edmondson /* 11356567907SDavid Edmondson * Process the checksum flags `flags' provided by the peer for the 11456567907SDavid Edmondson * packet `mp'. 11556567907SDavid Edmondson */ 116843e1988Sjohnlev static mblk_t * 117843e1988Sjohnlev xnbo_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags) 118843e1988Sjohnlev { 119551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 120843e1988Sjohnlev 121843e1988Sjohnlev ASSERT(mp->b_next == NULL); 122843e1988Sjohnlev 123843e1988Sjohnlev if ((flags & NETTXF_csum_blank) != 0) { 124*fd0939efSDavid Edmondson uint32_t capab = xnbop->o_hcksum_capab; 125*fd0939efSDavid Edmondson 126*fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 127*fd0939efSDavid Edmondson if (!xnbo_cksum_offload_from_peer) 128*fd0939efSDavid Edmondson capab = 0; 129*fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 130*fd0939efSDavid Edmondson 131843e1988Sjohnlev /* 132843e1988Sjohnlev * The checksum in the packet is blank. Determine 133843e1988Sjohnlev * whether we can do hardware offload and, if so, 134843e1988Sjohnlev * update the flags on the mblk according. If not, 135843e1988Sjohnlev * calculate and insert the checksum using software. 136843e1988Sjohnlev */ 137*fd0939efSDavid Edmondson mp = xnb_process_cksum_flags(xnbp, mp, capab); 138843e1988Sjohnlev } 139843e1988Sjohnlev 140843e1988Sjohnlev return (mp); 141843e1988Sjohnlev } 142843e1988Sjohnlev 14356567907SDavid Edmondson /* 14456567907SDavid Edmondson * Calculate the checksum flags to be relayed to the peer for the 14556567907SDavid Edmondson * packet `mp'. 14656567907SDavid Edmondson */ 147843e1988Sjohnlev static uint16_t 148843e1988Sjohnlev xnbo_cksum_to_peer(xnb_t *xnbp, mblk_t *mp) 149843e1988Sjohnlev { 15056567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp)); 151843e1988Sjohnlev uint16_t r = 0; 15256567907SDavid Edmondson uint32_t pflags, csum; 153843e1988Sjohnlev 154*fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 155*fd0939efSDavid Edmondson if (!xnbo_cksum_offload_to_peer) 156*fd0939efSDavid Edmondson return (0); 157*fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 158*fd0939efSDavid Edmondson 159843e1988Sjohnlev /* 160843e1988Sjohnlev * We might also check for HCK_PARTIALCKSUM here and, 161843e1988Sjohnlev * providing that the partial checksum covers the TCP/UDP 162843e1988Sjohnlev * payload, return NETRXF_data_validated. 163843e1988Sjohnlev * 164843e1988Sjohnlev * It seems that it's probably not worthwhile, as even MAC 165843e1988Sjohnlev * devices which advertise HCKSUM_INET_PARTIAL in their 166843e1988Sjohnlev * capabilities tend to use HCK_FULLCKSUM on the receive side 167843e1988Sjohnlev * - they are actually saying that in the output path the 168843e1988Sjohnlev * caller must use HCK_PARTIALCKSUM. 16956567907SDavid Edmondson * 17056567907SDavid Edmondson * Then again, if a NIC supports HCK_PARTIALCKSUM in its' 17156567907SDavid Edmondson * output path, the host IP stack will use it. If such packets 17256567907SDavid Edmondson * are destined for the peer (i.e. looped around) we would 17356567907SDavid Edmondson * gain some advantage. 174843e1988Sjohnlev */ 175843e1988Sjohnlev 17656567907SDavid Edmondson hcksum_retrieve(mp, NULL, NULL, NULL, NULL, 17756567907SDavid Edmondson NULL, &csum, &pflags); 178843e1988Sjohnlev 17956567907SDavid Edmondson /* 18056567907SDavid Edmondson * If the MAC driver has asserted that the checksum is 18156567907SDavid Edmondson * good, let the peer know. 18256567907SDavid Edmondson */ 18356567907SDavid Edmondson if (((pflags & HCK_FULLCKSUM) != 0) && 18456567907SDavid Edmondson (((pflags & HCK_FULLCKSUM_OK) != 0) || 18556567907SDavid Edmondson (csum == 0xffff))) 18656567907SDavid Edmondson r |= NETRXF_data_validated; 187843e1988Sjohnlev 188843e1988Sjohnlev return (r); 189843e1988Sjohnlev } 190843e1988Sjohnlev 191843e1988Sjohnlev /* 192843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer. 193843e1988Sjohnlev */ 194843e1988Sjohnlev /*ARGSUSED*/ 195843e1988Sjohnlev static void 196da14cebeSEric Cheng xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 197da14cebeSEric Cheng boolean_t loopback) 198843e1988Sjohnlev { 199843e1988Sjohnlev xnb_t *xnbp = arg; 200843e1988Sjohnlev 201551bc2a6Smrj mp = xnb_copy_to_peer(xnbp, mp); 202843e1988Sjohnlev 203843e1988Sjohnlev if (mp != NULL) 204843e1988Sjohnlev freemsgchain(mp); 205843e1988Sjohnlev } 206843e1988Sjohnlev 207843e1988Sjohnlev /* 208843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer if 209843e1988Sjohnlev * the destination mac address matches or it's a multicast/broadcast 210843e1988Sjohnlev * address. 211843e1988Sjohnlev */ 212843e1988Sjohnlev static void 213da14cebeSEric Cheng xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 214da14cebeSEric Cheng boolean_t loopback) 215843e1988Sjohnlev { 21656567907SDavid Edmondson _NOTE(ARGUNUSED(loopback)); 217843e1988Sjohnlev xnb_t *xnbp = arg; 218551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 219843e1988Sjohnlev mblk_t *next, *keep, *keep_head, *free, *free_head; 220843e1988Sjohnlev 221843e1988Sjohnlev keep = keep_head = free = free_head = NULL; 222843e1988Sjohnlev 223843e1988Sjohnlev #define ADD(list, bp) \ 224843e1988Sjohnlev if (list != NULL) \ 225843e1988Sjohnlev list->b_next = bp; \ 226843e1988Sjohnlev else \ 227843e1988Sjohnlev list##_head = bp; \ 228843e1988Sjohnlev list = bp; 229843e1988Sjohnlev 230843e1988Sjohnlev for (; mp != NULL; mp = next) { 231843e1988Sjohnlev mac_header_info_t hdr_info; 232843e1988Sjohnlev 233843e1988Sjohnlev next = mp->b_next; 234843e1988Sjohnlev mp->b_next = NULL; 235843e1988Sjohnlev 236843e1988Sjohnlev if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) { 237843e1988Sjohnlev ADD(free, mp); 238843e1988Sjohnlev continue; 239843e1988Sjohnlev } 240843e1988Sjohnlev 241843e1988Sjohnlev if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || 242843e1988Sjohnlev (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) { 243843e1988Sjohnlev ADD(keep, mp); 244843e1988Sjohnlev continue; 245843e1988Sjohnlev } 246843e1988Sjohnlev 247551bc2a6Smrj if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr, 248551bc2a6Smrj sizeof (xnbp->xnb_mac_addr)) == 0) { 249843e1988Sjohnlev ADD(keep, mp); 250843e1988Sjohnlev continue; 251843e1988Sjohnlev } 252843e1988Sjohnlev 253843e1988Sjohnlev ADD(free, mp); 254843e1988Sjohnlev } 255843e1988Sjohnlev #undef ADD 256843e1988Sjohnlev 257843e1988Sjohnlev if (keep_head != NULL) 258da14cebeSEric Cheng xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE); 259843e1988Sjohnlev 260843e1988Sjohnlev if (free_head != NULL) 261843e1988Sjohnlev freemsgchain(free_head); 262843e1988Sjohnlev } 263843e1988Sjohnlev 264843e1988Sjohnlev static boolean_t 265843e1988Sjohnlev xnbo_open_mac(xnb_t *xnbp, char *mac) 266843e1988Sjohnlev { 267551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 26856567907SDavid Edmondson int err; 269843e1988Sjohnlev const mac_info_t *mi; 270da14cebeSEric Cheng void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t); 271da14cebeSEric Cheng struct ether_addr ea; 272e7801d59Ssowmini uint_t max_sdu; 273da14cebeSEric Cheng mac_diag_t diag; 274843e1988Sjohnlev 275d62bc4baSyz if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) { 276843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 277d62bc4baSyz "cannot open mac for link %s (%d)", mac, err); 278843e1988Sjohnlev return (B_FALSE); 279843e1988Sjohnlev } 280843e1988Sjohnlev ASSERT(xnbop->o_mh != NULL); 281843e1988Sjohnlev 282843e1988Sjohnlev mi = mac_info(xnbop->o_mh); 283843e1988Sjohnlev ASSERT(mi != NULL); 284843e1988Sjohnlev 285843e1988Sjohnlev if (mi->mi_media != DL_ETHER) { 286843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 287d62bc4baSyz "device is not DL_ETHER (%d)", mi->mi_media); 2880324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 289843e1988Sjohnlev return (B_FALSE); 290843e1988Sjohnlev } 291843e1988Sjohnlev if (mi->mi_media != mi->mi_nativemedia) { 292843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 293d62bc4baSyz "device media and native media mismatch (%d != %d)", 294843e1988Sjohnlev mi->mi_media, mi->mi_nativemedia); 2950324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 296843e1988Sjohnlev return (B_FALSE); 297843e1988Sjohnlev } 298e7801d59Ssowmini 299e7801d59Ssowmini mac_sdu_get(xnbop->o_mh, NULL, &max_sdu); 300e7801d59Ssowmini if (max_sdu > XNBMAXPKT) { 301e7801d59Ssowmini cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)", 302e7801d59Ssowmini max_sdu); 3030324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 304843e1988Sjohnlev return (B_FALSE); 305843e1988Sjohnlev } 306843e1988Sjohnlev 307fc4e975dSVenugopal Iyer /* 308fc4e975dSVenugopal Iyer * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a 309fc4e975dSVenugopal Iyer * guest on the localhost itself. In this case we would have the MAC 310fc4e975dSVenugopal Iyer * client open for the guest being migrated *and* also for the 311fc4e975dSVenugopal Iyer * migrated guest (i.e. the former will be active till the migration 312fc4e975dSVenugopal Iyer * is complete when the latter will be activated). This flag states 313fc4e975dSVenugopal Iyer * that it is OK for mac_unicast_add to add the primary MAC unicast 314fc4e975dSVenugopal Iyer * address multiple times. 315fc4e975dSVenugopal Iyer */ 316da14cebeSEric Cheng if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL, 317fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_USE_DATALINK_NAME | 318fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) { 319da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 320da14cebeSEric Cheng "error (%d) opening mac client", err); 3210324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 322da14cebeSEric Cheng return (B_FALSE); 323da14cebeSEric Cheng } 324da14cebeSEric Cheng 32556567907SDavid Edmondson if (xnbop->o_need_rx_filter) 326843e1988Sjohnlev rx_fn = xnbo_from_mac_filter; 327843e1988Sjohnlev else 328843e1988Sjohnlev rx_fn = xnbo_from_mac; 329843e1988Sjohnlev 330fc4e975dSVenugopal Iyer err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY, 33156567907SDavid Edmondson &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL, 33256567907SDavid Edmondson xnbp); 333fc4e975dSVenugopal Iyer if (err != 0) { 334fc4e975dSVenugopal Iyer cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary " 335fc4e975dSVenugopal Iyer "MAC address of %s: %d", mac, err); 3360324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 337fc4e975dSVenugopal Iyer return (B_FALSE); 338fc4e975dSVenugopal Iyer } 33956567907SDavid Edmondson if (!xnbop->o_multicast_control) { 340da14cebeSEric Cheng err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL, 341ae6aa22aSVenugopal Iyer rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP | 342ae6aa22aSVenugopal Iyer MAC_PROMISC_FLAGS_VLAN_TAG_STRIP); 343da14cebeSEric Cheng if (err != 0) { 344da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 345da14cebeSEric Cheng "cannot enable promiscuous mode of %s: %d", 346da14cebeSEric Cheng mac, err); 3470324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 348da14cebeSEric Cheng return (B_FALSE); 349da14cebeSEric Cheng } 350da14cebeSEric Cheng xnbop->o_promiscuous = B_TRUE; 351da14cebeSEric Cheng } 352843e1988Sjohnlev 35356567907SDavid Edmondson if (xnbop->o_need_setphysaddr) { 354da14cebeSEric Cheng err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr); 355843e1988Sjohnlev /* Warn, but continue on. */ 356843e1988Sjohnlev if (err != 0) { 357551bc2a6Smrj bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet, 358843e1988Sjohnlev ETHERADDRL); 359843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 360843e1988Sjohnlev "cannot set MAC address of %s to " 361da14cebeSEric Cheng "%s: %d", mac, ether_sprintf(&ea), err); 362843e1988Sjohnlev } 363843e1988Sjohnlev } 364843e1988Sjohnlev 36556567907SDavid Edmondson if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, 36656567907SDavid Edmondson &xnbop->o_hcksum_capab)) 36756567907SDavid Edmondson xnbop->o_hcksum_capab = 0; 36856567907SDavid Edmondson 369843e1988Sjohnlev xnbop->o_running = B_TRUE; 370843e1988Sjohnlev 371843e1988Sjohnlev return (B_TRUE); 372843e1988Sjohnlev } 373843e1988Sjohnlev 374843e1988Sjohnlev static void 37556567907SDavid Edmondson xnbo_close_mac(xnb_t *xnbp) 3760324f02aSDavid Edmondson { 3770324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_FALSE); 3780324f02aSDavid Edmondson } 3790324f02aSDavid Edmondson 3800324f02aSDavid Edmondson static void 3810324f02aSDavid Edmondson i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked) 382843e1988Sjohnlev { 38356567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 38456567907SDavid Edmondson xmca_t *loop; 38556567907SDavid Edmondson 3860324f02aSDavid Edmondson ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock)); 3870324f02aSDavid Edmondson 388843e1988Sjohnlev if (xnbop->o_mh == NULL) 389843e1988Sjohnlev return; 390843e1988Sjohnlev 39156567907SDavid Edmondson if (xnbop->o_running) 392843e1988Sjohnlev xnbop->o_running = B_FALSE; 39356567907SDavid Edmondson 3940324f02aSDavid Edmondson if (!locked) 3950324f02aSDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 39656567907SDavid Edmondson loop = xnbop->o_mca; 39756567907SDavid Edmondson xnbop->o_mca = NULL; 3980324f02aSDavid Edmondson if (!locked) 3990324f02aSDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 40056567907SDavid Edmondson 40156567907SDavid Edmondson while (loop != NULL) { 40256567907SDavid Edmondson xmca_t *next = loop->next; 40356567907SDavid Edmondson 40456567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 40556567907SDavid Edmondson (char *), "close", 40656567907SDavid Edmondson (void *), xnbp, 40756567907SDavid Edmondson (etheraddr_t *), loop->addr); 40856567907SDavid Edmondson (void) mac_multicast_remove(xnbop->o_mch, loop->addr); 40956567907SDavid Edmondson kmem_free(loop, sizeof (*loop)); 41056567907SDavid Edmondson loop = next; 411843e1988Sjohnlev } 412843e1988Sjohnlev 413843e1988Sjohnlev if (xnbop->o_promiscuous) { 41449dbeaeaSDavid Edmondson if (xnbop->o_mphp != NULL) { 4155ecc58b1SGirish Moodalbail mac_promisc_remove(xnbop->o_mphp); 41649dbeaeaSDavid Edmondson xnbop->o_mphp = NULL; 41749dbeaeaSDavid Edmondson } 418843e1988Sjohnlev xnbop->o_promiscuous = B_FALSE; 419da14cebeSEric Cheng } else { 42049dbeaeaSDavid Edmondson if (xnbop->o_mch != NULL) 42149dbeaeaSDavid Edmondson mac_rx_clear(xnbop->o_mch); 422843e1988Sjohnlev } 423843e1988Sjohnlev 424da14cebeSEric Cheng if (xnbop->o_mah != NULL) { 425da14cebeSEric Cheng (void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah); 426da14cebeSEric Cheng xnbop->o_mah = NULL; 427843e1988Sjohnlev } 428843e1988Sjohnlev 429da14cebeSEric Cheng if (xnbop->o_mch != NULL) { 430da14cebeSEric Cheng mac_client_close(xnbop->o_mch, 0); 431da14cebeSEric Cheng xnbop->o_mch = NULL; 432843e1988Sjohnlev } 433843e1988Sjohnlev 434843e1988Sjohnlev mac_close(xnbop->o_mh); 435843e1988Sjohnlev xnbop->o_mh = NULL; 436843e1988Sjohnlev } 437843e1988Sjohnlev 438843e1988Sjohnlev /* 43956567907SDavid Edmondson * Hotplug has completed and we are connected to the peer. We have all 44056567907SDavid Edmondson * the information we need to exchange traffic, so open the MAC device 44156567907SDavid Edmondson * and configure it appropriately. 442843e1988Sjohnlev */ 44356567907SDavid Edmondson static boolean_t 44456567907SDavid Edmondson xnbo_start_connect(xnb_t *xnbp) 445843e1988Sjohnlev { 44656567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 44756567907SDavid Edmondson 44856567907SDavid Edmondson return (xnbo_open_mac(xnbp, xnbop->o_link_name)); 449843e1988Sjohnlev } 450843e1988Sjohnlev 451843e1988Sjohnlev /* 45256567907SDavid Edmondson * The guest has successfully synchronize with this instance. We read 45356567907SDavid Edmondson * the configuration of the guest from xenstore to check whether the 45456567907SDavid Edmondson * guest requests multicast control. If not (the default) we make a 45556567907SDavid Edmondson * note that the MAC device needs to be used in promiscious mode. 45656567907SDavid Edmondson */ 45756567907SDavid Edmondson static boolean_t 45856567907SDavid Edmondson xnbo_peer_connected(xnb_t *xnbp) 45956567907SDavid Edmondson { 46056567907SDavid Edmondson char *oename; 46156567907SDavid Edmondson int request; 46256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 46356567907SDavid Edmondson 46456567907SDavid Edmondson oename = xvdi_get_oename(xnbp->xnb_devinfo); 46556567907SDavid Edmondson 46656567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, oename, 46756567907SDavid Edmondson "request-multicast-control", "%d", &request) != 0) 46856567907SDavid Edmondson request = 0; 46956567907SDavid Edmondson xnbop->o_multicast_control = (request > 0); 47056567907SDavid Edmondson 47156567907SDavid Edmondson return (B_TRUE); 47256567907SDavid Edmondson } 47356567907SDavid Edmondson 47456567907SDavid Edmondson /* 47556567907SDavid Edmondson * The guest domain has closed down the inter-domain connection. We 47656567907SDavid Edmondson * close the underlying MAC device. 477843e1988Sjohnlev */ 478843e1988Sjohnlev static void 47956567907SDavid Edmondson xnbo_peer_disconnected(xnb_t *xnbp) 48056567907SDavid Edmondson { 48156567907SDavid Edmondson xnbo_close_mac(xnbp); 48256567907SDavid Edmondson } 48356567907SDavid Edmondson 48456567907SDavid Edmondson /* 48556567907SDavid Edmondson * The hotplug script has completed. We read information from xenstore 48656567907SDavid Edmondson * about our configuration, most notably the name of the MAC device we 48756567907SDavid Edmondson * should use. 48856567907SDavid Edmondson */ 48956567907SDavid Edmondson static boolean_t 49056567907SDavid Edmondson xnbo_hotplug_connected(xnb_t *xnbp) 491843e1988Sjohnlev { 49256567907SDavid Edmondson char *xsname; 49356567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 49456567907SDavid Edmondson int need; 49556567907SDavid Edmondson 49656567907SDavid Edmondson xsname = xvdi_get_xsname(xnbp->xnb_devinfo); 49756567907SDavid Edmondson 49856567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 49956567907SDavid Edmondson "nic", "%s", xnbop->o_link_name) != 0) { 50056567907SDavid Edmondson cmn_err(CE_WARN, "xnbo_connect: " 50156567907SDavid Edmondson "cannot read nic name from %s", xsname); 50256567907SDavid Edmondson return (B_FALSE); 50356567907SDavid Edmondson } 50456567907SDavid Edmondson 50556567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 50656567907SDavid Edmondson "SUNW-need-rx-filter", "%d", &need) != 0) 50756567907SDavid Edmondson need = 0; 50856567907SDavid Edmondson xnbop->o_need_rx_filter = (need > 0); 50956567907SDavid Edmondson 51056567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 51156567907SDavid Edmondson "SUNW-need-set-physaddr", "%d", &need) != 0) 51256567907SDavid Edmondson need = 0; 51356567907SDavid Edmondson xnbop->o_need_setphysaddr = (need > 0); 51456567907SDavid Edmondson 51556567907SDavid Edmondson return (B_TRUE); 51656567907SDavid Edmondson } 51756567907SDavid Edmondson 51856567907SDavid Edmondson /* 51956567907SDavid Edmondson * Find the multicast address `addr', return B_TRUE if it is one that 52056567907SDavid Edmondson * we receive. If `remove', remove it from the set received. 52156567907SDavid Edmondson */ 52256567907SDavid Edmondson static boolean_t 52356567907SDavid Edmondson xnbo_mcast_find(xnb_t *xnbp, ether_addr_t *addr, boolean_t remove) 52456567907SDavid Edmondson { 52556567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 52656567907SDavid Edmondson xmca_t *prev, *del, *this; 52756567907SDavid Edmondson 52856567907SDavid Edmondson ASSERT(MUTEX_HELD(&xnbp->xnb_state_lock)); 52956567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 53056567907SDavid Edmondson 53156567907SDavid Edmondson prev = del = NULL; 53256567907SDavid Edmondson 53356567907SDavid Edmondson this = xnbop->o_mca; 53456567907SDavid Edmondson 53556567907SDavid Edmondson while (this != NULL) { 53656567907SDavid Edmondson if (bcmp(&this->addr, addr, sizeof (this->addr)) == 0) { 53756567907SDavid Edmondson del = this; 53856567907SDavid Edmondson if (remove) { 53956567907SDavid Edmondson if (prev == NULL) 54056567907SDavid Edmondson xnbop->o_mca = this->next; 54156567907SDavid Edmondson else 54256567907SDavid Edmondson prev->next = this->next; 54356567907SDavid Edmondson } 54456567907SDavid Edmondson break; 54556567907SDavid Edmondson } 54656567907SDavid Edmondson 54756567907SDavid Edmondson prev = this; 54856567907SDavid Edmondson this = this->next; 54956567907SDavid Edmondson } 55056567907SDavid Edmondson 55156567907SDavid Edmondson if (del == NULL) 55256567907SDavid Edmondson return (B_FALSE); 55356567907SDavid Edmondson 55456567907SDavid Edmondson if (remove) { 55556567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 55656567907SDavid Edmondson (char *), "remove", 55756567907SDavid Edmondson (void *), xnbp, 55856567907SDavid Edmondson (etheraddr_t *), del->addr); 55956567907SDavid Edmondson mac_multicast_remove(xnbop->o_mch, del->addr); 56056567907SDavid Edmondson kmem_free(del, sizeof (*del)); 56156567907SDavid Edmondson } 56256567907SDavid Edmondson 56356567907SDavid Edmondson return (B_TRUE); 56456567907SDavid Edmondson } 56556567907SDavid Edmondson 56656567907SDavid Edmondson /* 56756567907SDavid Edmondson * Add the multicast address `addr' to the set received. 56856567907SDavid Edmondson */ 56956567907SDavid Edmondson static boolean_t 57056567907SDavid Edmondson xnbo_mcast_add(xnb_t *xnbp, ether_addr_t *addr) 57156567907SDavid Edmondson { 57256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 57356567907SDavid Edmondson boolean_t r = B_FALSE; 57456567907SDavid Edmondson 57556567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 57656567907SDavid Edmondson 57756567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 57856567907SDavid Edmondson 57956567907SDavid Edmondson if (xnbo_mcast_find(xnbp, addr, B_FALSE)) { 58056567907SDavid Edmondson r = B_TRUE; 58156567907SDavid Edmondson } else if (mac_multicast_add(xnbop->o_mch, 58256567907SDavid Edmondson (const uint8_t *)addr) == 0) { 58356567907SDavid Edmondson xmca_t *mca; 58456567907SDavid Edmondson 58556567907SDavid Edmondson DTRACE_PROBE3(mcast_add, 58656567907SDavid Edmondson (char *), "add", 58756567907SDavid Edmondson (void *), xnbp, 58856567907SDavid Edmondson (etheraddr_t *), addr); 58956567907SDavid Edmondson 59056567907SDavid Edmondson mca = kmem_alloc(sizeof (*mca), KM_SLEEP); 59156567907SDavid Edmondson bcopy(addr, &mca->addr, sizeof (mca->addr)); 59256567907SDavid Edmondson 59356567907SDavid Edmondson mca->next = xnbop->o_mca; 59456567907SDavid Edmondson xnbop->o_mca = mca; 59556567907SDavid Edmondson 59656567907SDavid Edmondson r = B_TRUE; 59756567907SDavid Edmondson } 59856567907SDavid Edmondson 59956567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 60056567907SDavid Edmondson 60156567907SDavid Edmondson return (r); 60256567907SDavid Edmondson } 60356567907SDavid Edmondson 60456567907SDavid Edmondson /* 60556567907SDavid Edmondson * Remove the multicast address `addr' from the set received. 60656567907SDavid Edmondson */ 60756567907SDavid Edmondson static boolean_t 60856567907SDavid Edmondson xnbo_mcast_del(xnb_t *xnbp, ether_addr_t *addr) 60956567907SDavid Edmondson { 61056567907SDavid Edmondson boolean_t r; 61156567907SDavid Edmondson 61256567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 61356567907SDavid Edmondson r = xnbo_mcast_find(xnbp, addr, B_TRUE); 61456567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 61556567907SDavid Edmondson 61656567907SDavid Edmondson return (r); 617843e1988Sjohnlev } 618843e1988Sjohnlev 619843e1988Sjohnlev static int 620843e1988Sjohnlev xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 621843e1988Sjohnlev { 622843e1988Sjohnlev static xnb_flavour_t flavour = { 62356567907SDavid Edmondson xnbo_to_mac, xnbo_peer_connected, xnbo_peer_disconnected, 62456567907SDavid Edmondson xnbo_hotplug_connected, xnbo_start_connect, 625843e1988Sjohnlev xnbo_cksum_from_peer, xnbo_cksum_to_peer, 62656567907SDavid Edmondson xnbo_mcast_add, xnbo_mcast_del, 627843e1988Sjohnlev }; 628843e1988Sjohnlev xnbo_t *xnbop; 629843e1988Sjohnlev 630843e1988Sjohnlev switch (cmd) { 631843e1988Sjohnlev case DDI_ATTACH: 632843e1988Sjohnlev break; 633843e1988Sjohnlev case DDI_RESUME: 634843e1988Sjohnlev return (DDI_SUCCESS); 635843e1988Sjohnlev default: 636843e1988Sjohnlev return (DDI_FAILURE); 637843e1988Sjohnlev } 638843e1988Sjohnlev 639843e1988Sjohnlev xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP); 640843e1988Sjohnlev 641843e1988Sjohnlev if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) { 642843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 643843e1988Sjohnlev return (DDI_FAILURE); 644843e1988Sjohnlev } 645843e1988Sjohnlev 646843e1988Sjohnlev return (DDI_SUCCESS); 647843e1988Sjohnlev } 648843e1988Sjohnlev 649843e1988Sjohnlev static int 650843e1988Sjohnlev xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 651843e1988Sjohnlev { 652843e1988Sjohnlev xnb_t *xnbp = ddi_get_driver_private(dip); 653551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 654843e1988Sjohnlev 655843e1988Sjohnlev switch (cmd) { 656843e1988Sjohnlev case DDI_DETACH: 657843e1988Sjohnlev break; 658843e1988Sjohnlev case DDI_SUSPEND: 659843e1988Sjohnlev return (DDI_SUCCESS); 660843e1988Sjohnlev default: 661843e1988Sjohnlev return (DDI_FAILURE); 662843e1988Sjohnlev } 663843e1988Sjohnlev 664551bc2a6Smrj mutex_enter(&xnbp->xnb_tx_lock); 665551bc2a6Smrj mutex_enter(&xnbp->xnb_rx_lock); 666843e1988Sjohnlev 667551bc2a6Smrj if (!xnbp->xnb_detachable || xnbp->xnb_connected || 668024c26efSMax zhen (xnbp->xnb_tx_buf_count > 0)) { 669551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 670551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 671843e1988Sjohnlev 672843e1988Sjohnlev return (DDI_FAILURE); 673843e1988Sjohnlev } 674843e1988Sjohnlev 675551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 676551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 677843e1988Sjohnlev 67856567907SDavid Edmondson xnbo_close_mac(xnbp); 679843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 680843e1988Sjohnlev 681843e1988Sjohnlev xnb_detach(dip); 682843e1988Sjohnlev 683843e1988Sjohnlev return (DDI_SUCCESS); 684843e1988Sjohnlev } 685843e1988Sjohnlev 686843e1988Sjohnlev static struct cb_ops cb_ops = { 687843e1988Sjohnlev nulldev, /* open */ 688843e1988Sjohnlev nulldev, /* close */ 689843e1988Sjohnlev nodev, /* strategy */ 690843e1988Sjohnlev nodev, /* print */ 691843e1988Sjohnlev nodev, /* dump */ 692843e1988Sjohnlev nodev, /* read */ 693843e1988Sjohnlev nodev, /* write */ 694843e1988Sjohnlev nodev, /* ioctl */ 695843e1988Sjohnlev nodev, /* devmap */ 696843e1988Sjohnlev nodev, /* mmap */ 697843e1988Sjohnlev nodev, /* segmap */ 698843e1988Sjohnlev nochpoll, /* poll */ 699843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */ 700843e1988Sjohnlev 0, /* streamtab */ 701843e1988Sjohnlev D_NEW | D_MP | D_64BIT /* Driver compatibility flag */ 702843e1988Sjohnlev }; 703843e1988Sjohnlev 704843e1988Sjohnlev static struct dev_ops ops = { 705843e1988Sjohnlev DEVO_REV, /* devo_rev */ 706843e1988Sjohnlev 0, /* devo_refcnt */ 707843e1988Sjohnlev nulldev, /* devo_getinfo */ 708843e1988Sjohnlev nulldev, /* devo_identify */ 709843e1988Sjohnlev nulldev, /* devo_probe */ 710843e1988Sjohnlev xnbo_attach, /* devo_attach */ 711843e1988Sjohnlev xnbo_detach, /* devo_detach */ 712843e1988Sjohnlev nodev, /* devo_reset */ 713843e1988Sjohnlev &cb_ops, /* devo_cb_ops */ 714843e1988Sjohnlev (struct bus_ops *)0, /* devo_bus_ops */ 71519397407SSherry Moore NULL, /* devo_power */ 71619397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 717843e1988Sjohnlev }; 718843e1988Sjohnlev 719843e1988Sjohnlev static struct modldrv modldrv = { 720024c26efSMax zhen &mod_driverops, "xnbo driver", &ops, 721843e1988Sjohnlev }; 722843e1988Sjohnlev 723843e1988Sjohnlev static struct modlinkage modlinkage = { 724843e1988Sjohnlev MODREV_1, &modldrv, NULL 725843e1988Sjohnlev }; 726843e1988Sjohnlev 727843e1988Sjohnlev int 728843e1988Sjohnlev _init(void) 729843e1988Sjohnlev { 730843e1988Sjohnlev return (mod_install(&modlinkage)); 731843e1988Sjohnlev } 732843e1988Sjohnlev 733843e1988Sjohnlev int 734843e1988Sjohnlev _info(struct modinfo *modinfop) 735843e1988Sjohnlev { 736843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 737843e1988Sjohnlev } 738843e1988Sjohnlev 739843e1988Sjohnlev int 740843e1988Sjohnlev _fini(void) 741843e1988Sjohnlev { 742843e1988Sjohnlev return (mod_remove(&modlinkage)); 743843e1988Sjohnlev } 744