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 /* 23fd0939efSDavid 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 34fd0939efSDavid Edmondson #ifdef DEBUG 35fd0939efSDavid Edmondson #define XNBO_DEBUG 1 36fd0939efSDavid Edmondson #endif /* DEBUG */ 37fd0939efSDavid 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 56fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 57fd0939efSDavid Edmondson boolean_t xnbo_cksum_offload_to_peer = B_TRUE; 58fd0939efSDavid Edmondson boolean_t xnbo_cksum_offload_from_peer = B_TRUE; 59fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 60fd0939efSDavid 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) { 124fd0939efSDavid Edmondson uint32_t capab = xnbop->o_hcksum_capab; 125fd0939efSDavid Edmondson 126fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 127fd0939efSDavid Edmondson if (!xnbo_cksum_offload_from_peer) 128fd0939efSDavid Edmondson capab = 0; 129fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 130fd0939efSDavid 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 */ 137fd0939efSDavid 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 154fd0939efSDavid Edmondson #ifdef XNBO_DEBUG 155fd0939efSDavid Edmondson if (!xnbo_cksum_offload_to_peer) 156fd0939efSDavid Edmondson return (0); 157fd0939efSDavid Edmondson #endif /* XNBO_DEBUG */ 158fd0939efSDavid 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 176*0dc2366fSVenugopal Iyer mac_hcksum_get(mp, NULL, NULL, NULL, &csum, &pflags); 177843e1988Sjohnlev 178843e1988Sjohnlev /* 179843e1988Sjohnlev * If the MAC driver has asserted that the checksum is 180843e1988Sjohnlev * good, let the peer know. 181843e1988Sjohnlev */ 182843e1988Sjohnlev if (((pflags & HCK_FULLCKSUM) != 0) && 183843e1988Sjohnlev (((pflags & HCK_FULLCKSUM_OK) != 0) || 184843e1988Sjohnlev (csum == 0xffff))) 185843e1988Sjohnlev r |= NETRXF_data_validated; 186843e1988Sjohnlev 187843e1988Sjohnlev return (r); 188843e1988Sjohnlev } 189843e1988Sjohnlev 190843e1988Sjohnlev /* 191843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer. 192843e1988Sjohnlev */ 193843e1988Sjohnlev /*ARGSUSED*/ 194843e1988Sjohnlev static void 195da14cebeSEric Cheng xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 196da14cebeSEric Cheng boolean_t loopback) 197843e1988Sjohnlev { 198843e1988Sjohnlev xnb_t *xnbp = arg; 199843e1988Sjohnlev 200551bc2a6Smrj mp = xnb_copy_to_peer(xnbp, mp); 201843e1988Sjohnlev 202843e1988Sjohnlev if (mp != NULL) 203843e1988Sjohnlev freemsgchain(mp); 204843e1988Sjohnlev } 205843e1988Sjohnlev 206843e1988Sjohnlev /* 207843e1988Sjohnlev * Packets from the mac device come here. We pass them to the peer if 208843e1988Sjohnlev * the destination mac address matches or it's a multicast/broadcast 209843e1988Sjohnlev * address. 210843e1988Sjohnlev */ 211843e1988Sjohnlev static void 212da14cebeSEric Cheng xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 213da14cebeSEric Cheng boolean_t loopback) 214843e1988Sjohnlev { 21556567907SDavid Edmondson _NOTE(ARGUNUSED(loopback)); 216843e1988Sjohnlev xnb_t *xnbp = arg; 217551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 218843e1988Sjohnlev mblk_t *next, *keep, *keep_head, *free, *free_head; 219843e1988Sjohnlev 220843e1988Sjohnlev keep = keep_head = free = free_head = NULL; 221843e1988Sjohnlev 222843e1988Sjohnlev #define ADD(list, bp) \ 223843e1988Sjohnlev if (list != NULL) \ 224843e1988Sjohnlev list->b_next = bp; \ 225843e1988Sjohnlev else \ 226843e1988Sjohnlev list##_head = bp; \ 227843e1988Sjohnlev list = bp; 228843e1988Sjohnlev 229843e1988Sjohnlev for (; mp != NULL; mp = next) { 230843e1988Sjohnlev mac_header_info_t hdr_info; 231843e1988Sjohnlev 232843e1988Sjohnlev next = mp->b_next; 233843e1988Sjohnlev mp->b_next = NULL; 234843e1988Sjohnlev 235843e1988Sjohnlev if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) { 236843e1988Sjohnlev ADD(free, mp); 237843e1988Sjohnlev continue; 238843e1988Sjohnlev } 239843e1988Sjohnlev 240843e1988Sjohnlev if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || 241843e1988Sjohnlev (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) { 242843e1988Sjohnlev ADD(keep, mp); 243843e1988Sjohnlev continue; 244843e1988Sjohnlev } 245843e1988Sjohnlev 246551bc2a6Smrj if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr, 247551bc2a6Smrj sizeof (xnbp->xnb_mac_addr)) == 0) { 248843e1988Sjohnlev ADD(keep, mp); 249843e1988Sjohnlev continue; 250843e1988Sjohnlev } 251843e1988Sjohnlev 252843e1988Sjohnlev ADD(free, mp); 253843e1988Sjohnlev } 254843e1988Sjohnlev #undef ADD 255843e1988Sjohnlev 256843e1988Sjohnlev if (keep_head != NULL) 257da14cebeSEric Cheng xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE); 258843e1988Sjohnlev 259843e1988Sjohnlev if (free_head != NULL) 260843e1988Sjohnlev freemsgchain(free_head); 261843e1988Sjohnlev } 262843e1988Sjohnlev 263843e1988Sjohnlev static boolean_t 264843e1988Sjohnlev xnbo_open_mac(xnb_t *xnbp, char *mac) 265843e1988Sjohnlev { 266551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 26756567907SDavid Edmondson int err; 268843e1988Sjohnlev const mac_info_t *mi; 269da14cebeSEric Cheng void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t); 270da14cebeSEric Cheng struct ether_addr ea; 271e7801d59Ssowmini uint_t max_sdu; 272da14cebeSEric Cheng mac_diag_t diag; 273843e1988Sjohnlev 274d62bc4baSyz147064 if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) { 275843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 276d62bc4baSyz147064 "cannot open mac for link %s (%d)", mac, err); 277843e1988Sjohnlev return (B_FALSE); 278843e1988Sjohnlev } 279843e1988Sjohnlev ASSERT(xnbop->o_mh != NULL); 280843e1988Sjohnlev 281843e1988Sjohnlev mi = mac_info(xnbop->o_mh); 282843e1988Sjohnlev ASSERT(mi != NULL); 283843e1988Sjohnlev 284843e1988Sjohnlev if (mi->mi_media != DL_ETHER) { 285843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 286d62bc4baSyz147064 "device is not DL_ETHER (%d)", mi->mi_media); 2870324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 288843e1988Sjohnlev return (B_FALSE); 289843e1988Sjohnlev } 290843e1988Sjohnlev if (mi->mi_media != mi->mi_nativemedia) { 291843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 292d62bc4baSyz147064 "device media and native media mismatch (%d != %d)", 293843e1988Sjohnlev mi->mi_media, mi->mi_nativemedia); 2940324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 295843e1988Sjohnlev return (B_FALSE); 296843e1988Sjohnlev } 297e7801d59Ssowmini 298e7801d59Ssowmini mac_sdu_get(xnbop->o_mh, NULL, &max_sdu); 299e7801d59Ssowmini if (max_sdu > XNBMAXPKT) { 300e7801d59Ssowmini cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)", 301e7801d59Ssowmini max_sdu); 3020324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 303843e1988Sjohnlev return (B_FALSE); 304843e1988Sjohnlev } 305843e1988Sjohnlev 306fc4e975dSVenugopal Iyer /* 307fc4e975dSVenugopal Iyer * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a 308fc4e975dSVenugopal Iyer * guest on the localhost itself. In this case we would have the MAC 309fc4e975dSVenugopal Iyer * client open for the guest being migrated *and* also for the 310fc4e975dSVenugopal Iyer * migrated guest (i.e. the former will be active till the migration 311fc4e975dSVenugopal Iyer * is complete when the latter will be activated). This flag states 312fc4e975dSVenugopal Iyer * that it is OK for mac_unicast_add to add the primary MAC unicast 313fc4e975dSVenugopal Iyer * address multiple times. 314fc4e975dSVenugopal Iyer */ 315da14cebeSEric Cheng if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL, 316fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_USE_DATALINK_NAME | 317fc4e975dSVenugopal Iyer MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) { 318da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 319da14cebeSEric Cheng "error (%d) opening mac client", err); 3200324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 321da14cebeSEric Cheng return (B_FALSE); 322da14cebeSEric Cheng } 323da14cebeSEric Cheng 32456567907SDavid Edmondson if (xnbop->o_need_rx_filter) 325843e1988Sjohnlev rx_fn = xnbo_from_mac_filter; 326843e1988Sjohnlev else 327843e1988Sjohnlev rx_fn = xnbo_from_mac; 328843e1988Sjohnlev 329fc4e975dSVenugopal Iyer err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY, 33056567907SDavid Edmondson &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL, 33156567907SDavid Edmondson xnbp); 332fc4e975dSVenugopal Iyer if (err != 0) { 333fc4e975dSVenugopal Iyer cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary " 334fc4e975dSVenugopal Iyer "MAC address of %s: %d", mac, err); 3350324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 336fc4e975dSVenugopal Iyer return (B_FALSE); 337fc4e975dSVenugopal Iyer } 33856567907SDavid Edmondson if (!xnbop->o_multicast_control) { 339da14cebeSEric Cheng err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL, 340ae6aa22aSVenugopal Iyer rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP | 341ae6aa22aSVenugopal Iyer MAC_PROMISC_FLAGS_VLAN_TAG_STRIP); 342da14cebeSEric Cheng if (err != 0) { 343da14cebeSEric Cheng cmn_err(CE_WARN, "xnbo_open_mac: " 344da14cebeSEric Cheng "cannot enable promiscuous mode of %s: %d", 345da14cebeSEric Cheng mac, err); 3460324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_TRUE); 347da14cebeSEric Cheng return (B_FALSE); 348da14cebeSEric Cheng } 349da14cebeSEric Cheng xnbop->o_promiscuous = B_TRUE; 350da14cebeSEric Cheng } 351843e1988Sjohnlev 35256567907SDavid Edmondson if (xnbop->o_need_setphysaddr) { 353da14cebeSEric Cheng err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr); 354843e1988Sjohnlev /* Warn, but continue on. */ 355843e1988Sjohnlev if (err != 0) { 356551bc2a6Smrj bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet, 357843e1988Sjohnlev ETHERADDRL); 358843e1988Sjohnlev cmn_err(CE_WARN, "xnbo_open_mac: " 359843e1988Sjohnlev "cannot set MAC address of %s to " 360da14cebeSEric Cheng "%s: %d", mac, ether_sprintf(&ea), err); 361843e1988Sjohnlev } 362843e1988Sjohnlev } 363843e1988Sjohnlev 36456567907SDavid Edmondson if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, 36556567907SDavid Edmondson &xnbop->o_hcksum_capab)) 36656567907SDavid Edmondson xnbop->o_hcksum_capab = 0; 36756567907SDavid Edmondson 368843e1988Sjohnlev xnbop->o_running = B_TRUE; 369843e1988Sjohnlev 370843e1988Sjohnlev return (B_TRUE); 371843e1988Sjohnlev } 372843e1988Sjohnlev 373843e1988Sjohnlev static void 37456567907SDavid Edmondson xnbo_close_mac(xnb_t *xnbp) 375843e1988Sjohnlev { 3760324f02aSDavid Edmondson i_xnbo_close_mac(xnbp, B_FALSE); 3770324f02aSDavid Edmondson } 3780324f02aSDavid Edmondson 3790324f02aSDavid Edmondson static void 3800324f02aSDavid Edmondson i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked) 3810324f02aSDavid Edmondson { 38256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 38356567907SDavid Edmondson xmca_t *loop; 38456567907SDavid Edmondson 3850324f02aSDavid Edmondson ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock)); 3860324f02aSDavid Edmondson 387843e1988Sjohnlev if (xnbop->o_mh == NULL) 388843e1988Sjohnlev return; 389843e1988Sjohnlev 39056567907SDavid Edmondson if (xnbop->o_running) 391843e1988Sjohnlev xnbop->o_running = B_FALSE; 39256567907SDavid Edmondson 3930324f02aSDavid Edmondson if (!locked) 39456567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 39556567907SDavid Edmondson loop = xnbop->o_mca; 39656567907SDavid Edmondson xnbop->o_mca = NULL; 3970324f02aSDavid Edmondson if (!locked) 39856567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 39956567907SDavid Edmondson 40056567907SDavid Edmondson while (loop != NULL) { 40156567907SDavid Edmondson xmca_t *next = loop->next; 40256567907SDavid Edmondson 40356567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 40456567907SDavid Edmondson (char *), "close", 40556567907SDavid Edmondson (void *), xnbp, 40656567907SDavid Edmondson (etheraddr_t *), loop->addr); 40756567907SDavid Edmondson (void) mac_multicast_remove(xnbop->o_mch, loop->addr); 40856567907SDavid Edmondson kmem_free(loop, sizeof (*loop)); 40956567907SDavid Edmondson loop = next; 410843e1988Sjohnlev } 411843e1988Sjohnlev 412843e1988Sjohnlev if (xnbop->o_promiscuous) { 41349dbeaeaSDavid Edmondson if (xnbop->o_mphp != NULL) { 4145ecc58b1SGirish Moodalbail mac_promisc_remove(xnbop->o_mphp); 41549dbeaeaSDavid Edmondson xnbop->o_mphp = NULL; 41649dbeaeaSDavid Edmondson } 417843e1988Sjohnlev xnbop->o_promiscuous = B_FALSE; 418da14cebeSEric Cheng } else { 41949dbeaeaSDavid Edmondson if (xnbop->o_mch != NULL) 420da14cebeSEric Cheng mac_rx_clear(xnbop->o_mch); 421843e1988Sjohnlev } 422843e1988Sjohnlev 423da14cebeSEric Cheng if (xnbop->o_mah != NULL) { 424da14cebeSEric Cheng (void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah); 425da14cebeSEric Cheng xnbop->o_mah = NULL; 426843e1988Sjohnlev } 427843e1988Sjohnlev 428da14cebeSEric Cheng if (xnbop->o_mch != NULL) { 429da14cebeSEric Cheng mac_client_close(xnbop->o_mch, 0); 430da14cebeSEric Cheng xnbop->o_mch = NULL; 431843e1988Sjohnlev } 432843e1988Sjohnlev 433843e1988Sjohnlev mac_close(xnbop->o_mh); 434843e1988Sjohnlev xnbop->o_mh = NULL; 435843e1988Sjohnlev } 436843e1988Sjohnlev 437843e1988Sjohnlev /* 43856567907SDavid Edmondson * Hotplug has completed and we are connected to the peer. We have all 43956567907SDavid Edmondson * the information we need to exchange traffic, so open the MAC device 44056567907SDavid Edmondson * and configure it appropriately. 441843e1988Sjohnlev */ 44256567907SDavid Edmondson static boolean_t 44356567907SDavid Edmondson xnbo_start_connect(xnb_t *xnbp) 444843e1988Sjohnlev { 44556567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 44656567907SDavid Edmondson 44756567907SDavid Edmondson return (xnbo_open_mac(xnbp, xnbop->o_link_name)); 448843e1988Sjohnlev } 449843e1988Sjohnlev 450843e1988Sjohnlev /* 45156567907SDavid Edmondson * The guest has successfully synchronize with this instance. We read 45256567907SDavid Edmondson * the configuration of the guest from xenstore to check whether the 45356567907SDavid Edmondson * guest requests multicast control. If not (the default) we make a 45456567907SDavid Edmondson * note that the MAC device needs to be used in promiscious mode. 45556567907SDavid Edmondson */ 45656567907SDavid Edmondson static boolean_t 45756567907SDavid Edmondson xnbo_peer_connected(xnb_t *xnbp) 45856567907SDavid Edmondson { 45956567907SDavid Edmondson char *oename; 46056567907SDavid Edmondson int request; 46156567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 46256567907SDavid Edmondson 46356567907SDavid Edmondson oename = xvdi_get_oename(xnbp->xnb_devinfo); 46456567907SDavid Edmondson 46556567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, oename, 46656567907SDavid Edmondson "request-multicast-control", "%d", &request) != 0) 46756567907SDavid Edmondson request = 0; 46856567907SDavid Edmondson xnbop->o_multicast_control = (request > 0); 46956567907SDavid Edmondson 47056567907SDavid Edmondson return (B_TRUE); 47156567907SDavid Edmondson } 47256567907SDavid Edmondson 47356567907SDavid Edmondson /* 47456567907SDavid Edmondson * The guest domain has closed down the inter-domain connection. We 47556567907SDavid Edmondson * close the underlying MAC device. 476843e1988Sjohnlev */ 477843e1988Sjohnlev static void 47856567907SDavid Edmondson xnbo_peer_disconnected(xnb_t *xnbp) 479843e1988Sjohnlev { 48056567907SDavid Edmondson xnbo_close_mac(xnbp); 48156567907SDavid Edmondson } 48256567907SDavid Edmondson 48356567907SDavid Edmondson /* 48456567907SDavid Edmondson * The hotplug script has completed. We read information from xenstore 48556567907SDavid Edmondson * about our configuration, most notably the name of the MAC device we 48656567907SDavid Edmondson * should use. 48756567907SDavid Edmondson */ 48856567907SDavid Edmondson static boolean_t 48956567907SDavid Edmondson xnbo_hotplug_connected(xnb_t *xnbp) 49056567907SDavid Edmondson { 49156567907SDavid Edmondson char *xsname; 49256567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 49356567907SDavid Edmondson int need; 49456567907SDavid Edmondson 49556567907SDavid Edmondson xsname = xvdi_get_xsname(xnbp->xnb_devinfo); 49656567907SDavid Edmondson 49756567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 49856567907SDavid Edmondson "nic", "%s", xnbop->o_link_name) != 0) { 49956567907SDavid Edmondson cmn_err(CE_WARN, "xnbo_connect: " 50056567907SDavid Edmondson "cannot read nic name from %s", xsname); 50156567907SDavid Edmondson return (B_FALSE); 50256567907SDavid Edmondson } 50356567907SDavid Edmondson 50456567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 50556567907SDavid Edmondson "SUNW-need-rx-filter", "%d", &need) != 0) 50656567907SDavid Edmondson need = 0; 50756567907SDavid Edmondson xnbop->o_need_rx_filter = (need > 0); 50856567907SDavid Edmondson 50956567907SDavid Edmondson if (xenbus_scanf(XBT_NULL, xsname, 51056567907SDavid Edmondson "SUNW-need-set-physaddr", "%d", &need) != 0) 51156567907SDavid Edmondson need = 0; 51256567907SDavid Edmondson xnbop->o_need_setphysaddr = (need > 0); 51356567907SDavid Edmondson 51456567907SDavid Edmondson return (B_TRUE); 51556567907SDavid Edmondson } 51656567907SDavid Edmondson 51756567907SDavid Edmondson /* 51856567907SDavid Edmondson * Find the multicast address `addr', return B_TRUE if it is one that 51956567907SDavid Edmondson * we receive. If `remove', remove it from the set received. 52056567907SDavid Edmondson */ 52156567907SDavid Edmondson static boolean_t 52256567907SDavid Edmondson xnbo_mcast_find(xnb_t *xnbp, ether_addr_t *addr, boolean_t remove) 52356567907SDavid Edmondson { 52456567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 52556567907SDavid Edmondson xmca_t *prev, *del, *this; 52656567907SDavid Edmondson 52756567907SDavid Edmondson ASSERT(MUTEX_HELD(&xnbp->xnb_state_lock)); 52856567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 52956567907SDavid Edmondson 53056567907SDavid Edmondson prev = del = NULL; 53156567907SDavid Edmondson 53256567907SDavid Edmondson this = xnbop->o_mca; 53356567907SDavid Edmondson 53456567907SDavid Edmondson while (this != NULL) { 53556567907SDavid Edmondson if (bcmp(&this->addr, addr, sizeof (this->addr)) == 0) { 53656567907SDavid Edmondson del = this; 53756567907SDavid Edmondson if (remove) { 53856567907SDavid Edmondson if (prev == NULL) 53956567907SDavid Edmondson xnbop->o_mca = this->next; 54056567907SDavid Edmondson else 54156567907SDavid Edmondson prev->next = this->next; 54256567907SDavid Edmondson } 54356567907SDavid Edmondson break; 54456567907SDavid Edmondson } 54556567907SDavid Edmondson 54656567907SDavid Edmondson prev = this; 54756567907SDavid Edmondson this = this->next; 54856567907SDavid Edmondson } 54956567907SDavid Edmondson 55056567907SDavid Edmondson if (del == NULL) 55156567907SDavid Edmondson return (B_FALSE); 55256567907SDavid Edmondson 55356567907SDavid Edmondson if (remove) { 55456567907SDavid Edmondson DTRACE_PROBE3(mcast_remove, 55556567907SDavid Edmondson (char *), "remove", 55656567907SDavid Edmondson (void *), xnbp, 55756567907SDavid Edmondson (etheraddr_t *), del->addr); 55856567907SDavid Edmondson mac_multicast_remove(xnbop->o_mch, del->addr); 55956567907SDavid Edmondson kmem_free(del, sizeof (*del)); 56056567907SDavid Edmondson } 56156567907SDavid Edmondson 56256567907SDavid Edmondson return (B_TRUE); 56356567907SDavid Edmondson } 56456567907SDavid Edmondson 56556567907SDavid Edmondson /* 56656567907SDavid Edmondson * Add the multicast address `addr' to the set received. 56756567907SDavid Edmondson */ 56856567907SDavid Edmondson static boolean_t 56956567907SDavid Edmondson xnbo_mcast_add(xnb_t *xnbp, ether_addr_t *addr) 57056567907SDavid Edmondson { 57156567907SDavid Edmondson xnbo_t *xnbop = xnbp->xnb_flavour_data; 57256567907SDavid Edmondson boolean_t r = B_FALSE; 57356567907SDavid Edmondson 57456567907SDavid Edmondson ASSERT(xnbop->o_promiscuous == B_FALSE); 57556567907SDavid Edmondson 57656567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 57756567907SDavid Edmondson 57856567907SDavid Edmondson if (xnbo_mcast_find(xnbp, addr, B_FALSE)) { 57956567907SDavid Edmondson r = B_TRUE; 58056567907SDavid Edmondson } else if (mac_multicast_add(xnbop->o_mch, 58156567907SDavid Edmondson (const uint8_t *)addr) == 0) { 58256567907SDavid Edmondson xmca_t *mca; 58356567907SDavid Edmondson 58456567907SDavid Edmondson DTRACE_PROBE3(mcast_add, 58556567907SDavid Edmondson (char *), "add", 58656567907SDavid Edmondson (void *), xnbp, 58756567907SDavid Edmondson (etheraddr_t *), addr); 58856567907SDavid Edmondson 58956567907SDavid Edmondson mca = kmem_alloc(sizeof (*mca), KM_SLEEP); 59056567907SDavid Edmondson bcopy(addr, &mca->addr, sizeof (mca->addr)); 59156567907SDavid Edmondson 59256567907SDavid Edmondson mca->next = xnbop->o_mca; 59356567907SDavid Edmondson xnbop->o_mca = mca; 59456567907SDavid Edmondson 59556567907SDavid Edmondson r = B_TRUE; 59656567907SDavid Edmondson } 59756567907SDavid Edmondson 59856567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 59956567907SDavid Edmondson 60056567907SDavid Edmondson return (r); 60156567907SDavid Edmondson } 60256567907SDavid Edmondson 60356567907SDavid Edmondson /* 60456567907SDavid Edmondson * Remove the multicast address `addr' from the set received. 60556567907SDavid Edmondson */ 60656567907SDavid Edmondson static boolean_t 60756567907SDavid Edmondson xnbo_mcast_del(xnb_t *xnbp, ether_addr_t *addr) 60856567907SDavid Edmondson { 60956567907SDavid Edmondson boolean_t r; 61056567907SDavid Edmondson 61156567907SDavid Edmondson mutex_enter(&xnbp->xnb_state_lock); 61256567907SDavid Edmondson r = xnbo_mcast_find(xnbp, addr, B_TRUE); 61356567907SDavid Edmondson mutex_exit(&xnbp->xnb_state_lock); 61456567907SDavid Edmondson 61556567907SDavid Edmondson return (r); 616843e1988Sjohnlev } 617843e1988Sjohnlev 618843e1988Sjohnlev static int 619843e1988Sjohnlev xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 620843e1988Sjohnlev { 621843e1988Sjohnlev static xnb_flavour_t flavour = { 62256567907SDavid Edmondson xnbo_to_mac, xnbo_peer_connected, xnbo_peer_disconnected, 62356567907SDavid Edmondson xnbo_hotplug_connected, xnbo_start_connect, 624843e1988Sjohnlev xnbo_cksum_from_peer, xnbo_cksum_to_peer, 62556567907SDavid Edmondson xnbo_mcast_add, xnbo_mcast_del, 626843e1988Sjohnlev }; 627843e1988Sjohnlev xnbo_t *xnbop; 628843e1988Sjohnlev 629843e1988Sjohnlev switch (cmd) { 630843e1988Sjohnlev case DDI_ATTACH: 631843e1988Sjohnlev break; 632843e1988Sjohnlev case DDI_RESUME: 633843e1988Sjohnlev return (DDI_SUCCESS); 634843e1988Sjohnlev default: 635843e1988Sjohnlev return (DDI_FAILURE); 636843e1988Sjohnlev } 637843e1988Sjohnlev 638843e1988Sjohnlev xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP); 639843e1988Sjohnlev 640843e1988Sjohnlev if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) { 641843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 642843e1988Sjohnlev return (DDI_FAILURE); 643843e1988Sjohnlev } 644843e1988Sjohnlev 645843e1988Sjohnlev return (DDI_SUCCESS); 646843e1988Sjohnlev } 647843e1988Sjohnlev 648843e1988Sjohnlev static int 649843e1988Sjohnlev xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 650843e1988Sjohnlev { 651843e1988Sjohnlev xnb_t *xnbp = ddi_get_driver_private(dip); 652551bc2a6Smrj xnbo_t *xnbop = xnbp->xnb_flavour_data; 653843e1988Sjohnlev 654843e1988Sjohnlev switch (cmd) { 655843e1988Sjohnlev case DDI_DETACH: 656843e1988Sjohnlev break; 657843e1988Sjohnlev case DDI_SUSPEND: 658843e1988Sjohnlev return (DDI_SUCCESS); 659843e1988Sjohnlev default: 660843e1988Sjohnlev return (DDI_FAILURE); 661843e1988Sjohnlev } 662843e1988Sjohnlev 663551bc2a6Smrj mutex_enter(&xnbp->xnb_tx_lock); 664551bc2a6Smrj mutex_enter(&xnbp->xnb_rx_lock); 665843e1988Sjohnlev 666551bc2a6Smrj if (!xnbp->xnb_detachable || xnbp->xnb_connected || 667024c26efSMax zhen (xnbp->xnb_tx_buf_count > 0)) { 668551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 669551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 670843e1988Sjohnlev 671843e1988Sjohnlev return (DDI_FAILURE); 672843e1988Sjohnlev } 673843e1988Sjohnlev 674551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 675551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 676843e1988Sjohnlev 67756567907SDavid Edmondson xnbo_close_mac(xnbp); 678843e1988Sjohnlev kmem_free(xnbop, sizeof (*xnbop)); 679843e1988Sjohnlev 680843e1988Sjohnlev xnb_detach(dip); 681843e1988Sjohnlev 682843e1988Sjohnlev return (DDI_SUCCESS); 683843e1988Sjohnlev } 684843e1988Sjohnlev 685843e1988Sjohnlev static struct cb_ops cb_ops = { 686843e1988Sjohnlev nulldev, /* open */ 687843e1988Sjohnlev nulldev, /* close */ 688843e1988Sjohnlev nodev, /* strategy */ 689843e1988Sjohnlev nodev, /* print */ 690843e1988Sjohnlev nodev, /* dump */ 691843e1988Sjohnlev nodev, /* read */ 692843e1988Sjohnlev nodev, /* write */ 693843e1988Sjohnlev nodev, /* ioctl */ 694843e1988Sjohnlev nodev, /* devmap */ 695843e1988Sjohnlev nodev, /* mmap */ 696843e1988Sjohnlev nodev, /* segmap */ 697843e1988Sjohnlev nochpoll, /* poll */ 698843e1988Sjohnlev ddi_prop_op, /* cb_prop_op */ 699843e1988Sjohnlev 0, /* streamtab */ 700843e1988Sjohnlev D_NEW | D_MP | D_64BIT /* Driver compatibility flag */ 701843e1988Sjohnlev }; 702843e1988Sjohnlev 703843e1988Sjohnlev static struct dev_ops ops = { 704843e1988Sjohnlev DEVO_REV, /* devo_rev */ 705843e1988Sjohnlev 0, /* devo_refcnt */ 706843e1988Sjohnlev nulldev, /* devo_getinfo */ 707843e1988Sjohnlev nulldev, /* devo_identify */ 708843e1988Sjohnlev nulldev, /* devo_probe */ 709843e1988Sjohnlev xnbo_attach, /* devo_attach */ 710843e1988Sjohnlev xnbo_detach, /* devo_detach */ 711843e1988Sjohnlev nodev, /* devo_reset */ 712843e1988Sjohnlev &cb_ops, /* devo_cb_ops */ 713843e1988Sjohnlev (struct bus_ops *)0, /* devo_bus_ops */ 71419397407SSherry Moore NULL, /* devo_power */ 71519397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 716843e1988Sjohnlev }; 717843e1988Sjohnlev 718843e1988Sjohnlev static struct modldrv modldrv = { 719024c26efSMax zhen &mod_driverops, "xnbo driver", &ops, 720843e1988Sjohnlev }; 721843e1988Sjohnlev 722843e1988Sjohnlev static struct modlinkage modlinkage = { 723843e1988Sjohnlev MODREV_1, &modldrv, NULL 724843e1988Sjohnlev }; 725843e1988Sjohnlev 726843e1988Sjohnlev int 727843e1988Sjohnlev _init(void) 728843e1988Sjohnlev { 729843e1988Sjohnlev return (mod_install(&modlinkage)); 730843e1988Sjohnlev } 731843e1988Sjohnlev 732843e1988Sjohnlev int 733843e1988Sjohnlev _info(struct modinfo *modinfop) 734843e1988Sjohnlev { 735843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 736843e1988Sjohnlev } 737843e1988Sjohnlev 738843e1988Sjohnlev int 739843e1988Sjohnlev _fini(void) 740843e1988Sjohnlev { 741843e1988Sjohnlev return (mod_remove(&modlinkage)); 742843e1988Sjohnlev } 743