Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Xen network backend - mac client edition.
     29  *
     30  * A driver that sits above an existing GLDv3/Nemo MAC driver and
     31  * relays packets to/from that driver from/to a guest domain.
     32  */
     33 
     34 #ifdef DEBUG
     35 #define	XNBO_DEBUG 1
     36 #endif /* DEBUG */
     37 
     38 #include "xnb.h"
     39 
     40 #include <sys/sunddi.h>
     41 #include <sys/ddi.h>
     42 #include <sys/modctl.h>
     43 #include <sys/strsubr.h>
     44 #include <sys/mac_client.h>
     45 #include <sys/mac_provider.h>
     46 #include <sys/mac_client_priv.h>
     47 #include <sys/mac.h>
     48 #include <net/if.h>
     49 #include <sys/dlpi.h>
     50 #include <sys/pattr.h>
     51 #include <xen/sys/xenbus_impl.h>
     52 #include <xen/sys/xendev.h>
     53 #include <sys/sdt.h>
     54 #include <sys/note.h>
     55 
     56 #ifdef XNBO_DEBUG
     57 boolean_t xnbo_cksum_offload_to_peer = B_TRUE;
     58 boolean_t xnbo_cksum_offload_from_peer = B_TRUE;
     59 #endif /* XNBO_DEBUG */
     60 
     61 /* Track multicast addresses. */
     62 typedef struct xmca {
     63 	struct xmca *next;
     64 	ether_addr_t addr;
     65 } xmca_t;
     66 
     67 /* State about this device instance. */
     68 typedef struct xnbo {
     69 	mac_handle_t		o_mh;
     70 	mac_client_handle_t	o_mch;
     71 	mac_unicast_handle_t	o_mah;
     72 	mac_promisc_handle_t	o_mphp;
     73 	boolean_t		o_running;
     74 	boolean_t		o_promiscuous;
     75 	uint32_t		o_hcksum_capab;
     76 	xmca_t			*o_mca;
     77 	char			o_link_name[LIFNAMSIZ];
     78 	boolean_t		o_need_rx_filter;
     79 	boolean_t		o_need_setphysaddr;
     80 	boolean_t		o_multicast_control;
     81 } xnbo_t;
     82 
     83 static void xnbo_close_mac(xnb_t *);
     84 static void i_xnbo_close_mac(xnb_t *, boolean_t);
     85 
     86 /*
     87  * Packets from the peer come here.  We pass them to the mac device.
     88  */
     89 static void
     90 xnbo_to_mac(xnb_t *xnbp, mblk_t *mp)
     91 {
     92 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
     93 
     94 	ASSERT(mp != NULL);
     95 
     96 	if (!xnbop->o_running) {
     97 		xnbp->xnb_stat_tx_too_early++;
     98 		goto fail;
     99 	}
    100 
    101 	if (mac_tx(xnbop->o_mch, mp, 0,
    102 	    MAC_DROP_ON_NO_DESC, NULL) != NULL) {
    103 		xnbp->xnb_stat_mac_full++;
    104 	}
    105 
    106 	return;
    107 
    108 fail:
    109 	freemsgchain(mp);
    110 }
    111 
    112 /*
    113  * Process the checksum flags `flags' provided by the peer for the
    114  * packet `mp'.
    115  */
    116 static mblk_t *
    117 xnbo_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags)
    118 {
    119 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    120 
    121 	ASSERT(mp->b_next == NULL);
    122 
    123 	if ((flags & NETTXF_csum_blank) != 0) {
    124 		uint32_t capab = xnbop->o_hcksum_capab;
    125 
    126 #ifdef XNBO_DEBUG
    127 		if (!xnbo_cksum_offload_from_peer)
    128 			capab = 0;
    129 #endif /* XNBO_DEBUG */
    130 
    131 		/*
    132 		 * The checksum in the packet is blank.  Determine
    133 		 * whether we can do hardware offload and, if so,
    134 		 * update the flags on the mblk according.  If not,
    135 		 * calculate and insert the checksum using software.
    136 		 */
    137 		mp = xnb_process_cksum_flags(xnbp, mp, capab);
    138 	}
    139 
    140 	return (mp);
    141 }
    142 
    143 /*
    144  * Calculate the checksum flags to be relayed to the peer for the
    145  * packet `mp'.
    146  */
    147 static uint16_t
    148 xnbo_cksum_to_peer(xnb_t *xnbp, mblk_t *mp)
    149 {
    150 	_NOTE(ARGUNUSED(xnbp));
    151 	uint16_t r = 0;
    152 	uint32_t pflags, csum;
    153 
    154 #ifdef XNBO_DEBUG
    155 	if (!xnbo_cksum_offload_to_peer)
    156 		return (0);
    157 #endif /* XNBO_DEBUG */
    158 
    159 	/*
    160 	 * We might also check for HCK_PARTIALCKSUM here and,
    161 	 * providing that the partial checksum covers the TCP/UDP
    162 	 * payload, return NETRXF_data_validated.
    163 	 *
    164 	 * It seems that it's probably not worthwhile, as even MAC
    165 	 * devices which advertise HCKSUM_INET_PARTIAL in their
    166 	 * capabilities tend to use HCK_FULLCKSUM on the receive side
    167 	 * - they are actually saying that in the output path the
    168 	 * caller must use HCK_PARTIALCKSUM.
    169 	 *
    170 	 * Then again, if a NIC supports HCK_PARTIALCKSUM in its'
    171 	 * output path, the host IP stack will use it. If such packets
    172 	 * are destined for the peer (i.e. looped around) we would
    173 	 * gain some advantage.
    174 	 */
    175 
    176 	hcksum_retrieve(mp, NULL, NULL, NULL, NULL,
    177 	    NULL, &csum, &pflags);
    178 
    179 	/*
    180 	 * If the MAC driver has asserted that the checksum is
    181 	 * good, let the peer know.
    182 	 */
    183 	if (((pflags & HCK_FULLCKSUM) != 0) &&
    184 	    (((pflags & HCK_FULLCKSUM_OK) != 0) ||
    185 	    (csum == 0xffff)))
    186 		r |= NETRXF_data_validated;
    187 
    188 	return (r);
    189 }
    190 
    191 /*
    192  * Packets from the mac device come here.  We pass them to the peer.
    193  */
    194 /*ARGSUSED*/
    195 static void
    196 xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
    197     boolean_t loopback)
    198 {
    199 	xnb_t *xnbp = arg;
    200 
    201 	mp = xnb_copy_to_peer(xnbp, mp);
    202 
    203 	if (mp != NULL)
    204 		freemsgchain(mp);
    205 }
    206 
    207 /*
    208  * Packets from the mac device come here. We pass them to the peer if
    209  * the destination mac address matches or it's a multicast/broadcast
    210  * address.
    211  */
    212 static void
    213 xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
    214     boolean_t loopback)
    215 {
    216 	_NOTE(ARGUNUSED(loopback));
    217 	xnb_t *xnbp = arg;
    218 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    219 	mblk_t *next, *keep, *keep_head, *free, *free_head;
    220 
    221 	keep = keep_head = free = free_head = NULL;
    222 
    223 #define	ADD(list, bp)				\
    224 	if (list != NULL)			\
    225 		list->b_next = bp;		\
    226 	else					\
    227 		list##_head = bp;		\
    228 	list = bp;
    229 
    230 	for (; mp != NULL; mp = next) {
    231 		mac_header_info_t hdr_info;
    232 
    233 		next = mp->b_next;
    234 		mp->b_next = NULL;
    235 
    236 		if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) {
    237 			ADD(free, mp);
    238 			continue;
    239 		}
    240 
    241 		if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) ||
    242 		    (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) {
    243 			ADD(keep, mp);
    244 			continue;
    245 		}
    246 
    247 		if (bcmp(hdr_info.mhi_daddr, xnbp->xnb_mac_addr,
    248 		    sizeof (xnbp->xnb_mac_addr)) == 0) {
    249 			ADD(keep, mp);
    250 			continue;
    251 		}
    252 
    253 		ADD(free, mp);
    254 	}
    255 #undef	ADD
    256 
    257 	if (keep_head != NULL)
    258 		xnbo_from_mac(xnbp, mrh, keep_head, B_FALSE);
    259 
    260 	if (free_head != NULL)
    261 		freemsgchain(free_head);
    262 }
    263 
    264 static boolean_t
    265 xnbo_open_mac(xnb_t *xnbp, char *mac)
    266 {
    267 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    268 	int err;
    269 	const mac_info_t *mi;
    270 	void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t);
    271 	struct ether_addr ea;
    272 	uint_t max_sdu;
    273 	mac_diag_t diag;
    274 
    275 	if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) {
    276 		cmn_err(CE_WARN, "xnbo_open_mac: "
    277 		    "cannot open mac for link %s (%d)", mac, err);
    278 		return (B_FALSE);
    279 	}
    280 	ASSERT(xnbop->o_mh != NULL);
    281 
    282 	mi = mac_info(xnbop->o_mh);
    283 	ASSERT(mi != NULL);
    284 
    285 	if (mi->mi_media != DL_ETHER) {
    286 		cmn_err(CE_WARN, "xnbo_open_mac: "
    287 		    "device is not DL_ETHER (%d)", mi->mi_media);
    288 		i_xnbo_close_mac(xnbp, B_TRUE);
    289 		return (B_FALSE);
    290 	}
    291 	if (mi->mi_media != mi->mi_nativemedia) {
    292 		cmn_err(CE_WARN, "xnbo_open_mac: "
    293 		    "device media and native media mismatch (%d != %d)",
    294 		    mi->mi_media, mi->mi_nativemedia);
    295 		i_xnbo_close_mac(xnbp, B_TRUE);
    296 		return (B_FALSE);
    297 	}
    298 
    299 	mac_sdu_get(xnbop->o_mh, NULL, &max_sdu);
    300 	if (max_sdu > XNBMAXPKT) {
    301 		cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)",
    302 		    max_sdu);
    303 		i_xnbo_close_mac(xnbp, B_TRUE);
    304 		return (B_FALSE);
    305 	}
    306 
    307 	/*
    308 	 * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a
    309 	 * guest on the localhost itself. In this case we would have the MAC
    310 	 * client open for the guest being migrated *and* also for the
    311 	 * migrated guest (i.e. the former will be active till the migration
    312 	 * is complete when the latter will be activated). This flag states
    313 	 * that it is OK for mac_unicast_add to add the primary MAC unicast
    314 	 * address multiple times.
    315 	 */
    316 	if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL,
    317 	    MAC_OPEN_FLAGS_USE_DATALINK_NAME |
    318 	    MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) {
    319 		cmn_err(CE_WARN, "xnbo_open_mac: "
    320 		    "error (%d) opening mac client", err);
    321 		i_xnbo_close_mac(xnbp, B_TRUE);
    322 		return (B_FALSE);
    323 	}
    324 
    325 	if (xnbop->o_need_rx_filter)
    326 		rx_fn = xnbo_from_mac_filter;
    327 	else
    328 		rx_fn = xnbo_from_mac;
    329 
    330 	err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY,
    331 	    &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL,
    332 	    xnbp);
    333 	if (err != 0) {
    334 		cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary "
    335 		    "MAC address of %s: %d", mac, err);
    336 		i_xnbo_close_mac(xnbp, B_TRUE);
    337 		return (B_FALSE);
    338 	}
    339 	if (!xnbop->o_multicast_control) {
    340 		err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL,
    341 		    rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP |
    342 		    MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
    343 		if (err != 0) {
    344 			cmn_err(CE_WARN, "xnbo_open_mac: "
    345 			    "cannot enable promiscuous mode of %s: %d",
    346 			    mac, err);
    347 			i_xnbo_close_mac(xnbp, B_TRUE);
    348 			return (B_FALSE);
    349 		}
    350 		xnbop->o_promiscuous = B_TRUE;
    351 	}
    352 
    353 	if (xnbop->o_need_setphysaddr) {
    354 		err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr);
    355 		/* Warn, but continue on. */
    356 		if (err != 0) {
    357 			bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet,
    358 			    ETHERADDRL);
    359 			cmn_err(CE_WARN, "xnbo_open_mac: "
    360 			    "cannot set MAC address of %s to "
    361 			    "%s: %d", mac, ether_sprintf(&ea), err);
    362 		}
    363 	}
    364 
    365 	if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM,
    366 	    &xnbop->o_hcksum_capab))
    367 		xnbop->o_hcksum_capab = 0;
    368 
    369 	xnbop->o_running = B_TRUE;
    370 
    371 	return (B_TRUE);
    372 }
    373 
    374 static void
    375 xnbo_close_mac(xnb_t *xnbp)
    376 {
    377 	i_xnbo_close_mac(xnbp, B_FALSE);
    378 }
    379 
    380 static void
    381 i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked)
    382 {
    383 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    384 	xmca_t *loop;
    385 
    386 	ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock));
    387 
    388 	if (xnbop->o_mh == NULL)
    389 		return;
    390 
    391 	if (xnbop->o_running)
    392 		xnbop->o_running = B_FALSE;
    393 
    394 	if (!locked)
    395 		mutex_enter(&xnbp->xnb_state_lock);
    396 	loop = xnbop->o_mca;
    397 	xnbop->o_mca = NULL;
    398 	if (!locked)
    399 		mutex_exit(&xnbp->xnb_state_lock);
    400 
    401 	while (loop != NULL) {
    402 		xmca_t *next = loop->next;
    403 
    404 		DTRACE_PROBE3(mcast_remove,
    405 		    (char *), "close",
    406 		    (void *), xnbp,
    407 		    (etheraddr_t *), loop->addr);
    408 		(void) mac_multicast_remove(xnbop->o_mch, loop->addr);
    409 		kmem_free(loop, sizeof (*loop));
    410 		loop = next;
    411 	}
    412 
    413 	if (xnbop->o_promiscuous) {
    414 		if (xnbop->o_mphp != NULL) {
    415 			mac_promisc_remove(xnbop->o_mphp);
    416 			xnbop->o_mphp = NULL;
    417 		}
    418 		xnbop->o_promiscuous = B_FALSE;
    419 	} else {
    420 		if (xnbop->o_mch != NULL)
    421 			mac_rx_clear(xnbop->o_mch);
    422 	}
    423 
    424 	if (xnbop->o_mah != NULL) {
    425 		(void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah);
    426 		xnbop->o_mah = NULL;
    427 	}
    428 
    429 	if (xnbop->o_mch != NULL) {
    430 		mac_client_close(xnbop->o_mch, 0);
    431 		xnbop->o_mch = NULL;
    432 	}
    433 
    434 	mac_close(xnbop->o_mh);
    435 	xnbop->o_mh = NULL;
    436 }
    437 
    438 /*
    439  * Hotplug has completed and we are connected to the peer. We have all
    440  * the information we need to exchange traffic, so open the MAC device
    441  * and configure it appropriately.
    442  */
    443 static boolean_t
    444 xnbo_start_connect(xnb_t *xnbp)
    445 {
    446 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    447 
    448 	return (xnbo_open_mac(xnbp, xnbop->o_link_name));
    449 }
    450 
    451 /*
    452  * The guest has successfully synchronize with this instance. We read
    453  * the configuration of the guest from xenstore to check whether the
    454  * guest requests multicast control. If not (the default) we make a
    455  * note that the MAC device needs to be used in promiscious mode.
    456  */
    457 static boolean_t
    458 xnbo_peer_connected(xnb_t *xnbp)
    459 {
    460 	char *oename;
    461 	int request;
    462 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    463 
    464 	oename = xvdi_get_oename(xnbp->xnb_devinfo);
    465 
    466 	if (xenbus_scanf(XBT_NULL, oename,
    467 	    "request-multicast-control", "%d", &request) != 0)
    468 		request = 0;
    469 	xnbop->o_multicast_control = (request > 0);
    470 
    471 	return (B_TRUE);
    472 }
    473 
    474 /*
    475  * The guest domain has closed down the inter-domain connection. We
    476  * close the underlying MAC device.
    477  */
    478 static void
    479 xnbo_peer_disconnected(xnb_t *xnbp)
    480 {
    481 	xnbo_close_mac(xnbp);
    482 }
    483 
    484 /*
    485  * The hotplug script has completed. We read information from xenstore
    486  * about our configuration, most notably the name of the MAC device we
    487  * should use.
    488  */
    489 static boolean_t
    490 xnbo_hotplug_connected(xnb_t *xnbp)
    491 {
    492 	char *xsname;
    493 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    494 	int need;
    495 
    496 	xsname = xvdi_get_xsname(xnbp->xnb_devinfo);
    497 
    498 	if (xenbus_scanf(XBT_NULL, xsname,
    499 	    "nic", "%s", xnbop->o_link_name) != 0) {
    500 		cmn_err(CE_WARN, "xnbo_connect: "
    501 		    "cannot read nic name from %s", xsname);
    502 		return (B_FALSE);
    503 	}
    504 
    505 	if (xenbus_scanf(XBT_NULL, xsname,
    506 	    "SUNW-need-rx-filter", "%d", &need) != 0)
    507 		need = 0;
    508 	xnbop->o_need_rx_filter = (need > 0);
    509 
    510 	if (xenbus_scanf(XBT_NULL, xsname,
    511 	    "SUNW-need-set-physaddr", "%d", &need) != 0)
    512 		need = 0;
    513 	xnbop->o_need_setphysaddr = (need > 0);
    514 
    515 	return (B_TRUE);
    516 }
    517 
    518 /*
    519  * Find the multicast address `addr', return B_TRUE if it is one that
    520  * we receive. If `remove', remove it from the set received.
    521  */
    522 static boolean_t
    523 xnbo_mcast_find(xnb_t *xnbp, ether_addr_t *addr, boolean_t remove)
    524 {
    525 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    526 	xmca_t *prev, *del, *this;
    527 
    528 	ASSERT(MUTEX_HELD(&xnbp->xnb_state_lock));
    529 	ASSERT(xnbop->o_promiscuous == B_FALSE);
    530 
    531 	prev = del = NULL;
    532 
    533 	this = xnbop->o_mca;
    534 
    535 	while (this != NULL) {
    536 		if (bcmp(&this->addr, addr, sizeof (this->addr)) == 0) {
    537 			del = this;
    538 			if (remove) {
    539 				if (prev == NULL)
    540 					xnbop->o_mca = this->next;
    541 				else
    542 					prev->next = this->next;
    543 			}
    544 			break;
    545 		}
    546 
    547 		prev = this;
    548 		this = this->next;
    549 	}
    550 
    551 	if (del == NULL)
    552 		return (B_FALSE);
    553 
    554 	if (remove) {
    555 		DTRACE_PROBE3(mcast_remove,
    556 		    (char *), "remove",
    557 		    (void *), xnbp,
    558 		    (etheraddr_t *), del->addr);
    559 		mac_multicast_remove(xnbop->o_mch, del->addr);
    560 		kmem_free(del, sizeof (*del));
    561 	}
    562 
    563 	return (B_TRUE);
    564 }
    565 
    566 /*
    567  * Add the multicast address `addr' to the set received.
    568  */
    569 static boolean_t
    570 xnbo_mcast_add(xnb_t *xnbp, ether_addr_t *addr)
    571 {
    572 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    573 	boolean_t r = B_FALSE;
    574 
    575 	ASSERT(xnbop->o_promiscuous == B_FALSE);
    576 
    577 	mutex_enter(&xnbp->xnb_state_lock);
    578 
    579 	if (xnbo_mcast_find(xnbp, addr, B_FALSE)) {
    580 		r = B_TRUE;
    581 	} else if (mac_multicast_add(xnbop->o_mch,
    582 	    (const uint8_t *)addr) == 0) {
    583 		xmca_t *mca;
    584 
    585 		DTRACE_PROBE3(mcast_add,
    586 		    (char *), "add",
    587 		    (void *), xnbp,
    588 		    (etheraddr_t *), addr);
    589 
    590 		mca = kmem_alloc(sizeof (*mca), KM_SLEEP);
    591 		bcopy(addr, &mca->addr, sizeof (mca->addr));
    592 
    593 		mca->next = xnbop->o_mca;
    594 		xnbop->o_mca = mca;
    595 
    596 		r = B_TRUE;
    597 	}
    598 
    599 	mutex_exit(&xnbp->xnb_state_lock);
    600 
    601 	return (r);
    602 }
    603 
    604 /*
    605  * Remove the multicast address `addr' from the set received.
    606  */
    607 static boolean_t
    608 xnbo_mcast_del(xnb_t *xnbp, ether_addr_t *addr)
    609 {
    610 	boolean_t r;
    611 
    612 	mutex_enter(&xnbp->xnb_state_lock);
    613 	r = xnbo_mcast_find(xnbp, addr, B_TRUE);
    614 	mutex_exit(&xnbp->xnb_state_lock);
    615 
    616 	return (r);
    617 }
    618 
    619 static int
    620 xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    621 {
    622 	static xnb_flavour_t flavour = {
    623 		xnbo_to_mac, xnbo_peer_connected, xnbo_peer_disconnected,
    624 		xnbo_hotplug_connected, xnbo_start_connect,
    625 		xnbo_cksum_from_peer, xnbo_cksum_to_peer,
    626 		xnbo_mcast_add, xnbo_mcast_del,
    627 	};
    628 	xnbo_t *xnbop;
    629 
    630 	switch (cmd) {
    631 	case DDI_ATTACH:
    632 		break;
    633 	case DDI_RESUME:
    634 		return (DDI_SUCCESS);
    635 	default:
    636 		return (DDI_FAILURE);
    637 	}
    638 
    639 	xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP);
    640 
    641 	if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) {
    642 		kmem_free(xnbop, sizeof (*xnbop));
    643 		return (DDI_FAILURE);
    644 	}
    645 
    646 	return (DDI_SUCCESS);
    647 }
    648 
    649 static int
    650 xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    651 {
    652 	xnb_t *xnbp = ddi_get_driver_private(dip);
    653 	xnbo_t *xnbop = xnbp->xnb_flavour_data;
    654 
    655 	switch (cmd) {
    656 	case DDI_DETACH:
    657 		break;
    658 	case DDI_SUSPEND:
    659 		return (DDI_SUCCESS);
    660 	default:
    661 		return (DDI_FAILURE);
    662 	}
    663 
    664 	mutex_enter(&xnbp->xnb_tx_lock);
    665 	mutex_enter(&xnbp->xnb_rx_lock);
    666 
    667 	if (!xnbp->xnb_detachable || xnbp->xnb_connected ||
    668 	    (xnbp->xnb_tx_buf_count > 0)) {
    669 		mutex_exit(&xnbp->xnb_rx_lock);
    670 		mutex_exit(&xnbp->xnb_tx_lock);
    671 
    672 		return (DDI_FAILURE);
    673 	}
    674 
    675 	mutex_exit(&xnbp->xnb_rx_lock);
    676 	mutex_exit(&xnbp->xnb_tx_lock);
    677 
    678 	xnbo_close_mac(xnbp);
    679 	kmem_free(xnbop, sizeof (*xnbop));
    680 
    681 	xnb_detach(dip);
    682 
    683 	return (DDI_SUCCESS);
    684 }
    685 
    686 static struct cb_ops cb_ops = {
    687 	nulldev,		/* open */
    688 	nulldev,		/* close */
    689 	nodev,			/* strategy */
    690 	nodev,			/* print */
    691 	nodev,			/* dump */
    692 	nodev,			/* read */
    693 	nodev,			/* write */
    694 	nodev,			/* ioctl */
    695 	nodev,			/* devmap */
    696 	nodev,			/* mmap */
    697 	nodev,			/* segmap */
    698 	nochpoll,		/* poll */
    699 	ddi_prop_op,		/* cb_prop_op */
    700 	0,			/* streamtab  */
    701 	D_NEW | D_MP | D_64BIT	/* Driver compatibility flag */
    702 };
    703 
    704 static struct dev_ops ops = {
    705 	DEVO_REV,		/* devo_rev */
    706 	0,			/* devo_refcnt  */
    707 	nulldev,		/* devo_getinfo */
    708 	nulldev,		/* devo_identify */
    709 	nulldev,		/* devo_probe */
    710 	xnbo_attach,		/* devo_attach */
    711 	xnbo_detach,		/* devo_detach */
    712 	nodev,			/* devo_reset */
    713 	&cb_ops,		/* devo_cb_ops */
    714 	(struct bus_ops *)0,	/* devo_bus_ops */
    715 	NULL,			/* devo_power */
    716 	ddi_quiesce_not_needed,		/* devo_quiesce */
    717 };
    718 
    719 static struct modldrv modldrv = {
    720 	&mod_driverops, "xnbo driver", &ops,
    721 };
    722 
    723 static struct modlinkage modlinkage = {
    724 	MODREV_1, &modldrv, NULL
    725 };
    726 
    727 int
    728 _init(void)
    729 {
    730 	return (mod_install(&modlinkage));
    731 }
    732 
    733 int
    734 _info(struct modinfo *modinfop)
    735 {
    736 	return (mod_info(&modlinkage, modinfop));
    737 }
    738 
    739 int
    740 _fini(void)
    741 {
    742 	return (mod_remove(&modlinkage));
    743 }
    744