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 /* 2349dbeaeaSDavid Edmondson * Copyright 2009 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 34843e1988Sjohnlev #include "xnb.h" 35843e1988Sjohnlev 36843e1988Sjohnlev #include <sys/sunddi.h> 37da14cebeSEric Cheng #include <sys/ddi.h> 38843e1988Sjohnlev #include <sys/modctl.h> 39843e1988Sjohnlev #include <sys/strsubr.h> 40da14cebeSEric Cheng #include <sys/mac_client.h> 41da14cebeSEric Cheng #include <sys/mac_provider.h> 42da14cebeSEric Cheng #include <sys/mac_client_priv.h> 43843e1988Sjohnlev #include <sys/mac.h> 44843e1988Sjohnlev #include <net/if.h> 45843e1988Sjohnlev #include <sys/dlpi.h> 46843e1988Sjohnlev #include <sys/pattr.h> 47843e1988Sjohnlev #include <xen/sys/xenbus_impl.h> 48843e1988Sjohnlev #include <xen/sys/xendev.h> 4956567907SDavid Edmondson #include <sys/sdt.h> 5056567907SDavid Edmondson #include <sys/note.h> 51843e1988Sjohnlev 5256567907SDavid Edmondson /* Track multicast addresses. */ 5356567907SDavid Edmondson typedef struct xmca { 5456567907SDavid Edmondson struct xmca *next; 5556567907SDavid Edmondson ether_addr_t addr; 5656567907SDavid Edmondson } xmca_t; 5756567907SDavid Edmondson 5856567907SDavid Edmondson /* State about this device instance. */ 59843e1988Sjohnlev typedef struct xnbo { 60843e1988Sjohnlev mac_handle_t o_mh; 61da14cebeSEric Cheng mac_client_handle_t o_mch; 62da14cebeSEric Cheng mac_unicast_handle_t o_mah; 63da14cebeSEric Cheng mac_promisc_handle_t o_mphp; 64843e1988Sjohnlev boolean_t o_running; 65843e1988Sjohnlev boolean_t o_promiscuous; 66843e1988Sjohnlev uint32_t o_hcksum_capab; 6756567907SDavid Edmondson xmca_t *o_mca; 6856567907SDavid Edmondson char o_link_name[LIFNAMSIZ]; 6956567907SDavid Edmondson boolean_t o_need_rx_filter; 7056567907SDavid Edmondson boolean_t o_need_setphysaddr; 7156567907SDavid Edmondson boolean_t o_multicast_control; 72843e1988Sjohnlev } xnbo_t; 73843e1988Sjohnlev 7456567907SDavid Edmondson static void xnbo_close_mac(xnb_t *); 75*0324f02aSDavid Edmondson static void i_xnbo_close_mac(xnb_t *, boolean_t); 76843e1988Sjohnlev 77843e1988Sjohnlev /* 78843e1988Sjohnlev * Packets from the peer come here. We pass them to the mac device. 79843e1988Sjohnlev */ 80843e1988Sjohnlev static void 81843e1988Sjohnlev xnbo_to_mac(xnb_t *xnbp, mblk_t *mp) 82843e1988Sjohnlev { 83551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 84843e1988Sjohnlev 85843e1988Sjohnlev ASSERT(mp != NULL); 86843e1988Sjohnlev 87843e1988Sjohnlev if (!xnbop->o_running) { 88024c26efSMax zhen xnbp->xnb_stat_tx_too_early++; 89843e1988Sjohnlev goto fail; 90843e1988Sjohnlev } 91843e1988Sjohnlev 92da14cebeSEric Cheng if (mac_tx(xnbop->o_mch, mp, 0, 93da14cebeSEric Cheng MAC_DROP_ON_NO_DESC, NULL) != NULL) { 94551bc2a6Smrj xnbp->xnb_stat_mac_full++; 95843e1988Sjohnlev } 96843e1988Sjohnlev 97843e1988Sjohnlev return; 98843e1988Sjohnlev 99843e1988Sjohnlev fail: 100843e1988Sjohnlev freemsgchain(mp); 101843e1988Sjohnlev } 102843e1988Sjohnlev 10356567907SDavid Edmondson /* 10456567907SDavid Edmondson * Process the checksum flags `flags' provided by the peer for the 10556567907SDavid Edmondson * packet `mp'. 10656567907SDavid Edmondson */ 107843e1988Sjohnlev static mblk_t * 108843e1988Sjohnlev xnbo_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags) 109843e1988Sjohnlev { 110551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 111843e1988Sjohnlev 112843e1988Sjohnlev ASSERT(mp->b_next == NULL); 113843e1988Sjohnlev 114843e1988Sjohnlev if ((flags & NETTXF_csum_blank) != 0) { 115843e1988Sjohnlev /* 116843e1988Sjohnlev * The checksum in the packet is blank. Determine 117843e1988Sjohnlev * whether we can do hardware offload and, if so, 118843e1988Sjohnlev * update the flags on the mblk according. If not, 119843e1988Sjohnlev * calculate and insert the checksum using software. 120843e1988Sjohnlev */ 121843e1988Sjohnlev mp = xnb_process_cksum_flags(xnbp, mp, 122843e1988Sjohnlev xnbop->o_hcksum_capab); 123843e1988Sjohnlev } 124843e1988Sjohnlev 125843e1988Sjohnlev return (mp); 126843e1988Sjohnlev } 127843e1988Sjohnlev 12856567907SDavid Edmondson /* 12956567907SDavid Edmondson * Calculate the checksum flags to be relayed to the peer for the 13056567907SDavid Edmondson * packet `mp'. 13156567907SDavid Edmondson */ 132843e1988Sjohnlev static uint16_t 133843e1988Sjohnlev xnbo_cksum_to_peer(xnb_t *xnbp, mblk_t *mp) 134843e1988Sjohnlev { 13556567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp)); 136843e1988Sjohnlev uint16_t r = 0; 13756567907SDavid Edmondson uint32_t pflags, csum; 138843e1988Sjohnlev 139843e1988Sjohnlev /* 140843e1988Sjohnlev * We might also check for HCK_PARTIALCKSUM here and, 141843e1988Sjohnlev * providing that the partial checksum covers the TCP/UDP 142843e1988Sjohnlev * payload, return NETRXF_data_validated. 143843e1988Sjohnlev * 144843e1988Sjohnlev * It seems that it's probably not worthwhile, as even MAC 145843e1988Sjohnlev * devices which advertise HCKSUM_INET_PARTIAL in their 146843e1988Sjohnlev * capabilities tend to use HCK_FULLCKSUM on the receive side 147843e1988Sjohnlev * - they are actually saying that in the output path the 148843e1988Sjohnlev * caller must use HCK_PARTIALCKSUM. 14956567907SDavid Edmondson * 15056567907SDavid Edmondson * Then again, if a NIC supports HCK_PARTIALCKSUM in its' 15156567907SDavid Edmondson * output path, the host IP stack will use it. If such packets 15256567907SDavid Edmondson * are destined for the peer (i.e. looped around) we would 15356567907SDavid Edmondson * gain some advantage. 154843e1988Sjohnlev */ 155843e1988Sjohnlev 15656567907SDavid Edmondson hcksum_retrieve(mp, NULL, NULL, NULL, NULL, 15756567907SDavid Edmondson NULL, &csum, &pflags); 158843e1988Sjohnlev 15956567907SDavid Edmondson /* 16056567907SDavid Edmondson * If the MAC driver has asserted that the checksum is 16156567907SDavid Edmondson * good, let the peer know. 16256567907SDavid Edmondson */ 16356567907SDavid Edmondson if (((pflags & HCK_FULLCKSUM) != 0) && 16456567907SDavid Edmondson (((pflags & HCK_FULLCKSUM_OK) != 0) || 16556567907SDavid Edmondson (csum == 0xffff))) 16656567907SDavid Edmondson r |= NETRXF_data_validated; 167843e1988Sjohnlev 168843e1988Sjohnlev return (r); 169843e1988Sjohnlev } 170843e1988Sjohnlev 171843e1988Sjohnlev /* 172843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer. 173843e1988Sjohnlev */ 174843e1988Sjohnlev /*ARGSUSED*/ 175843e1988Sjohnlev static void 176da14cebeSEric Cheng xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 177da14cebeSEric Cheng boolean_t loopback) 178843e1988Sjohnlev { 179843e1988Sjohnlev xnb_t *xnbp = arg; 180843e1988Sjohnlev 181551bc2a6Smrj mp = xnb_copy_to_peer(xnbp, mp); 182843e1988Sjohnlev 183843e1988Sjohnlev if (mp != NULL) 184843e1988Sjohnlev freemsgchain(mp); 185843e1988Sjohnlev } 186843e1988Sjohnlev 187843e1988Sjohnlev /* 188843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer if 189843e1988Sjohnlev * the destination mac address matches or it's a multicast/broadcast 190843e1988Sjohnlev * address. 191843e1988Sjohnlev */ 192843e1988Sjohnlev static void 193da14cebeSEric Cheng xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 194da14cebeSEric Cheng boolean_t loopback) 195843e1988Sjohnlev { 19656567907SDavid Edmondson _NOTE(ARGUNUSED(loopback)); 197843e1988Sjohnlev xnb_t *xnbp = arg; 198551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 199843e1988Sjohnlev mblk_t *next, *keep, *keep_head, *free, *free_head; 200843e1988Sjohnlev 201843e1988Sjohnlev keep = keep_head = free = free_head = NULL; 202843e1988Sjohnlev 203843e1988Sjohnlev #define ADD(list, bp) \ 204843e1988Sjohnlev if (list != NULL) \ 205843e1988Sjohnlev list->b_next = bp; \ 206843e1988Sjohnlev else \ 207843e1988Sjohnlev list##_head = bp; \ 208843e1988Sjohnlev list = bp; 209843e1988Sjohnlev 210843e1988Sjohnlev for (; mp != NULL; mp = next) { 211843e1988Sjohnlev mac_header_info_t hdr_info; 212843e1988Sjohnlev 213843e1988Sjohnlev next = mp->b_next; 214843e1988Sjohnlev mp->b_next = NULL; 215843e1988Sjohnlev 216843e1988Sjohnlev if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) { 217843e1988Sjohnlev ADD(free, mp); 218843e1988Sjohnlev continue; 219843e1988Sjohnlev } 220843e1988Sjohnlev 221843e1988Sjohnlev if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || 222843e1988Sjohnlev (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) { 223843e1988Sjohnlev ADD(keep, mp); 224843e1988Sjohnlev continue; 225843e1988Sjohnlev } 226843e1988Sjohnlev 227551bc2a6Smrj if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr, 228551bc2a6Smrj sizeof (xnbp->xnb_mac_addr)) == 0) { 229843e1988Sjohnlev ADD(keep, mp); 230843e1988Sjohnlev continue; 231843e1988Sjohnlev } 232843e1988Sjohnlev 233843e1988Sjohnlev ADD(free, mp); 234843e1988Sjohnlev } 235843e1988Sjohnlev #undef ADD 236843e1988Sjohnlev 237843e1988Sjohnlev if (keep_head != NULL) 238da14cebeSEric Cheng xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE); 239843e1988Sjohnlev 240843e1988Sjohnlev if (free_head != NULL) 241843e1988Sjohnlev freemsgchain(free_head); 242843e1988Sjohnlev } 243843e1988Sjohnlev 244843e1988Sjohnlev static boolean_t 245843e1988Sjohnlev xnbo_open_mac(xnb_t *xnbp, char *mac) 246843e1988Sjohnlev { 247551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 24856567907SDavid Edmondson int err; 249843e1988Sjohnlev const mac_info_t *mi; 250da14cebeSEric Cheng void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t); 251da14cebeSEric Cheng struct ether_addr ea; 252e7801d59Ssowmini uint_t max_sdu; 253da14cebeSEric Cheng mac_diag_t diag; 254843e1988Sjohnlev 255d62bc4baSyz if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) { 256843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 257d62bc4baSyz "cannot open mac for link %s (%d)", mac, err); 258843e1988Sjohnlev return (B_FALSE); 259843e1988Sjohnlev } 260843e1988Sjohnlev ASSERT(xnbop->o_mh != NULL); 261843e1988Sjohnlev 262843e1988Sjohnlev mi = mac_info(xnbop->o_mh); 263843e1988Sjohnlev ASSERT(mi != NULL); 264843e1988Sjohnlev 265843e1988Sjohnlev if (mi->mi_media != DL_ETHER) { 266843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 267d62bc4baSyz "device is not DL_ETHER (%d)", mi->mi_media); 268*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 269843e1988Sjohnlev return (B_FALSE); 270843e1988Sjohnlev } 271843e1988Sjohnlev if (mi->mi_media != mi->mi_nativemedia) { 272843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 273d62bc4baSyz "device media and native media mismatch (%d != %d)", 274843e1988Sjohnlev mi->mi_media, mi->mi_nativemedia); 275*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 276843e1988Sjohnlev return (B_FALSE); 277843e1988Sjohnlev } 278e7801d59Ssowmini 279e7801d59Ssowmini mac_sdu_get(xnbop->o_mh, NULL, &max_sdu); 280e7801d59Ssowmini if (max_sdu > XNBMAXPKT) { 281e7801d59Ssowmini cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)", 282e7801d59Ssowmini max_sdu); 283*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 284843e1988Sjohnlev return (B_FALSE); 285843e1988Sjohnlev } 286843e1988Sjohnlev 287fc4e975dSVenugopal Iyer /* 288fc4e975dSVenugopal Iyer * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a 289fc4e975dSVenugopal Iyer * guest on the localhost itself. In this case we would have the MAC 290fc4e975dSVenugopal Iyer * client open for the guest being migrated *and* also for the 291fc4e975dSVenugopal Iyer * migrated guest (i.e. the former will be active till the migration 292fc4e975dSVenugopal Iyer * is complete when the latter will be activated). This flag states 293fc4e975dSVenugopal Iyer * that it is OK for mac_unicast_add to add the primary MAC unicast 294fc4e975dSVenugopal Iyer * address multiple times. 295fc4e975dSVenugopal Iyer */ 296da14cebeSEric Cheng if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL, 297fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_USE_DATALINK_NAME | 298fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) { 299da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 300da14cebeSEric Cheng "error (%d) opening mac client", err); 301*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 302da14cebeSEric Cheng return (B_FALSE); 303da14cebeSEric Cheng } 304da14cebeSEric Cheng 30556567907SDavid Edmondson if (xnbop->o_need_rx_filter) 306843e1988Sjohnlev rx_fn = xnbo_from_mac_filter; 307843e1988Sjohnlev else 308843e1988Sjohnlev rx_fn = xnbo_from_mac; 309843e1988Sjohnlev 310fc4e975dSVenugopal Iyer err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY, 31156567907SDavid Edmondson &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL, 31256567907SDavid Edmondson xnbp); 313fc4e975dSVenugopal Iyer if (err != 0) { 314fc4e975dSVenugopal Iyer cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary " 315fc4e975dSVenugopal Iyer "MAC address of %s: %d", mac, err); 316*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 317fc4e975dSVenugopal Iyer return (B_FALSE); 318fc4e975dSVenugopal Iyer } 31956567907SDavid Edmondson if (!xnbop->o_multicast_control) { 320da14cebeSEric Cheng err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL, 321ae6aa22aSVenugopal Iyer rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP | 322ae6aa22aSVenugopal Iyer MAC_PROMISC_FLAGS_VLAN_TAG_STRIP); 323da14cebeSEric Cheng if (err != 0) { 324da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 325da14cebeSEric Cheng "cannot enable promiscuous mode of %s: %d", 326da14cebeSEric Cheng mac, err); 327*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 328da14cebeSEric Cheng return (B_FALSE); 329da14cebeSEric Cheng } 330da14cebeSEric Cheng xnbop->o_promiscuous = B_TRUE; 331da14cebeSEric Cheng } 332843e1988Sjohnlev 33356567907SDavid Edmondson if (xnbop->o_need_setphysaddr) { 334da14cebeSEric Cheng err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr); 335843e1988Sjohnlev /* Warn, but continue on. */ 336843e1988Sjohnlev if (err != 0) { 337551bc2a6Smrj bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet, 338843e1988Sjohnlev ETHERADDRL); 339843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 340843e1988Sjohnlev "cannot set MAC address of %s to " 341da14cebeSEric Cheng "%s: %d", mac, ether_sprintf(&ea), err); 342843e1988Sjohnlev } 343843e1988Sjohnlev } 344843e1988Sjohnlev 34556567907SDavid Edmondson if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, 34656567907SDavid Edmondson &xnbop->o_hcksum_capab)) 34756567907SDavid Edmondson xnbop->o_hcksum_capab = 0; 34856567907SDavid Edmondson 349843e1988Sjohnlev xnbop->o_running = B_TRUE; 350843e1988Sjohnlev 351843e1988Sjohnlev return (B_TRUE); 352843e1988Sjohnlev } 353843e1988Sjohnlev 354843e1988Sjohnlev static void 35556567907SDavid Edmondson xnbo_close_mac(xnb_t *xnbp) 356*0324f02aSDavid Edmondson { 357*0324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_FALSE); 358*0324f02aSDavid Edmondson } 359*0324f02aSDavid Edmondson 360*0324f02aSDavid Edmondson static void 361*0324f02aSDavid Edmondson i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked) 362843e1988Sjohnlev { 36356567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 36456567907SDavid Edmondson xmca_t *loop; 36556567907SDavid Edmondson 366*0324f02aSDavid Edmondson ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock)); 367*0324f02aSDavid Edmondson 368843e1988Sjohnlev if (xnbop->o_mh == NULL) 369843e1988Sjohnlev return; 370843e1988Sjohnlev 37156567907SDavid Edmondson if (xnbop->o_running) 372843e1988Sjohnlev xnbop->o_running = B_FALSE; 37356567907SDavid Edmondson 374*0324f02aSDavid Edmondson if (!locked) 375*0324f02aSDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 37656567907SDavid Edmondson loop = xnbop->o_mca; 37756567907SDavid Edmondson xnbop->o_mca = NULL; 378*0324f02aSDavid Edmondson if (!locked) 379*0324f02aSDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 38056567907SDavid Edmondson 38156567907SDavid Edmondson while (loop != NULL) { 38256567907SDavid Edmondson xmca_t *next = loop->next; 38356567907SDavid Edmondson 38456567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 38556567907SDavid Edmondson (char *), "close", 38656567907SDavid Edmondson (void *), xnbp, 38756567907SDavid Edmondson (etheraddr_t *), loop->addr); 38856567907SDavid Edmondson (void) mac_multicast_remove(xnbop->o_mch, loop->addr); 38956567907SDavid Edmondson kmem_free(loop, sizeof (*loop)); 39056567907SDavid Edmondson loop = next; 391843e1988Sjohnlev } 392843e1988Sjohnlev 393843e1988Sjohnlev if (xnbop->o_promiscuous) { 39449dbeaeaSDavid Edmondson if (xnbop->o_mphp != NULL) { 3955ecc58b1SGirish Moodalbail mac_promisc_remove(xnbop->o_mphp); 39649dbeaeaSDavid Edmondson xnbop->o_mphp = NULL; 39749dbeaeaSDavid Edmondson } 398843e1988Sjohnlev xnbop->o_promiscuous = B_FALSE; 399da14cebeSEric Cheng } else { 40049dbeaeaSDavid Edmondson if (xnbop->o_mch != NULL) 40149dbeaeaSDavid Edmondson mac_rx_clear(xnbop->o_mch); 402843e1988Sjohnlev } 403843e1988Sjohnlev 404da14cebeSEric Cheng if (xnbop->o_mah != NULL) { 405da14cebeSEric Cheng (void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah); 406da14cebeSEric Cheng xnbop->o_mah = NULL; 407843e1988Sjohnlev } 408843e1988Sjohnlev 409da14cebeSEric Cheng if (xnbop->o_mch != NULL) { 410da14cebeSEric Cheng mac_client_close(xnbop->o_mch, 0); 411da14cebeSEric Cheng xnbop->o_mch = NULL; 412843e1988Sjohnlev } 413843e1988Sjohnlev 414843e1988Sjohnlev mac_close(xnbop->o_mh); 415843e1988Sjohnlev xnbop->o_mh = NULL; 416843e1988Sjohnlev } 417843e1988Sjohnlev 418843e1988Sjohnlev /* 41956567907SDavid Edmondson * Hotplug has completed and we are connected to the peer. We have all 42056567907SDavid Edmondson * the information we need to exchange traffic, so open the MAC device 42156567907SDavid Edmondson * and configure it appropriately. 422843e1988Sjohnlev */ 42356567907SDavid Edmondson static boolean_t 42456567907SDavid Edmondson xnbo_start_connect(xnb_t *xnbp) 425843e1988Sjohnlev { 42656567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 42756567907SDavid Edmondson 42856567907SDavid Edmondson return (xnbo_open_mac(xnbp, xnbop->o_link_name)); 429843e1988Sjohnlev } 430843e1988Sjohnlev 431843e1988Sjohnlev /* 43256567907SDavid Edmondson * The guest has successfully synchronize with this instance. We read 43356567907SDavid Edmondson * the configuration of the guest from xenstore to check whether the 43456567907SDavid Edmondson * guest requests multicast control. If not (the default) we make a 43556567907SDavid Edmondson * note that the MAC device needs to be used in promiscious mode. 43656567907SDavid Edmondson */ 43756567907SDavid Edmondson static boolean_t 43856567907SDavid Edmondson xnbo_peer_connected(xnb_t *xnbp) 43956567907SDavid Edmondson { 44056567907SDavid Edmondson char *oename; 44156567907SDavid Edmondson int request; 44256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 44356567907SDavid Edmondson 44456567907SDavid Edmondson oename = xvdi_get_oename(xnbp->xnb_devinfo); 44556567907SDavid Edmondson 44656567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, oename, 44756567907SDavid Edmondson "request-multicast-control", "%d", &request) != 0) 44856567907SDavid Edmondson request = 0; 44956567907SDavid Edmondson xnbop->o_multicast_control = (request > 0); 45056567907SDavid Edmondson 45156567907SDavid Edmondson return (B_TRUE); 45256567907SDavid Edmondson } 45356567907SDavid Edmondson 45456567907SDavid Edmondson /* 45556567907SDavid Edmondson * The guest domain has closed down the inter-domain connection. We 45656567907SDavid Edmondson * close the underlying MAC device. 457843e1988Sjohnlev */ 458843e1988Sjohnlev static void 45956567907SDavid Edmondson xnbo_peer_disconnected(xnb_t *xnbp) 46056567907SDavid Edmondson { 46156567907SDavid Edmondson xnbo_close_mac(xnbp); 46256567907SDavid Edmondson } 46356567907SDavid Edmondson 46456567907SDavid Edmondson /* 46556567907SDavid Edmondson * The hotplug script has completed. We read information from xenstore 46656567907SDavid Edmondson * about our configuration, most notably the name of the MAC device we 46756567907SDavid Edmondson * should use. 46856567907SDavid Edmondson */ 46956567907SDavid Edmondson static boolean_t 47056567907SDavid Edmondson xnbo_hotplug_connected(xnb_t *xnbp) 471843e1988Sjohnlev { 47256567907SDavid Edmondson char *xsname; 47356567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 47456567907SDavid Edmondson int need; 47556567907SDavid Edmondson 47656567907SDavid Edmondson xsname = xvdi_get_xsname(xnbp->xnb_devinfo); 47756567907SDavid Edmondson 47856567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 47956567907SDavid Edmondson "nic", "%s", xnbop->o_link_name) != 0) { 48056567907SDavid Edmondson cmn_err(CE_WARN, "xnbo_connect: " 48156567907SDavid Edmondson "cannot read nic name from %s", xsname); 48256567907SDavid Edmondson return (B_FALSE); 48356567907SDavid Edmondson } 48456567907SDavid Edmondson 48556567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 48656567907SDavid Edmondson "SUNW-need-rx-filter", "%d", &need) != 0) 48756567907SDavid Edmondson need = 0; 48856567907SDavid Edmondson xnbop->o_need_rx_filter = (need > 0); 48956567907SDavid Edmondson 49056567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 49156567907SDavid Edmondson "SUNW-need-set-physaddr", "%d", &need) != 0) 49256567907SDavid Edmondson need = 0; 49356567907SDavid Edmondson xnbop->o_need_setphysaddr = (need > 0); 49456567907SDavid Edmondson 49556567907SDavid Edmondson return (B_TRUE); 49656567907SDavid Edmondson } 49756567907SDavid Edmondson 49856567907SDavid Edmondson /* 49956567907SDavid Edmondson * Find the multicast address `addr', return B_TRUE if it is one that 50056567907SDavid Edmondson * we receive. If `remove', remove it from the set received. 50156567907SDavid Edmondson */ 50256567907SDavid Edmondson static boolean_t 50356567907SDavid Edmondson xnbo_mcast_find(xnb_t *xnbp, ether_addr_t *addr, boolean_t remove) 50456567907SDavid Edmondson { 50556567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 50656567907SDavid Edmondson xmca_t *prev, *del, *this; 50756567907SDavid Edmondson 50856567907SDavid Edmondson ASSERT(MUTEX_HELD(&xnbp->xnb_state_lock)); 50956567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 51056567907SDavid Edmondson 51156567907SDavid Edmondson prev = del = NULL; 51256567907SDavid Edmondson 51356567907SDavid Edmondson this = xnbop->o_mca; 51456567907SDavid Edmondson 51556567907SDavid Edmondson while (this != NULL) { 51656567907SDavid Edmondson if (bcmp(&this->addr, addr, sizeof (this->addr)) == 0) { 51756567907SDavid Edmondson del = this; 51856567907SDavid Edmondson if (remove) { 51956567907SDavid Edmondson if (prev == NULL) 52056567907SDavid Edmondson xnbop->o_mca = this->next; 52156567907SDavid Edmondson else 52256567907SDavid Edmondson prev->next = this->next; 52356567907SDavid Edmondson } 52456567907SDavid Edmondson break; 52556567907SDavid Edmondson } 52656567907SDavid Edmondson 52756567907SDavid Edmondson prev = this; 52856567907SDavid Edmondson this = this->next; 52956567907SDavid Edmondson } 53056567907SDavid Edmondson 53156567907SDavid Edmondson if (del == NULL) 53256567907SDavid Edmondson return (B_FALSE); 53356567907SDavid Edmondson 53456567907SDavid Edmondson if (remove) { 53556567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 53656567907SDavid Edmondson (char *), "remove", 53756567907SDavid Edmondson (void *), xnbp, 53856567907SDavid Edmondson (etheraddr_t *), del->addr); 53956567907SDavid Edmondson mac_multicast_remove(xnbop->o_mch, del->addr); 54056567907SDavid Edmondson kmem_free(del, sizeof (*del)); 54156567907SDavid Edmondson } 54256567907SDavid Edmondson 54356567907SDavid Edmondson return (B_TRUE); 54456567907SDavid Edmondson } 54556567907SDavid Edmondson 54656567907SDavid Edmondson /* 54756567907SDavid Edmondson * Add the multicast address `addr' to the set received. 54856567907SDavid Edmondson */ 54956567907SDavid Edmondson static boolean_t 55056567907SDavid Edmondson xnbo_mcast_add(xnb_t *xnbp, ether_addr_t *addr) 55156567907SDavid Edmondson { 55256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 55356567907SDavid Edmondson boolean_t r = B_FALSE; 55456567907SDavid Edmondson 55556567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 55656567907SDavid Edmondson 55756567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 55856567907SDavid Edmondson 55956567907SDavid Edmondson if (xnbo_mcast_find(xnbp, addr, B_FALSE)) { 56056567907SDavid Edmondson r = B_TRUE; 56156567907SDavid Edmondson } else if (mac_multicast_add(xnbop->o_mch, 56256567907SDavid Edmondson (const uint8_t *)addr) == 0) { 56356567907SDavid Edmondson xmca_t *mca; 56456567907SDavid Edmondson 56556567907SDavid Edmondson DTRACE_PROBE3(mcast_add, 56656567907SDavid Edmondson (char *), "add", 56756567907SDavid Edmondson (void *), xnbp, 56856567907SDavid Edmondson (etheraddr_t *), addr); 56956567907SDavid Edmondson 57056567907SDavid Edmondson mca = kmem_alloc(sizeof (*mca), KM_SLEEP); 57156567907SDavid Edmondson bcopy(addr, &mca->addr, sizeof (mca->addr)); 57256567907SDavid Edmondson 57356567907SDavid Edmondson mca->next = xnbop->o_mca; 57456567907SDavid Edmondson xnbop->o_mca = mca; 57556567907SDavid Edmondson 57656567907SDavid Edmondson r = B_TRUE; 57756567907SDavid Edmondson } 57856567907SDavid Edmondson 57956567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 58056567907SDavid Edmondson 58156567907SDavid Edmondson return (r); 58256567907SDavid Edmondson } 58356567907SDavid Edmondson 58456567907SDavid Edmondson /* 58556567907SDavid Edmondson * Remove the multicast address `addr' from the set received. 58656567907SDavid Edmondson */ 58756567907SDavid Edmondson static boolean_t 58856567907SDavid Edmondson xnbo_mcast_del(xnb_t *xnbp, ether_addr_t *addr) 58956567907SDavid Edmondson { 59056567907SDavid Edmondson boolean_t r; 59156567907SDavid Edmondson 59256567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 59356567907SDavid Edmondson r = xnbo_mcast_find(xnbp, addr, B_TRUE); 59456567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 59556567907SDavid Edmondson 59656567907SDavid Edmondson return (r); 597843e1988Sjohnlev } 598843e1988Sjohnlev 599843e1988Sjohnlev static int 600843e1988Sjohnlev xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 601843e1988Sjohnlev { 602843e1988Sjohnlev static xnb_flavour_t flavour = { 60356567907SDavid Edmondson xnbo_to_mac, xnbo_peer_connected, xnbo_peer_disconnected, 60456567907SDavid Edmondson xnbo_hotplug_connected, xnbo_start_connect, 605843e1988Sjohnlev xnbo_cksum_from_peer, xnbo_cksum_to_peer, 60656567907SDavid Edmondson xnbo_mcast_add, xnbo_mcast_del, 607843e1988Sjohnlev }; 608843e1988Sjohnlev xnbo_t *xnbop; 609843e1988Sjohnlev 610843e1988Sjohnlev switch (cmd) { 611843e1988Sjohnlev case DDI_ATTACH: 612843e1988Sjohnlev break; 613843e1988Sjohnlev case DDI_RESUME: 614843e1988Sjohnlev return (DDI_SUCCESS); 615843e1988Sjohnlev default: 616843e1988Sjohnlev return (DDI_FAILURE); 617843e1988Sjohnlev } 618843e1988Sjohnlev 619843e1988Sjohnlev xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP); 620843e1988Sjohnlev 621843e1988Sjohnlev if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) { 622843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 623843e1988Sjohnlev return (DDI_FAILURE); 624843e1988Sjohnlev } 625843e1988Sjohnlev 626843e1988Sjohnlev return (DDI_SUCCESS); 627843e1988Sjohnlev } 628843e1988Sjohnlev 629843e1988Sjohnlev static int 630843e1988Sjohnlev xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 631843e1988Sjohnlev { 632843e1988Sjohnlev xnb_t *xnbp = ddi_get_driver_private(dip); 633551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 634843e1988Sjohnlev 635843e1988Sjohnlev switch (cmd) { 636843e1988Sjohnlev case DDI_DETACH: 637843e1988Sjohnlev break; 638843e1988Sjohnlev case DDI_SUSPEND: 639843e1988Sjohnlev return (DDI_SUCCESS); 640843e1988Sjohnlev default: 641843e1988Sjohnlev return (DDI_FAILURE); 642843e1988Sjohnlev } 643843e1988Sjohnlev 644551bc2a6Smrj mutex_enter(&xnbp->xnb_tx_lock); 645551bc2a6Smrj mutex_enter(&xnbp->xnb_rx_lock); 646843e1988Sjohnlev 647551bc2a6Smrj if (!xnbp->xnb_detachable || xnbp->xnb_connected || 648024c26efSMax zhen (xnbp->xnb_tx_buf_count > 0)) { 649551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 650551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 651843e1988Sjohnlev 652843e1988Sjohnlev return (DDI_FAILURE); 653843e1988Sjohnlev } 654843e1988Sjohnlev 655551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 656551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 657843e1988Sjohnlev 65856567907SDavid Edmondson xnbo_close_mac(xnbp); 659843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 660843e1988Sjohnlev 661843e1988Sjohnlev xnb_detach(dip); 662843e1988Sjohnlev 663843e1988Sjohnlev return (DDI_SUCCESS); 664843e1988Sjohnlev } 665843e1988Sjohnlev 666843e1988Sjohnlev static struct cb_ops cb_ops = { 667843e1988Sjohnlev nulldev, /* open */ 668843e1988Sjohnlev nulldev, /* close */ 669843e1988Sjohnlev nodev, /* strategy */ 670843e1988Sjohnlev nodev, /* print */ 671843e1988Sjohnlev nodev, /* dump */ 672843e1988Sjohnlev nodev, /* read */ 673843e1988Sjohnlev nodev, /* write */ 674843e1988Sjohnlev nodev, /* ioctl */ 675843e1988Sjohnlev nodev, /* devmap */ 676843e1988Sjohnlev nodev, /* mmap */ 677843e1988Sjohnlev nodev, /* segmap */ 678843e1988Sjohnlev nochpoll, /* poll */ 679843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */ 680843e1988Sjohnlev 0, /* streamtab */ 681843e1988Sjohnlev D_NEW | D_MP | D_64BIT /* Driver compatibility flag */ 682843e1988Sjohnlev }; 683843e1988Sjohnlev 684843e1988Sjohnlev static struct dev_ops ops = { 685843e1988Sjohnlev DEVO_REV, /* devo_rev */ 686843e1988Sjohnlev 0, /* devo_refcnt */ 687843e1988Sjohnlev nulldev, /* devo_getinfo */ 688843e1988Sjohnlev nulldev, /* devo_identify */ 689843e1988Sjohnlev nulldev, /* devo_probe */ 690843e1988Sjohnlev xnbo_attach, /* devo_attach */ 691843e1988Sjohnlev xnbo_detach, /* devo_detach */ 692843e1988Sjohnlev nodev, /* devo_reset */ 693843e1988Sjohnlev &cb_ops, /* devo_cb_ops */ 694843e1988Sjohnlev (struct bus_ops *)0, /* devo_bus_ops */ 69519397407SSherry Moore NULL, /* devo_power */ 69619397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 697843e1988Sjohnlev }; 698843e1988Sjohnlev 699843e1988Sjohnlev static struct modldrv modldrv = { 700024c26efSMax zhen &mod_driverops, "xnbo driver", &ops, 701843e1988Sjohnlev }; 702843e1988Sjohnlev 703843e1988Sjohnlev static struct modlinkage modlinkage = { 704843e1988Sjohnlev MODREV_1, &modldrv, NULL 705843e1988Sjohnlev }; 706843e1988Sjohnlev 707843e1988Sjohnlev int 708843e1988Sjohnlev _init(void) 709843e1988Sjohnlev { 710843e1988Sjohnlev return (mod_install(&modlinkage)); 711843e1988Sjohnlev } 712843e1988Sjohnlev 713843e1988Sjohnlev int 714843e1988Sjohnlev _info(struct modinfo *modinfop) 715843e1988Sjohnlev { 716843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 717843e1988Sjohnlev } 718843e1988Sjohnlev 719843e1988Sjohnlev int 720843e1988Sjohnlev _fini(void) 721843e1988Sjohnlev { 722843e1988Sjohnlev return (mod_remove(&modlinkage)); 723843e1988Sjohnlev } 724