15084Sjohnlev /*
25084Sjohnlev * CDDL HEADER START
35084Sjohnlev *
45084Sjohnlev * The contents of this file are subject to the terms of the
55084Sjohnlev * Common Development and Distribution License (the "License").
65084Sjohnlev * You may not use this file except in compliance with the License.
75084Sjohnlev *
85084Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev * See the License for the specific language governing permissions
115084Sjohnlev * and limitations under the License.
125084Sjohnlev *
135084Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev *
195084Sjohnlev * CDDL HEADER END
205084Sjohnlev */
215084Sjohnlev
225084Sjohnlev /*
2311588Sdavid.edmondson@sun.com * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
245084Sjohnlev * Use is subject to license terms.
255084Sjohnlev */
265084Sjohnlev
275084Sjohnlev /*
285084Sjohnlev *
295084Sjohnlev * Copyright (c) 2004 Christian Limpach.
305084Sjohnlev * All rights reserved.
315084Sjohnlev *
325084Sjohnlev * Redistribution and use in source and binary forms, with or without
335084Sjohnlev * modification, are permitted provided that the following conditions
345084Sjohnlev * are met:
355084Sjohnlev * 1. Redistributions of source code must retain the above copyright
365084Sjohnlev * notice, this list of conditions and the following disclaimer.
375084Sjohnlev * 2. Redistributions in binary form must reproduce the above copyright
385084Sjohnlev * notice, this list of conditions and the following disclaimer in the
395084Sjohnlev * documentation and/or other materials provided with the distribution.
405084Sjohnlev * 3. This section intentionally left blank.
415084Sjohnlev * 4. The name of the author may not be used to endorse or promote products
425084Sjohnlev * derived from this software without specific prior written permission.
435084Sjohnlev *
445084Sjohnlev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
455084Sjohnlev * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
465084Sjohnlev * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
475084Sjohnlev * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
485084Sjohnlev * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
495084Sjohnlev * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
505084Sjohnlev * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
515084Sjohnlev * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
525084Sjohnlev * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
535084Sjohnlev * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
545084Sjohnlev */
555084Sjohnlev /*
565084Sjohnlev * Section 3 of the above license was updated in response to bug 6379571.
575084Sjohnlev */
585084Sjohnlev
595084Sjohnlev /*
6010958Sdme@sun.com * xnf.c - GLDv3 network driver for domU.
6110958Sdme@sun.com */
6210958Sdme@sun.com
6310958Sdme@sun.com /*
6410958Sdme@sun.com * This driver uses four per-instance locks:
6510958Sdme@sun.com *
6610958Sdme@sun.com * xnf_gref_lock:
6710958Sdme@sun.com *
6810958Sdme@sun.com * Protects access to the grant reference list stored in
6910958Sdme@sun.com * xnf_gref_head. Grant references should be acquired and released
7010958Sdme@sun.com * using gref_get() and gref_put() respectively.
7110958Sdme@sun.com *
7210958Sdme@sun.com * xnf_schedlock:
7310958Sdme@sun.com *
7410958Sdme@sun.com * Protects:
7510958Sdme@sun.com * xnf_need_sched - used to record that a previous transmit attempt
7610958Sdme@sun.com * failed (and consequently it will be necessary to call
7710958Sdme@sun.com * mac_tx_update() when transmit resources are available).
7810958Sdme@sun.com * xnf_pending_multicast - the number of multicast requests that
7910958Sdme@sun.com * have been submitted to the backend for which we have not
8010958Sdme@sun.com * processed responses.
8110958Sdme@sun.com *
8210958Sdme@sun.com * xnf_txlock:
8310958Sdme@sun.com *
8410958Sdme@sun.com * Protects the transmit ring (xnf_tx_ring) and associated
8510958Sdme@sun.com * structures (notably xnf_tx_pkt_id and xnf_tx_pkt_id_head).
8610958Sdme@sun.com *
8710958Sdme@sun.com * xnf_rxlock:
8810958Sdme@sun.com *
8910958Sdme@sun.com * Protects the receive ring (xnf_rx_ring) and associated
9010958Sdme@sun.com * structures (notably xnf_rx_pkt_info).
9110958Sdme@sun.com *
9210958Sdme@sun.com * If driver-global state that affects both the transmit and receive
9310958Sdme@sun.com * rings is manipulated, both xnf_txlock and xnf_rxlock should be
9410958Sdme@sun.com * held, in that order.
9510958Sdme@sun.com *
9610958Sdme@sun.com * xnf_schedlock is acquired both whilst holding xnf_txlock and
9710958Sdme@sun.com * without. It should always be acquired after xnf_txlock if both are
9810958Sdme@sun.com * held.
9910958Sdme@sun.com *
10010958Sdme@sun.com * Notes:
10110958Sdme@sun.com * - atomic_add_64() is used to manipulate counters where we require
10210958Sdme@sun.com * accuracy. For counters intended only for observation by humans,
10310958Sdme@sun.com * post increment/decrement are used instead.
1045084Sjohnlev */
1055084Sjohnlev
1065084Sjohnlev #include <sys/types.h>
1075084Sjohnlev #include <sys/errno.h>
1085084Sjohnlev #include <sys/param.h>
1095084Sjohnlev #include <sys/sysmacros.h>
1105084Sjohnlev #include <sys/systm.h>
1115084Sjohnlev #include <sys/stream.h>
1125084Sjohnlev #include <sys/strsubr.h>
11310958Sdme@sun.com #include <sys/strsun.h>
1145084Sjohnlev #include <sys/conf.h>
1155084Sjohnlev #include <sys/ddi.h>
1165084Sjohnlev #include <sys/devops.h>
1175084Sjohnlev #include <sys/sunddi.h>
1185084Sjohnlev #include <sys/sunndi.h>
1195084Sjohnlev #include <sys/dlpi.h>
1205084Sjohnlev #include <sys/ethernet.h>
1215084Sjohnlev #include <sys/strsun.h>
1225084Sjohnlev #include <sys/pattr.h>
1235084Sjohnlev #include <inet/ip.h>
1247351Sdme@sun.com #include <inet/ip_impl.h>
1257351Sdme@sun.com #include <sys/gld.h>
1265084Sjohnlev #include <sys/modctl.h>
1278275SEric Cheng #include <sys/mac_provider.h>
1285084Sjohnlev #include <sys/mac_ether.h>
1295084Sjohnlev #include <sys/bootinfo.h>
1305084Sjohnlev #include <sys/mach_mmu.h>
1315741Smrj #ifdef XPV_HVM_DRIVER
1325741Smrj #include <sys/xpv_support.h>
1335741Smrj #include <sys/hypervisor.h>
1345741Smrj #else
1355741Smrj #include <sys/hypervisor.h>
1365084Sjohnlev #include <sys/evtchn_impl.h>
1375084Sjohnlev #include <sys/balloon_impl.h>
1385741Smrj #endif
1395741Smrj #include <xen/public/io/netif.h>
1405741Smrj #include <sys/gnttab.h>
1415084Sjohnlev #include <xen/sys/xendev.h>
1425741Smrj #include <sys/sdt.h>
14310958Sdme@sun.com #include <sys/note.h>
14410958Sdme@sun.com #include <sys/debug.h>
1455741Smrj
1465741Smrj #include <io/xnf.h>
1475741Smrj
1485084Sjohnlev #if defined(DEBUG) || defined(__lint)
1495084Sjohnlev #define XNF_DEBUG
15010958Sdme@sun.com #endif
15110958Sdme@sun.com
15210958Sdme@sun.com #ifdef XNF_DEBUG
15310958Sdme@sun.com int xnf_debug = 0;
15410958Sdme@sun.com xnf_t *xnf_debug_instance = NULL;
1555084Sjohnlev #endif
1565084Sjohnlev
1575084Sjohnlev /*
1585084Sjohnlev * On a 32 bit PAE system physical and machine addresses are larger
1595084Sjohnlev * than 32 bits. ddi_btop() on such systems take an unsigned long
1605084Sjohnlev * argument, and so addresses above 4G are truncated before ddi_btop()
1615084Sjohnlev * gets to see them. To avoid this, code the shift operation here.
1625084Sjohnlev */
1635084Sjohnlev #define xnf_btop(addr) ((addr) >> PAGESHIFT)
1645084Sjohnlev
16510958Sdme@sun.com unsigned int xnf_max_tx_frags = 1;
16610958Sdme@sun.com
16710958Sdme@sun.com /*
16810958Sdme@sun.com * Should we use the multicast control feature if the backend provides
16910958Sdme@sun.com * it?
17010958Sdme@sun.com */
17110958Sdme@sun.com boolean_t xnf_multicast_control = B_TRUE;
1725741Smrj
1735084Sjohnlev /*
17410958Sdme@sun.com * Received packets below this size are copied to a new streams buffer
17510958Sdme@sun.com * rather than being desballoc'ed.
17610958Sdme@sun.com *
17710958Sdme@sun.com * This value is chosen to accommodate traffic where there are a large
17810958Sdme@sun.com * number of small packets. For data showing a typical distribution,
17910958Sdme@sun.com * see:
18010958Sdme@sun.com *
18110958Sdme@sun.com * Sinha07a:
18210958Sdme@sun.com * Rishi Sinha, Christos Papadopoulos, and John
18310958Sdme@sun.com * Heidemann. Internet Packet Size Distributions: Some
18410958Sdme@sun.com * Observations. Technical Report ISI-TR-2007-643,
18510958Sdme@sun.com * USC/Information Sciences Institute, May, 2007. Orignally
18610958Sdme@sun.com * released October 2005 as web page
18710958Sdme@sun.com * http://netweb.usc.edu/~sinha/pkt-sizes/.
18810958Sdme@sun.com * <http://www.isi.edu/~johnh/PAPERS/Sinha07a.html>.
1895084Sjohnlev */
19010958Sdme@sun.com size_t xnf_rx_copy_limit = 64;
19110958Sdme@sun.com
19210958Sdme@sun.com #define INVALID_GRANT_HANDLE ((grant_handle_t)-1)
19310958Sdme@sun.com #define INVALID_GRANT_REF ((grant_ref_t)-1)
19410958Sdme@sun.com #define INVALID_TX_ID ((uint16_t)-1)
19510958Sdme@sun.com
19610958Sdme@sun.com #define TX_ID_TO_TXID(p, id) (&((p)->xnf_tx_pkt_id[(id)]))
19710958Sdme@sun.com #define TX_ID_VALID(i) (((i) != INVALID_TX_ID) && ((i) < NET_TX_RING_SIZE))
1985084Sjohnlev
1995084Sjohnlev /* Required system entry points */
2005084Sjohnlev static int xnf_attach(dev_info_t *, ddi_attach_cmd_t);
2015084Sjohnlev static int xnf_detach(dev_info_t *, ddi_detach_cmd_t);
2025084Sjohnlev
2035084Sjohnlev /* Required driver entry points for Nemo */
2045084Sjohnlev static int xnf_start(void *);
2055084Sjohnlev static void xnf_stop(void *);
2065084Sjohnlev static int xnf_set_mac_addr(void *, const uint8_t *);
2075084Sjohnlev static int xnf_set_multicast(void *, boolean_t, const uint8_t *);
2085084Sjohnlev static int xnf_set_promiscuous(void *, boolean_t);
2095084Sjohnlev static mblk_t *xnf_send(void *, mblk_t *);
2105084Sjohnlev static uint_t xnf_intr(caddr_t);
2115084Sjohnlev static int xnf_stat(void *, uint_t, uint64_t *);
2125084Sjohnlev static boolean_t xnf_getcapab(void *, mac_capab_t, void *);
2135084Sjohnlev
2145084Sjohnlev /* Driver private functions */
2155084Sjohnlev static int xnf_alloc_dma_resources(xnf_t *);
2165084Sjohnlev static void xnf_release_dma_resources(xnf_t *);
2175084Sjohnlev static void xnf_release_mblks(xnf_t *);
21810958Sdme@sun.com
21910958Sdme@sun.com static int xnf_buf_constructor(void *, void *, int);
22010958Sdme@sun.com static void xnf_buf_destructor(void *, void *);
22110958Sdme@sun.com static xnf_buf_t *xnf_buf_get(xnf_t *, int, boolean_t);
22210958Sdme@sun.com #pragma inline(xnf_buf_get)
22310958Sdme@sun.com static void xnf_buf_put(xnf_t *, xnf_buf_t *, boolean_t);
22410958Sdme@sun.com #pragma inline(xnf_buf_put)
22510958Sdme@sun.com static void xnf_buf_refresh(xnf_buf_t *);
22610958Sdme@sun.com #pragma inline(xnf_buf_refresh)
22710958Sdme@sun.com static void xnf_buf_recycle(xnf_buf_t *);
22810958Sdme@sun.com
22910958Sdme@sun.com static int xnf_tx_buf_constructor(void *, void *, int);
23010958Sdme@sun.com static void xnf_tx_buf_destructor(void *, void *);
23110958Sdme@sun.com
23210958Sdme@sun.com static grant_ref_t gref_get(xnf_t *);
23310958Sdme@sun.com #pragma inline(gref_get)
23410958Sdme@sun.com static void gref_put(xnf_t *, grant_ref_t);
23510958Sdme@sun.com #pragma inline(gref_put)
23610958Sdme@sun.com
23710958Sdme@sun.com static xnf_txid_t *txid_get(xnf_t *);
23810958Sdme@sun.com #pragma inline(txid_get)
23910958Sdme@sun.com static void txid_put(xnf_t *, xnf_txid_t *);
24010958Sdme@sun.com #pragma inline(txid_put)
24110958Sdme@sun.com
2425084Sjohnlev void xnf_send_driver_status(int, int);
24310958Sdme@sun.com static void xnf_rxbuf_hang(xnf_t *, xnf_buf_t *);
24410958Sdme@sun.com static int xnf_tx_clean_ring(xnf_t *);
2455084Sjohnlev static void oe_state_change(dev_info_t *, ddi_eventcookie_t,
2465084Sjohnlev void *, void *);
24710958Sdme@sun.com static boolean_t xnf_kstat_init(xnf_t *);
24810958Sdme@sun.com static void xnf_rx_collect(xnf_t *);
24910958Sdme@sun.com
2505084Sjohnlev static mac_callbacks_t xnf_callbacks = {
25110958Sdme@sun.com MC_GETCAPAB,
2525084Sjohnlev xnf_stat,
2535084Sjohnlev xnf_start,
2545084Sjohnlev xnf_stop,
2555084Sjohnlev xnf_set_promiscuous,
2565084Sjohnlev xnf_set_multicast,
2575084Sjohnlev xnf_set_mac_addr,
2585084Sjohnlev xnf_send,
25910958Sdme@sun.com NULL,
260*11878SVenu.Iyer@Sun.COM NULL,
2615084Sjohnlev xnf_getcapab
2625084Sjohnlev };
2635084Sjohnlev
2645084Sjohnlev /* DMA attributes for network ring buffer */
2655084Sjohnlev static ddi_dma_attr_t ringbuf_dma_attr = {
2665084Sjohnlev DMA_ATTR_V0, /* version of this structure */
2675084Sjohnlev 0, /* lowest usable address */
2685084Sjohnlev 0xffffffffffffffffULL, /* highest usable address */
2695084Sjohnlev 0x7fffffff, /* maximum DMAable byte count */
2705084Sjohnlev MMU_PAGESIZE, /* alignment in bytes */
2715084Sjohnlev 0x7ff, /* bitmap of burst sizes */
2725084Sjohnlev 1, /* minimum transfer */
2735084Sjohnlev 0xffffffffU, /* maximum transfer */
2745084Sjohnlev 0xffffffffffffffffULL, /* maximum segment length */
2755084Sjohnlev 1, /* maximum number of segments */
2765084Sjohnlev 1, /* granularity */
2775084Sjohnlev 0, /* flags (reserved) */
2785084Sjohnlev };
2795084Sjohnlev
28010958Sdme@sun.com /* DMA attributes for transmit and receive data */
28110958Sdme@sun.com static ddi_dma_attr_t buf_dma_attr = {
2825084Sjohnlev DMA_ATTR_V0, /* version of this structure */
2835084Sjohnlev 0, /* lowest usable address */
2845084Sjohnlev 0xffffffffffffffffULL, /* highest usable address */
2855084Sjohnlev 0x7fffffff, /* maximum DMAable byte count */
2865084Sjohnlev MMU_PAGESIZE, /* alignment in bytes */
2875084Sjohnlev 0x7ff, /* bitmap of burst sizes */
2885084Sjohnlev 1, /* minimum transfer */
2895084Sjohnlev 0xffffffffU, /* maximum transfer */
2905084Sjohnlev 0xffffffffffffffffULL, /* maximum segment length */
2915084Sjohnlev 1, /* maximum number of segments */
2925084Sjohnlev 1, /* granularity */
2935084Sjohnlev 0, /* flags (reserved) */
2945084Sjohnlev };
2955084Sjohnlev
2965084Sjohnlev /* DMA access attributes for registers and descriptors */
2975084Sjohnlev static ddi_device_acc_attr_t accattr = {
2985084Sjohnlev DDI_DEVICE_ATTR_V0,
2995084Sjohnlev DDI_STRUCTURE_LE_ACC, /* This is a little-endian device */
3005084Sjohnlev DDI_STRICTORDER_ACC
3015084Sjohnlev };
3025084Sjohnlev
3035084Sjohnlev /* DMA access attributes for data: NOT to be byte swapped. */
3045084Sjohnlev static ddi_device_acc_attr_t data_accattr = {
3055084Sjohnlev DDI_DEVICE_ATTR_V0,
3065084Sjohnlev DDI_NEVERSWAP_ACC,
3075084Sjohnlev DDI_STRICTORDER_ACC
3085084Sjohnlev };
3095084Sjohnlev
3105084Sjohnlev DDI_DEFINE_STREAM_OPS(xnf_dev_ops, nulldev, nulldev, xnf_attach, xnf_detach,
3117656SSherry.Moore@Sun.COM nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
3125084Sjohnlev
3135084Sjohnlev static struct modldrv xnf_modldrv = {
3147351Sdme@sun.com &mod_driverops,
3157351Sdme@sun.com "Virtual Ethernet driver",
3167351Sdme@sun.com &xnf_dev_ops
3175084Sjohnlev };
3185084Sjohnlev
3195084Sjohnlev static struct modlinkage modlinkage = {
3205084Sjohnlev MODREV_1, &xnf_modldrv, NULL
3215084Sjohnlev };
3225084Sjohnlev
3235084Sjohnlev int
_init(void)3245084Sjohnlev _init(void)
3255084Sjohnlev {
3265084Sjohnlev int r;
3275084Sjohnlev
3285084Sjohnlev mac_init_ops(&xnf_dev_ops, "xnf");
3295084Sjohnlev r = mod_install(&modlinkage);
3305084Sjohnlev if (r != DDI_SUCCESS)
3315084Sjohnlev mac_fini_ops(&xnf_dev_ops);
3325084Sjohnlev
3335084Sjohnlev return (r);
3345084Sjohnlev }
3355084Sjohnlev
3365084Sjohnlev int
_fini(void)3375084Sjohnlev _fini(void)
3385084Sjohnlev {
33910958Sdme@sun.com return (EBUSY); /* XXPV should be removable */
3405084Sjohnlev }
3415084Sjohnlev
3425084Sjohnlev int
_info(struct modinfo * modinfop)3435084Sjohnlev _info(struct modinfo *modinfop)
3445084Sjohnlev {
3455084Sjohnlev return (mod_info(&modlinkage, modinfop));
3465084Sjohnlev }
3475084Sjohnlev
34810958Sdme@sun.com /*
34910958Sdme@sun.com * Acquire a grant reference.
35010958Sdme@sun.com */
35110958Sdme@sun.com static grant_ref_t
gref_get(xnf_t * xnfp)35210958Sdme@sun.com gref_get(xnf_t *xnfp)
35310958Sdme@sun.com {
35410958Sdme@sun.com grant_ref_t gref;
35510958Sdme@sun.com
35610958Sdme@sun.com mutex_enter(&xnfp->xnf_gref_lock);
35710958Sdme@sun.com
35810958Sdme@sun.com do {
35910958Sdme@sun.com gref = gnttab_claim_grant_reference(&xnfp->xnf_gref_head);
36010958Sdme@sun.com
36110958Sdme@sun.com } while ((gref == INVALID_GRANT_REF) &&
36210958Sdme@sun.com (gnttab_alloc_grant_references(16, &xnfp->xnf_gref_head) == 0));
36310958Sdme@sun.com
36410958Sdme@sun.com mutex_exit(&xnfp->xnf_gref_lock);
36510958Sdme@sun.com
36610958Sdme@sun.com if (gref == INVALID_GRANT_REF) {
36710958Sdme@sun.com xnfp->xnf_stat_gref_failure++;
36810958Sdme@sun.com } else {
36910958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_gref_outstanding, 1);
37010958Sdme@sun.com if (xnfp->xnf_stat_gref_outstanding > xnfp->xnf_stat_gref_peak)
37110958Sdme@sun.com xnfp->xnf_stat_gref_peak =
37210958Sdme@sun.com xnfp->xnf_stat_gref_outstanding;
37310958Sdme@sun.com }
37410958Sdme@sun.com
37510958Sdme@sun.com return (gref);
37610958Sdme@sun.com }
37710958Sdme@sun.com
37810958Sdme@sun.com /*
37910958Sdme@sun.com * Release a grant reference.
38010958Sdme@sun.com */
38110958Sdme@sun.com static void
gref_put(xnf_t * xnfp,grant_ref_t gref)38210958Sdme@sun.com gref_put(xnf_t *xnfp, grant_ref_t gref)
38310958Sdme@sun.com {
38410958Sdme@sun.com ASSERT(gref != INVALID_GRANT_REF);
38510958Sdme@sun.com
38610958Sdme@sun.com mutex_enter(&xnfp->xnf_gref_lock);
38710958Sdme@sun.com gnttab_release_grant_reference(&xnfp->xnf_gref_head, gref);
38810958Sdme@sun.com mutex_exit(&xnfp->xnf_gref_lock);
38910958Sdme@sun.com
39010958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_gref_outstanding, -1);
39110958Sdme@sun.com }
39210958Sdme@sun.com
39310958Sdme@sun.com /*
39410958Sdme@sun.com * Acquire a transmit id.
39510958Sdme@sun.com */
39610958Sdme@sun.com static xnf_txid_t *
txid_get(xnf_t * xnfp)39710958Sdme@sun.com txid_get(xnf_t *xnfp)
39810958Sdme@sun.com {
39910958Sdme@sun.com xnf_txid_t *tidp;
40010958Sdme@sun.com
40110958Sdme@sun.com ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
40210958Sdme@sun.com
40310958Sdme@sun.com if (xnfp->xnf_tx_pkt_id_head == INVALID_TX_ID)
40410958Sdme@sun.com return (NULL);
40510958Sdme@sun.com
40610958Sdme@sun.com ASSERT(TX_ID_VALID(xnfp->xnf_tx_pkt_id_head));
40710958Sdme@sun.com
40810958Sdme@sun.com tidp = TX_ID_TO_TXID(xnfp, xnfp->xnf_tx_pkt_id_head);
40910958Sdme@sun.com xnfp->xnf_tx_pkt_id_head = tidp->next;
41010958Sdme@sun.com tidp->next = INVALID_TX_ID;
41110958Sdme@sun.com
41210958Sdme@sun.com ASSERT(tidp->txbuf == NULL);
41310958Sdme@sun.com
41410958Sdme@sun.com return (tidp);
41510958Sdme@sun.com }
41610958Sdme@sun.com
41710958Sdme@sun.com /*
41810958Sdme@sun.com * Release a transmit id.
41910958Sdme@sun.com */
42010958Sdme@sun.com static void
txid_put(xnf_t * xnfp,xnf_txid_t * tidp)42110958Sdme@sun.com txid_put(xnf_t *xnfp, xnf_txid_t *tidp)
42210958Sdme@sun.com {
42310958Sdme@sun.com ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
42410958Sdme@sun.com ASSERT(TX_ID_VALID(tidp->id));
42510958Sdme@sun.com ASSERT(tidp->next == INVALID_TX_ID);
42610958Sdme@sun.com
42710958Sdme@sun.com tidp->txbuf = NULL;
42810958Sdme@sun.com tidp->next = xnfp->xnf_tx_pkt_id_head;
42910958Sdme@sun.com xnfp->xnf_tx_pkt_id_head = tidp->id;
43010958Sdme@sun.com }
43110958Sdme@sun.com
43210958Sdme@sun.com /*
43310958Sdme@sun.com * Get `wanted' slots in the transmit ring, waiting for at least that
43410958Sdme@sun.com * number if `wait' is B_TRUE. Force the ring to be cleaned by setting
43510958Sdme@sun.com * `wanted' to zero.
43610958Sdme@sun.com *
43710958Sdme@sun.com * Return the number of slots available.
43810958Sdme@sun.com */
43910958Sdme@sun.com static int
tx_slots_get(xnf_t * xnfp,int wanted,boolean_t wait)44010958Sdme@sun.com tx_slots_get(xnf_t *xnfp, int wanted, boolean_t wait)
44110958Sdme@sun.com {
44210958Sdme@sun.com int slotsfree;
44310958Sdme@sun.com boolean_t forced_clean = (wanted == 0);
44410958Sdme@sun.com
44510958Sdme@sun.com ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
44610958Sdme@sun.com
44710958Sdme@sun.com /* LINTED: constant in conditional context */
44810958Sdme@sun.com while (B_TRUE) {
44910958Sdme@sun.com slotsfree = RING_FREE_REQUESTS(&xnfp->xnf_tx_ring);
45010958Sdme@sun.com
45110958Sdme@sun.com if ((slotsfree < wanted) || forced_clean)
45210958Sdme@sun.com slotsfree = xnf_tx_clean_ring(xnfp);
45310958Sdme@sun.com
45410958Sdme@sun.com /*
45510958Sdme@sun.com * If there are more than we need free, tell other
45610958Sdme@sun.com * people to come looking again. We hold txlock, so we
45710958Sdme@sun.com * are able to take our slots before anyone else runs.
45810958Sdme@sun.com */
45910958Sdme@sun.com if (slotsfree > wanted)
46010958Sdme@sun.com cv_broadcast(&xnfp->xnf_cv_tx_slots);
46110958Sdme@sun.com
46210958Sdme@sun.com if (slotsfree >= wanted)
46310958Sdme@sun.com break;
46410958Sdme@sun.com
46510958Sdme@sun.com if (!wait)
46610958Sdme@sun.com break;
46710958Sdme@sun.com
46810958Sdme@sun.com cv_wait(&xnfp->xnf_cv_tx_slots, &xnfp->xnf_txlock);
46910958Sdme@sun.com }
47010958Sdme@sun.com
47110958Sdme@sun.com ASSERT(slotsfree <= RING_SIZE(&(xnfp->xnf_tx_ring)));
47210958Sdme@sun.com
47310958Sdme@sun.com return (slotsfree);
47410958Sdme@sun.com }
47510958Sdme@sun.com
4765084Sjohnlev static int
xnf_setup_rings(xnf_t * xnfp)4775084Sjohnlev xnf_setup_rings(xnf_t *xnfp)
4785084Sjohnlev {
47910958Sdme@sun.com domid_t oeid;
48010958Sdme@sun.com struct xenbus_device *xsd;
4815084Sjohnlev RING_IDX i;
48210958Sdme@sun.com int err;
48310958Sdme@sun.com xnf_txid_t *tidp;
48410958Sdme@sun.com xnf_buf_t **bdescp;
4855084Sjohnlev
4865741Smrj oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
4875741Smrj xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
4885084Sjohnlev
48910958Sdme@sun.com if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
4905741Smrj gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
4915084Sjohnlev
4925084Sjohnlev err = gnttab_grant_foreign_access(oeid,
4935741Smrj xnf_btop(pa_to_ma(xnfp->xnf_tx_ring_phys_addr)), 0);
4945084Sjohnlev if (err <= 0) {
4955084Sjohnlev err = -err;
4965084Sjohnlev xenbus_dev_error(xsd, err, "granting access to tx ring page");
4975084Sjohnlev goto out;
4985084Sjohnlev }
4995741Smrj xnfp->xnf_tx_ring_ref = (grant_ref_t)err;
5005084Sjohnlev
50110958Sdme@sun.com if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
5025741Smrj gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
5035084Sjohnlev
5045084Sjohnlev err = gnttab_grant_foreign_access(oeid,
5055741Smrj xnf_btop(pa_to_ma(xnfp->xnf_rx_ring_phys_addr)), 0);
5065084Sjohnlev if (err <= 0) {
5075084Sjohnlev err = -err;
5085084Sjohnlev xenbus_dev_error(xsd, err, "granting access to rx ring page");
5095084Sjohnlev goto out;
5105084Sjohnlev }
5115741Smrj xnfp->xnf_rx_ring_ref = (grant_ref_t)err;
5125084Sjohnlev
51310958Sdme@sun.com mutex_enter(&xnfp->xnf_txlock);
5145084Sjohnlev
5155084Sjohnlev /*
51610958Sdme@sun.com * Setup/cleanup the TX ring. Note that this can lose packets
51710958Sdme@sun.com * after a resume, but we expect to stagger on.
5185084Sjohnlev */
51910958Sdme@sun.com xnfp->xnf_tx_pkt_id_head = INVALID_TX_ID; /* I.e. emtpy list. */
52010958Sdme@sun.com for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
52110958Sdme@sun.com i < NET_TX_RING_SIZE;
52210958Sdme@sun.com i++, tidp++) {
52310958Sdme@sun.com xnf_txbuf_t *txp;
52410958Sdme@sun.com
52510958Sdme@sun.com tidp->id = i;
52610958Sdme@sun.com
52710958Sdme@sun.com txp = tidp->txbuf;
52810958Sdme@sun.com if (txp == NULL) {
52910958Sdme@sun.com tidp->next = INVALID_TX_ID; /* Appease txid_put(). */
53010958Sdme@sun.com txid_put(xnfp, tidp);
5315084Sjohnlev continue;
5325084Sjohnlev }
5335084Sjohnlev
53410958Sdme@sun.com ASSERT(txp->tx_txreq.gref != INVALID_GRANT_REF);
53510958Sdme@sun.com ASSERT(txp->tx_mp != NULL);
53610958Sdme@sun.com
53710958Sdme@sun.com switch (txp->tx_type) {
53810958Sdme@sun.com case TX_DATA:
53910958Sdme@sun.com VERIFY(gnttab_query_foreign_access(txp->tx_txreq.gref)
54010958Sdme@sun.com == 0);
54110958Sdme@sun.com
54210958Sdme@sun.com if (txp->tx_bdesc == NULL) {
54310958Sdme@sun.com (void) gnttab_end_foreign_access_ref(
54410958Sdme@sun.com txp->tx_txreq.gref, 1);
54510958Sdme@sun.com gref_put(xnfp, txp->tx_txreq.gref);
54610958Sdme@sun.com (void) ddi_dma_unbind_handle(
54710958Sdme@sun.com txp->tx_dma_handle);
54810958Sdme@sun.com } else {
54910958Sdme@sun.com xnf_buf_put(xnfp, txp->tx_bdesc, B_TRUE);
55010958Sdme@sun.com }
55110958Sdme@sun.com
55210958Sdme@sun.com freemsg(txp->tx_mp);
55310958Sdme@sun.com txid_put(xnfp, tidp);
55410958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
55510958Sdme@sun.com
55610958Sdme@sun.com break;
55710958Sdme@sun.com
55810958Sdme@sun.com case TX_MCAST_REQ:
55910958Sdme@sun.com txp->tx_type = TX_MCAST_RSP;
56010958Sdme@sun.com txp->tx_status = NETIF_RSP_DROPPED;
56110958Sdme@sun.com cv_broadcast(&xnfp->xnf_cv_multicast);
56210958Sdme@sun.com
56310958Sdme@sun.com /*
56410958Sdme@sun.com * The request consumed two slots in the ring,
56510958Sdme@sun.com * yet only a single xnf_txid_t is used. Step
56610958Sdme@sun.com * over the empty slot.
56710958Sdme@sun.com */
56810958Sdme@sun.com i++;
56910958Sdme@sun.com ASSERT(i < NET_TX_RING_SIZE);
57010958Sdme@sun.com
57110958Sdme@sun.com break;
57210958Sdme@sun.com
57310958Sdme@sun.com case TX_MCAST_RSP:
57410958Sdme@sun.com break;
5755084Sjohnlev }
5765084Sjohnlev }
5775084Sjohnlev
5787351Sdme@sun.com /* LINTED: constant in conditional context */
5797351Sdme@sun.com SHARED_RING_INIT(xnfp->xnf_tx_ring.sring);
58010958Sdme@sun.com /* LINTED: constant in conditional context */
58110958Sdme@sun.com FRONT_RING_INIT(&xnfp->xnf_tx_ring,
58210958Sdme@sun.com xnfp->xnf_tx_ring.sring, PAGESIZE);
5835084Sjohnlev
5845741Smrj mutex_exit(&xnfp->xnf_txlock);
5855084Sjohnlev
58610958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
58710958Sdme@sun.com
5885084Sjohnlev /*
58910958Sdme@sun.com * Clean out any buffers currently posted to the receive ring
59010958Sdme@sun.com * before we reset it.
5915084Sjohnlev */
59210958Sdme@sun.com for (i = 0, bdescp = &xnfp->xnf_rx_pkt_info[0];
59310958Sdme@sun.com i < NET_RX_RING_SIZE;
59410958Sdme@sun.com i++, bdescp++) {
59510958Sdme@sun.com if (*bdescp != NULL) {
59610958Sdme@sun.com xnf_buf_put(xnfp, *bdescp, B_FALSE);
59710958Sdme@sun.com *bdescp = NULL;
5985084Sjohnlev }
5995084Sjohnlev }
6005741Smrj
6017351Sdme@sun.com /* LINTED: constant in conditional context */
6027351Sdme@sun.com SHARED_RING_INIT(xnfp->xnf_rx_ring.sring);
60310958Sdme@sun.com /* LINTED: constant in conditional context */
60410958Sdme@sun.com FRONT_RING_INIT(&xnfp->xnf_rx_ring,
60510958Sdme@sun.com xnfp->xnf_rx_ring.sring, PAGESIZE);
60610958Sdme@sun.com
60710958Sdme@sun.com /*
60810958Sdme@sun.com * Fill the ring with buffers.
60910958Sdme@sun.com */
6105084Sjohnlev for (i = 0; i < NET_RX_RING_SIZE; i++) {
61110958Sdme@sun.com xnf_buf_t *bdesc;
61210958Sdme@sun.com
61310958Sdme@sun.com bdesc = xnf_buf_get(xnfp, KM_SLEEP, B_FALSE);
61410958Sdme@sun.com VERIFY(bdesc != NULL);
61510958Sdme@sun.com xnf_rxbuf_hang(xnfp, bdesc);
6165084Sjohnlev }
61710958Sdme@sun.com
6185084Sjohnlev /* LINTED: constant in conditional context */
6195741Smrj RING_PUSH_REQUESTS(&xnfp->xnf_rx_ring);
6205084Sjohnlev
62110958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
6225084Sjohnlev
6235084Sjohnlev return (0);
6245084Sjohnlev
6255084Sjohnlev out:
62610958Sdme@sun.com if (xnfp->xnf_tx_ring_ref != INVALID_GRANT_REF)
6275741Smrj gnttab_end_foreign_access(xnfp->xnf_tx_ring_ref, 0, 0);
62810958Sdme@sun.com xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
62910958Sdme@sun.com
63010958Sdme@sun.com if (xnfp->xnf_rx_ring_ref != INVALID_GRANT_REF)
6315741Smrj gnttab_end_foreign_access(xnfp->xnf_rx_ring_ref, 0, 0);
63210958Sdme@sun.com xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
6335084Sjohnlev
6345084Sjohnlev return (err);
6355084Sjohnlev }
6365084Sjohnlev
6375084Sjohnlev /*
6385084Sjohnlev * Connect driver to back end, called to set up communication with
6395084Sjohnlev * back end driver both initially and on resume after restore/migrate.
6405084Sjohnlev */
6415084Sjohnlev void
xnf_be_connect(xnf_t * xnfp)6425084Sjohnlev xnf_be_connect(xnf_t *xnfp)
6435084Sjohnlev {
6445084Sjohnlev const char *message;
6455084Sjohnlev xenbus_transaction_t xbt;
6465741Smrj struct xenbus_device *xsd;
6475084Sjohnlev char *xsname;
6486899Scz147101 int err;
6495084Sjohnlev
6505741Smrj ASSERT(!xnfp->xnf_connected);
6515084Sjohnlev
6525741Smrj xsd = xvdi_get_xsd(xnfp->xnf_devinfo);
6535741Smrj xsname = xvdi_get_xsname(xnfp->xnf_devinfo);
6545084Sjohnlev
6555084Sjohnlev err = xnf_setup_rings(xnfp);
6565084Sjohnlev if (err != 0) {
6575084Sjohnlev cmn_err(CE_WARN, "failed to set up tx/rx rings");
6585084Sjohnlev xenbus_dev_error(xsd, err, "setting up ring");
6595084Sjohnlev return;
6605084Sjohnlev }
6615084Sjohnlev
6625084Sjohnlev again:
6635084Sjohnlev err = xenbus_transaction_start(&xbt);
6645084Sjohnlev if (err != 0) {
6655084Sjohnlev xenbus_dev_error(xsd, EIO, "starting transaction");
6665084Sjohnlev return;
6675084Sjohnlev }
6685084Sjohnlev
6695084Sjohnlev err = xenbus_printf(xbt, xsname, "tx-ring-ref", "%u",
6705741Smrj xnfp->xnf_tx_ring_ref);
6715084Sjohnlev if (err != 0) {
6725084Sjohnlev message = "writing tx ring-ref";
6735084Sjohnlev goto abort_transaction;
6745084Sjohnlev }
6755084Sjohnlev
6765084Sjohnlev err = xenbus_printf(xbt, xsname, "rx-ring-ref", "%u",
6775741Smrj xnfp->xnf_rx_ring_ref);
6785084Sjohnlev if (err != 0) {
6795084Sjohnlev message = "writing rx ring-ref";
6805084Sjohnlev goto abort_transaction;
6815084Sjohnlev }
6825084Sjohnlev
6835741Smrj err = xenbus_printf(xbt, xsname, "event-channel", "%u",
6845741Smrj xnfp->xnf_evtchn);
6855084Sjohnlev if (err != 0) {
6865084Sjohnlev message = "writing event-channel";
6875084Sjohnlev goto abort_transaction;
6885084Sjohnlev }
6895084Sjohnlev
6905084Sjohnlev err = xenbus_printf(xbt, xsname, "feature-rx-notify", "%d", 1);
6915084Sjohnlev if (err != 0) {
6925084Sjohnlev message = "writing feature-rx-notify";
6935084Sjohnlev goto abort_transaction;
6945084Sjohnlev }
6955084Sjohnlev
69610958Sdme@sun.com err = xenbus_printf(xbt, xsname, "request-rx-copy", "%d", 1);
6975741Smrj if (err != 0) {
6985741Smrj message = "writing request-rx-copy";
6995741Smrj goto abort_transaction;
7005741Smrj }
7015084Sjohnlev
70210958Sdme@sun.com if (xnfp->xnf_be_mcast_control) {
70310958Sdme@sun.com err = xenbus_printf(xbt, xsname, "request-multicast-control",
70410958Sdme@sun.com "%d", 1);
70510958Sdme@sun.com if (err != 0) {
70610958Sdme@sun.com message = "writing request-multicast-control";
70710958Sdme@sun.com goto abort_transaction;
70810958Sdme@sun.com }
70910958Sdme@sun.com }
71010958Sdme@sun.com
71110958Sdme@sun.com err = xvdi_switch_state(xnfp->xnf_devinfo, xbt, XenbusStateConnected);
7125084Sjohnlev if (err != 0) {
71310958Sdme@sun.com message = "switching state to XenbusStateConnected";
7145084Sjohnlev goto abort_transaction;
7155084Sjohnlev }
7165084Sjohnlev
7175084Sjohnlev err = xenbus_transaction_end(xbt, 0);
7185084Sjohnlev if (err != 0) {
7195084Sjohnlev if (err == EAGAIN)
7205084Sjohnlev goto again;
7215084Sjohnlev xenbus_dev_error(xsd, err, "completing transaction");
7225084Sjohnlev }
7235084Sjohnlev
7245084Sjohnlev return;
7255084Sjohnlev
7265084Sjohnlev abort_transaction:
7275084Sjohnlev (void) xenbus_transaction_end(xbt, 1);
7285084Sjohnlev xenbus_dev_error(xsd, err, "%s", message);
7295084Sjohnlev }
7305084Sjohnlev
7315084Sjohnlev /*
73210958Sdme@sun.com * Read configuration information from xenstore.
7336899Scz147101 */
7346899Scz147101 void
xnf_read_config(xnf_t * xnfp)7356899Scz147101 xnf_read_config(xnf_t *xnfp)
7366899Scz147101 {
73710958Sdme@sun.com int err, be_cap;
73810958Sdme@sun.com char mac[ETHERADDRL * 3];
73910958Sdme@sun.com char *oename = xvdi_get_oename(xnfp->xnf_devinfo);
74010958Sdme@sun.com
74110958Sdme@sun.com err = xenbus_scanf(XBT_NULL, oename, "mac",
7426899Scz147101 "%s", (char *)&mac[0]);
7436899Scz147101 if (err != 0) {
7446899Scz147101 /*
7456899Scz147101 * bad: we're supposed to be set up with a proper mac
7466899Scz147101 * addr. at this point
7476899Scz147101 */
7486899Scz147101 cmn_err(CE_WARN, "%s%d: no mac address",
7496899Scz147101 ddi_driver_name(xnfp->xnf_devinfo),
7506899Scz147101 ddi_get_instance(xnfp->xnf_devinfo));
7516899Scz147101 return;
7526899Scz147101 }
7536899Scz147101 if (ether_aton(mac, xnfp->xnf_mac_addr) != ETHERADDRL) {
7546899Scz147101 err = ENOENT;
7556899Scz147101 xenbus_dev_error(xvdi_get_xsd(xnfp->xnf_devinfo), ENOENT,
7566899Scz147101 "parsing %s/mac", xvdi_get_xsname(xnfp->xnf_devinfo));
7576899Scz147101 return;
7586899Scz147101 }
7596899Scz147101
76010958Sdme@sun.com err = xenbus_scanf(XBT_NULL, oename,
76110958Sdme@sun.com "feature-rx-copy", "%d", &be_cap);
7626899Scz147101 /*
7636899Scz147101 * If we fail to read the store we assume that the key is
7646899Scz147101 * absent, implying an older domain at the far end. Older
76510958Sdme@sun.com * domains cannot do HV copy.
7666899Scz147101 */
7676899Scz147101 if (err != 0)
76810958Sdme@sun.com be_cap = 0;
76910958Sdme@sun.com xnfp->xnf_be_rx_copy = (be_cap != 0);
77010958Sdme@sun.com
77110958Sdme@sun.com err = xenbus_scanf(XBT_NULL, oename,
77210958Sdme@sun.com "feature-multicast-control", "%d", &be_cap);
7736899Scz147101 /*
77410958Sdme@sun.com * If we fail to read the store we assume that the key is
77510958Sdme@sun.com * absent, implying an older domain at the far end. Older
77610958Sdme@sun.com * domains do not support multicast control.
7776899Scz147101 */
77810958Sdme@sun.com if (err != 0)
77910958Sdme@sun.com be_cap = 0;
78010958Sdme@sun.com xnfp->xnf_be_mcast_control = (be_cap != 0) && xnf_multicast_control;
7816899Scz147101 }
7826899Scz147101
7836899Scz147101 /*
7845084Sjohnlev * attach(9E) -- Attach a device to the system
7855084Sjohnlev */
7865084Sjohnlev static int
xnf_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)7875084Sjohnlev xnf_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
7885084Sjohnlev {
7895084Sjohnlev mac_register_t *macp;
7905084Sjohnlev xnf_t *xnfp;
7915084Sjohnlev int err;
79210958Sdme@sun.com char cachename[32];
7935084Sjohnlev
7945084Sjohnlev #ifdef XNF_DEBUG
79510958Sdme@sun.com if (xnf_debug & XNF_DEBUG_DDI)
7965084Sjohnlev printf("xnf%d: attach(0x%p)\n", ddi_get_instance(devinfo),
7975084Sjohnlev (void *)devinfo);
7985084Sjohnlev #endif
7995084Sjohnlev
8005084Sjohnlev switch (cmd) {
8015084Sjohnlev case DDI_RESUME:
8025084Sjohnlev xnfp = ddi_get_driver_private(devinfo);
80310958Sdme@sun.com xnfp->xnf_gen++;
8045084Sjohnlev
8055084Sjohnlev (void) xvdi_resume(devinfo);
8065084Sjohnlev (void) xvdi_alloc_evtchn(devinfo);
8075741Smrj xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
8085741Smrj #ifdef XPV_HVM_DRIVER
8095741Smrj ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr,
8105741Smrj xnfp);
8115741Smrj #else
8125084Sjohnlev (void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr,
8135084Sjohnlev (caddr_t)xnfp);
8145741Smrj #endif
8155084Sjohnlev return (DDI_SUCCESS);
8165084Sjohnlev
8175084Sjohnlev case DDI_ATTACH:
8185084Sjohnlev break;
8195084Sjohnlev
8205084Sjohnlev default:
8215084Sjohnlev return (DDI_FAILURE);
8225084Sjohnlev }
8235084Sjohnlev
8245084Sjohnlev /*
8255084Sjohnlev * Allocate gld_mac_info_t and xnf_instance structures
8265084Sjohnlev */
8275084Sjohnlev macp = mac_alloc(MAC_VERSION);
8285084Sjohnlev if (macp == NULL)
8295084Sjohnlev return (DDI_FAILURE);
8305084Sjohnlev xnfp = kmem_zalloc(sizeof (*xnfp), KM_SLEEP);
8315084Sjohnlev
8325084Sjohnlev macp->m_dip = devinfo;
8335084Sjohnlev macp->m_driver = xnfp;
8345741Smrj xnfp->xnf_devinfo = devinfo;
8355084Sjohnlev
8365084Sjohnlev macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
8375741Smrj macp->m_src_addr = xnfp->xnf_mac_addr;
8385084Sjohnlev macp->m_callbacks = &xnf_callbacks;
8395084Sjohnlev macp->m_min_sdu = 0;
8405084Sjohnlev macp->m_max_sdu = XNF_MAXPKT;
8415084Sjohnlev
8425741Smrj xnfp->xnf_running = B_FALSE;
8435741Smrj xnfp->xnf_connected = B_FALSE;
84410958Sdme@sun.com xnfp->xnf_be_rx_copy = B_FALSE;
84510958Sdme@sun.com xnfp->xnf_be_mcast_control = B_FALSE;
8467521Sdme@sun.com xnfp->xnf_need_sched = B_FALSE;
8475741Smrj
84810958Sdme@sun.com xnfp->xnf_rx_head = NULL;
84910958Sdme@sun.com xnfp->xnf_rx_tail = NULL;
85010958Sdme@sun.com xnfp->xnf_rx_new_buffers_posted = B_FALSE;
85110958Sdme@sun.com
8525741Smrj #ifdef XPV_HVM_DRIVER
8536450Srab /*
8546450Srab * Report our version to dom0.
8556450Srab */
85610175SStuart.Maybee@Sun.COM if (xenbus_printf(XBT_NULL, "guest/xnf", "version", "%d",
8576450Srab HVMPV_XNF_VERS))
8586450Srab cmn_err(CE_WARN, "xnf: couldn't write version\n");
8595741Smrj #endif
8605084Sjohnlev
8615084Sjohnlev /*
8625084Sjohnlev * Get the iblock cookie with which to initialize the mutexes.
8635084Sjohnlev */
8645741Smrj if (ddi_get_iblock_cookie(devinfo, 0, &xnfp->xnf_icookie)
8655084Sjohnlev != DDI_SUCCESS)
8665084Sjohnlev goto failure;
86710958Sdme@sun.com
8685741Smrj mutex_init(&xnfp->xnf_txlock,
8695741Smrj NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87010958Sdme@sun.com mutex_init(&xnfp->xnf_rxlock,
87110958Sdme@sun.com NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87210958Sdme@sun.com mutex_init(&xnfp->xnf_schedlock,
87310958Sdme@sun.com NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87410958Sdme@sun.com mutex_init(&xnfp->xnf_gref_lock,
8755741Smrj NULL, MUTEX_DRIVER, xnfp->xnf_icookie);
87610958Sdme@sun.com
87710958Sdme@sun.com cv_init(&xnfp->xnf_cv_state, NULL, CV_DEFAULT, NULL);
87810958Sdme@sun.com cv_init(&xnfp->xnf_cv_multicast, NULL, CV_DEFAULT, NULL);
87910958Sdme@sun.com cv_init(&xnfp->xnf_cv_tx_slots, NULL, CV_DEFAULT, NULL);
88010958Sdme@sun.com
88110958Sdme@sun.com (void) sprintf(cachename, "xnf_buf_cache_%d",
88210958Sdme@sun.com ddi_get_instance(devinfo));
88310958Sdme@sun.com xnfp->xnf_buf_cache = kmem_cache_create(cachename,
88410958Sdme@sun.com sizeof (xnf_buf_t), 0,
88510958Sdme@sun.com xnf_buf_constructor, xnf_buf_destructor,
88610958Sdme@sun.com NULL, xnfp, NULL, 0);
88710958Sdme@sun.com if (xnfp->xnf_buf_cache == NULL)
88810958Sdme@sun.com goto failure_0;
88910958Sdme@sun.com
89010958Sdme@sun.com (void) sprintf(cachename, "xnf_tx_buf_cache_%d",
89110958Sdme@sun.com ddi_get_instance(devinfo));
89210958Sdme@sun.com xnfp->xnf_tx_buf_cache = kmem_cache_create(cachename,
89310958Sdme@sun.com sizeof (xnf_txbuf_t), 0,
89410958Sdme@sun.com xnf_tx_buf_constructor, xnf_tx_buf_destructor,
89510958Sdme@sun.com NULL, xnfp, NULL, 0);
89610958Sdme@sun.com if (xnfp->xnf_tx_buf_cache == NULL)
8975741Smrj goto failure_1;
89810958Sdme@sun.com
89910958Sdme@sun.com xnfp->xnf_gref_head = INVALID_GRANT_REF;
90010958Sdme@sun.com
9015084Sjohnlev if (xnf_alloc_dma_resources(xnfp) == DDI_FAILURE) {
9025084Sjohnlev cmn_err(CE_WARN, "xnf%d: failed to allocate and initialize "
9035741Smrj "driver data structures",
9045741Smrj ddi_get_instance(xnfp->xnf_devinfo));
90510958Sdme@sun.com goto failure_2;
9065084Sjohnlev }
9075084Sjohnlev
9085741Smrj xnfp->xnf_rx_ring.sring->rsp_event =
9095741Smrj xnfp->xnf_tx_ring.sring->rsp_event = 1;
9105084Sjohnlev
91110958Sdme@sun.com xnfp->xnf_tx_ring_ref = INVALID_GRANT_REF;
91210958Sdme@sun.com xnfp->xnf_rx_ring_ref = INVALID_GRANT_REF;
9135084Sjohnlev
9145084Sjohnlev /* set driver private pointer now */
9155084Sjohnlev ddi_set_driver_private(devinfo, xnfp);
9165084Sjohnlev
9175084Sjohnlev if (!xnf_kstat_init(xnfp))
91810958Sdme@sun.com goto failure_3;
9195084Sjohnlev
9205084Sjohnlev /*
9215084Sjohnlev * Allocate an event channel, add the interrupt handler and
9225084Sjohnlev * bind it to the event channel.
9235084Sjohnlev */
9245084Sjohnlev (void) xvdi_alloc_evtchn(devinfo);
9255741Smrj xnfp->xnf_evtchn = xvdi_get_evtchn(devinfo);
9265741Smrj #ifdef XPV_HVM_DRIVER
9275741Smrj ec_bind_evtchn_to_handler(xnfp->xnf_evtchn, IPL_VIF, xnf_intr, xnfp);
9285741Smrj #else
9295084Sjohnlev (void) ddi_add_intr(devinfo, 0, NULL, NULL, xnf_intr, (caddr_t)xnfp);
9305741Smrj #endif
9315084Sjohnlev
9325741Smrj err = mac_register(macp, &xnfp->xnf_mh);
9335084Sjohnlev mac_free(macp);
9345084Sjohnlev macp = NULL;
9355084Sjohnlev if (err != 0)
93610958Sdme@sun.com goto failure_4;
93710958Sdme@sun.com
93810958Sdme@sun.com if (xvdi_add_event_handler(devinfo, XS_OE_STATE, oe_state_change, NULL)
93910958Sdme@sun.com != DDI_SUCCESS)
94010958Sdme@sun.com goto failure_5;
9415084Sjohnlev
9426873Sfvdl #ifdef XPV_HVM_DRIVER
9436873Sfvdl /*
9446873Sfvdl * In the HVM case, this driver essentially replaces a driver for
9456873Sfvdl * a 'real' PCI NIC. Without the "model" property set to
9466873Sfvdl * "Ethernet controller", like the PCI code does, netbooting does
9476873Sfvdl * not work correctly, as strplumb_get_netdev_path() will not find
9486873Sfvdl * this interface.
9496873Sfvdl */
9506873Sfvdl (void) ndi_prop_update_string(DDI_DEV_T_NONE, devinfo, "model",
9516873Sfvdl "Ethernet controller");
9526873Sfvdl #endif
9536873Sfvdl
95410958Sdme@sun.com #ifdef XNF_DEBUG
95510958Sdme@sun.com if (xnf_debug_instance == NULL)
95610958Sdme@sun.com xnf_debug_instance = xnfp;
95710958Sdme@sun.com #endif
9586899Scz147101
9595084Sjohnlev return (DDI_SUCCESS);
9605084Sjohnlev
96110958Sdme@sun.com failure_5:
96210981Sdme@sun.com (void) mac_unregister(xnfp->xnf_mh);
96310958Sdme@sun.com
96410958Sdme@sun.com failure_4:
9655741Smrj #ifdef XPV_HVM_DRIVER
9665741Smrj ec_unbind_evtchn(xnfp->xnf_evtchn);
9676431Ssmaybe xvdi_free_evtchn(devinfo);
9685741Smrj #else
9695741Smrj ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
9705741Smrj #endif
9715741Smrj xnfp->xnf_evtchn = INVALID_EVTCHN;
97210958Sdme@sun.com kstat_delete(xnfp->xnf_kstat_aux);
97310958Sdme@sun.com
97410958Sdme@sun.com failure_3:
97510958Sdme@sun.com xnf_release_dma_resources(xnfp);
9765084Sjohnlev
9776899Scz147101 failure_2:
97810958Sdme@sun.com kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
9796899Scz147101
9805741Smrj failure_1:
98110958Sdme@sun.com kmem_cache_destroy(xnfp->xnf_buf_cache);
98210958Sdme@sun.com
98310958Sdme@sun.com failure_0:
98410958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_tx_slots);
98510958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_multicast);
98610958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_state);
98710958Sdme@sun.com
98810958Sdme@sun.com mutex_destroy(&xnfp->xnf_gref_lock);
98910958Sdme@sun.com mutex_destroy(&xnfp->xnf_schedlock);
99010958Sdme@sun.com mutex_destroy(&xnfp->xnf_rxlock);
9915741Smrj mutex_destroy(&xnfp->xnf_txlock);
9925084Sjohnlev
9935084Sjohnlev failure:
9945084Sjohnlev kmem_free(xnfp, sizeof (*xnfp));
9955084Sjohnlev if (macp != NULL)
9965084Sjohnlev mac_free(macp);
9975084Sjohnlev
9985084Sjohnlev return (DDI_FAILURE);
9995084Sjohnlev }
10005084Sjohnlev
10015084Sjohnlev /* detach(9E) -- Detach a device from the system */
10025084Sjohnlev static int
xnf_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)10035084Sjohnlev xnf_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
10045084Sjohnlev {
10055084Sjohnlev xnf_t *xnfp; /* Our private device info */
10065084Sjohnlev
10075084Sjohnlev #ifdef XNF_DEBUG
100810958Sdme@sun.com if (xnf_debug & XNF_DEBUG_DDI)
10095084Sjohnlev printf("xnf_detach(0x%p)\n", (void *)devinfo);
10105084Sjohnlev #endif
10115084Sjohnlev
10125084Sjohnlev xnfp = ddi_get_driver_private(devinfo);
10135084Sjohnlev
10145084Sjohnlev switch (cmd) {
10155084Sjohnlev case DDI_SUSPEND:
10165741Smrj #ifdef XPV_HVM_DRIVER
10175741Smrj ec_unbind_evtchn(xnfp->xnf_evtchn);
10186431Ssmaybe xvdi_free_evtchn(devinfo);
10195741Smrj #else
10205741Smrj ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
10215741Smrj #endif
10225084Sjohnlev
10235084Sjohnlev xvdi_suspend(devinfo);
10245084Sjohnlev
102510958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
10265741Smrj mutex_enter(&xnfp->xnf_txlock);
10275084Sjohnlev
10285741Smrj xnfp->xnf_evtchn = INVALID_EVTCHN;
10295741Smrj xnfp->xnf_connected = B_FALSE;
10305741Smrj mutex_exit(&xnfp->xnf_txlock);
103110958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
10327397SMax.Zhen@Sun.COM
10337397SMax.Zhen@Sun.COM /* claim link to be down after disconnect */
10347397SMax.Zhen@Sun.COM mac_link_update(xnfp->xnf_mh, LINK_STATE_DOWN);
10355084Sjohnlev return (DDI_SUCCESS);
10365084Sjohnlev
10375084Sjohnlev case DDI_DETACH:
10385084Sjohnlev break;
10395084Sjohnlev
10405084Sjohnlev default:
10415084Sjohnlev return (DDI_FAILURE);
10425084Sjohnlev }
10435084Sjohnlev
10445741Smrj if (xnfp->xnf_connected)
10455084Sjohnlev return (DDI_FAILURE);
10465084Sjohnlev
104710958Sdme@sun.com /*
104810958Sdme@sun.com * Cannot detach if we have xnf_buf_t outstanding.
104910958Sdme@sun.com */
105010958Sdme@sun.com if (xnfp->xnf_stat_buf_allocated > 0)
105110958Sdme@sun.com return (DDI_FAILURE);
10525084Sjohnlev
10535741Smrj if (mac_unregister(xnfp->xnf_mh) != 0)
10545084Sjohnlev return (DDI_FAILURE);
10555084Sjohnlev
10566899Scz147101 kstat_delete(xnfp->xnf_kstat_aux);
10576899Scz147101
10585084Sjohnlev /* Stop the receiver */
10595084Sjohnlev xnf_stop(xnfp);
10605084Sjohnlev
10615084Sjohnlev xvdi_remove_event_handler(devinfo, XS_OE_STATE);
10625084Sjohnlev
10635084Sjohnlev /* Remove the interrupt */
10645741Smrj #ifdef XPV_HVM_DRIVER
10655741Smrj ec_unbind_evtchn(xnfp->xnf_evtchn);
10666431Ssmaybe xvdi_free_evtchn(devinfo);
10675741Smrj #else
10685741Smrj ddi_remove_intr(devinfo, 0, xnfp->xnf_icookie);
10695741Smrj #endif
10705084Sjohnlev
10715084Sjohnlev /* Release any pending xmit mblks */
10725084Sjohnlev xnf_release_mblks(xnfp);
10735084Sjohnlev
10745084Sjohnlev /* Release all DMA resources */
10755084Sjohnlev xnf_release_dma_resources(xnfp);
10765084Sjohnlev
107710958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_tx_slots);
107810958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_multicast);
107910958Sdme@sun.com cv_destroy(&xnfp->xnf_cv_state);
108010958Sdme@sun.com
108110958Sdme@sun.com kmem_cache_destroy(xnfp->xnf_tx_buf_cache);
108210958Sdme@sun.com kmem_cache_destroy(xnfp->xnf_buf_cache);
108310958Sdme@sun.com
108410958Sdme@sun.com mutex_destroy(&xnfp->xnf_gref_lock);
108510958Sdme@sun.com mutex_destroy(&xnfp->xnf_schedlock);
108610958Sdme@sun.com mutex_destroy(&xnfp->xnf_rxlock);
10875741Smrj mutex_destroy(&xnfp->xnf_txlock);
10885084Sjohnlev
10895084Sjohnlev kmem_free(xnfp, sizeof (*xnfp));
10905084Sjohnlev
10915084Sjohnlev return (DDI_SUCCESS);
10925084Sjohnlev }
10935084Sjohnlev
10945084Sjohnlev /*
10955084Sjohnlev * xnf_set_mac_addr() -- set the physical network address on the board.
10965084Sjohnlev */
10975084Sjohnlev static int
xnf_set_mac_addr(void * arg,const uint8_t * macaddr)10985084Sjohnlev xnf_set_mac_addr(void *arg, const uint8_t *macaddr)
10995084Sjohnlev {
110010958Sdme@sun.com _NOTE(ARGUNUSED(arg, macaddr));
110110958Sdme@sun.com
11025084Sjohnlev /*
11035084Sjohnlev * We can't set our macaddr.
11045084Sjohnlev */
11055084Sjohnlev return (ENOTSUP);
11065084Sjohnlev }
11075084Sjohnlev
11085084Sjohnlev /*
11095084Sjohnlev * xnf_set_multicast() -- set (enable) or disable a multicast address.
11105084Sjohnlev *
11115084Sjohnlev * Program the hardware to enable/disable the multicast address
111210958Sdme@sun.com * in "mca". Enable if "add" is true, disable if false.
11135084Sjohnlev */
11145084Sjohnlev static int
xnf_set_multicast(void * arg,boolean_t add,const uint8_t * mca)11155084Sjohnlev xnf_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
11165084Sjohnlev {
11175084Sjohnlev xnf_t *xnfp = arg;
111810958Sdme@sun.com xnf_txbuf_t *txp;
111910958Sdme@sun.com int n_slots;
112010958Sdme@sun.com RING_IDX slot;
112110958Sdme@sun.com xnf_txid_t *tidp;
112210958Sdme@sun.com netif_tx_request_t *txrp;
112310958Sdme@sun.com struct netif_extra_info *erp;
112410958Sdme@sun.com boolean_t notify, result;
112510958Sdme@sun.com
112610958Sdme@sun.com /*
112710958Sdme@sun.com * If the backend does not support multicast control then we
112810958Sdme@sun.com * must assume that the right packets will just arrive.
112910958Sdme@sun.com */
113010958Sdme@sun.com if (!xnfp->xnf_be_mcast_control)
113110958Sdme@sun.com return (0);
113210958Sdme@sun.com
113310958Sdme@sun.com txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
113410958Sdme@sun.com
113510958Sdme@sun.com mutex_enter(&xnfp->xnf_txlock);
113610958Sdme@sun.com
113710958Sdme@sun.com /*
113810958Sdme@sun.com * If we're not yet connected then claim success. This is
113910958Sdme@sun.com * acceptable because we refresh the entire set of multicast
114010958Sdme@sun.com * addresses when we get connected.
114110958Sdme@sun.com *
114210958Sdme@sun.com * We can't wait around here because the MAC layer expects
114310958Sdme@sun.com * this to be a non-blocking operation - waiting ends up
114410958Sdme@sun.com * causing a deadlock during resume.
114510958Sdme@sun.com */
114610958Sdme@sun.com if (!xnfp->xnf_connected) {
114710958Sdme@sun.com mutex_exit(&xnfp->xnf_txlock);
114810958Sdme@sun.com return (0);
114910958Sdme@sun.com }
11505084Sjohnlev
11515084Sjohnlev /*
115210958Sdme@sun.com * 1. Acquire two slots in the ring.
115310958Sdme@sun.com * 2. Fill in the slots.
115410958Sdme@sun.com * 3. Request notification when the operation is done.
115510958Sdme@sun.com * 4. Kick the peer.
115610958Sdme@sun.com * 5. Wait for the response via xnf_tx_clean_ring().
11575084Sjohnlev */
115810958Sdme@sun.com
115910958Sdme@sun.com n_slots = tx_slots_get(xnfp, 2, B_TRUE);
116010958Sdme@sun.com ASSERT(n_slots >= 2);
116110958Sdme@sun.com
116210958Sdme@sun.com slot = xnfp->xnf_tx_ring.req_prod_pvt;
116310958Sdme@sun.com tidp = txid_get(xnfp);
116410958Sdme@sun.com VERIFY(tidp != NULL);
116510958Sdme@sun.com
116610958Sdme@sun.com txp->tx_type = TX_MCAST_REQ;
116710958Sdme@sun.com txp->tx_slot = slot;
116810958Sdme@sun.com
116910958Sdme@sun.com txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
117010958Sdme@sun.com erp = (struct netif_extra_info *)
117110958Sdme@sun.com RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot + 1);
117210958Sdme@sun.com
117310958Sdme@sun.com txrp->gref = 0;
117410958Sdme@sun.com txrp->size = 0;
117510958Sdme@sun.com txrp->offset = 0;
117610958Sdme@sun.com /* Set tx_txreq.id to appease xnf_tx_clean_ring(). */
117710958Sdme@sun.com txrp->id = txp->tx_txreq.id = tidp->id;
117810958Sdme@sun.com txrp->flags = NETTXF_extra_info;
117910958Sdme@sun.com
118010958Sdme@sun.com erp->type = add ? XEN_NETIF_EXTRA_TYPE_MCAST_ADD :
118110958Sdme@sun.com XEN_NETIF_EXTRA_TYPE_MCAST_DEL;
118210958Sdme@sun.com bcopy((void *)mca, &erp->u.mcast.addr, ETHERADDRL);
118310958Sdme@sun.com
118410958Sdme@sun.com tidp->txbuf = txp;
118510958Sdme@sun.com
118610958Sdme@sun.com xnfp->xnf_tx_ring.req_prod_pvt = slot + 2;
118710958Sdme@sun.com
118810958Sdme@sun.com mutex_enter(&xnfp->xnf_schedlock);
118910958Sdme@sun.com xnfp->xnf_pending_multicast++;
119010958Sdme@sun.com mutex_exit(&xnfp->xnf_schedlock);
119110958Sdme@sun.com
119210958Sdme@sun.com /* LINTED: constant in conditional context */
119310958Sdme@sun.com RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
119410958Sdme@sun.com notify);
119510958Sdme@sun.com if (notify)
119610958Sdme@sun.com ec_notify_via_evtchn(xnfp->xnf_evtchn);
119710958Sdme@sun.com
119810958Sdme@sun.com while (txp->tx_type == TX_MCAST_REQ)
119910958Sdme@sun.com cv_wait(&xnfp->xnf_cv_multicast,
120010958Sdme@sun.com &xnfp->xnf_txlock);
120110958Sdme@sun.com
120210958Sdme@sun.com ASSERT(txp->tx_type == TX_MCAST_RSP);
120310958Sdme@sun.com
120410958Sdme@sun.com mutex_enter(&xnfp->xnf_schedlock);
120510958Sdme@sun.com xnfp->xnf_pending_multicast--;
120610958Sdme@sun.com mutex_exit(&xnfp->xnf_schedlock);
120710958Sdme@sun.com
120810958Sdme@sun.com result = (txp->tx_status == NETIF_RSP_OKAY);
120910958Sdme@sun.com
121010958Sdme@sun.com txid_put(xnfp, tidp);
121110958Sdme@sun.com
121210958Sdme@sun.com mutex_exit(&xnfp->xnf_txlock);
121310958Sdme@sun.com
121410958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
121510958Sdme@sun.com
121610958Sdme@sun.com return (result ? 0 : 1);
12175084Sjohnlev }
12185084Sjohnlev
12195084Sjohnlev /*
12205084Sjohnlev * xnf_set_promiscuous() -- set or reset promiscuous mode on the board
12215084Sjohnlev *
12225084Sjohnlev * Program the hardware to enable/disable promiscuous mode.
12235084Sjohnlev */
12245084Sjohnlev static int
xnf_set_promiscuous(void * arg,boolean_t on)12255084Sjohnlev xnf_set_promiscuous(void *arg, boolean_t on)
12265084Sjohnlev {
122710958Sdme@sun.com _NOTE(ARGUNUSED(arg, on));
122810958Sdme@sun.com
12295084Sjohnlev /*
12305084Sjohnlev * We can't really do this, but we pretend that we can in
12315084Sjohnlev * order that snoop will work.
12325084Sjohnlev */
12335084Sjohnlev return (0);
12345084Sjohnlev }
12355084Sjohnlev
12365084Sjohnlev /*
12375084Sjohnlev * Clean buffers that we have responses for from the transmit ring.
12385084Sjohnlev */
12395084Sjohnlev static int
xnf_tx_clean_ring(xnf_t * xnfp)124010958Sdme@sun.com xnf_tx_clean_ring(xnf_t *xnfp)
12415084Sjohnlev {
124210958Sdme@sun.com boolean_t work_to_do;
12435084Sjohnlev
12445741Smrj ASSERT(MUTEX_HELD(&xnfp->xnf_txlock));
12455084Sjohnlev
12466899Scz147101 loop:
12475990Sschuster while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_tx_ring)) {
124810958Sdme@sun.com RING_IDX cons, prod, i;
124910958Sdme@sun.com
125010958Sdme@sun.com cons = xnfp->xnf_tx_ring.rsp_cons;
125110958Sdme@sun.com prod = xnfp->xnf_tx_ring.sring->rsp_prod;
12525084Sjohnlev membar_consumer();
12535084Sjohnlev /*
125410958Sdme@sun.com * Clean tx requests from ring that we have responses
125510958Sdme@sun.com * for.
12565084Sjohnlev */
125710958Sdme@sun.com DTRACE_PROBE2(xnf_tx_clean_range, int, cons, int, prod);
125810958Sdme@sun.com for (i = cons; i != prod; i++) {
125910958Sdme@sun.com netif_tx_response_t *trp;
126010958Sdme@sun.com xnf_txid_t *tidp;
126110958Sdme@sun.com xnf_txbuf_t *txp;
126210958Sdme@sun.com
126310958Sdme@sun.com trp = RING_GET_RESPONSE(&xnfp->xnf_tx_ring, i);
126410958Sdme@sun.com ASSERT(TX_ID_VALID(trp->id));
126510958Sdme@sun.com
126610958Sdme@sun.com tidp = TX_ID_TO_TXID(xnfp, trp->id);
126710958Sdme@sun.com ASSERT(tidp->id == trp->id);
126810958Sdme@sun.com ASSERT(tidp->next == INVALID_TX_ID);
126910958Sdme@sun.com
127010958Sdme@sun.com txp = tidp->txbuf;
127110958Sdme@sun.com ASSERT(txp != NULL);
127210958Sdme@sun.com ASSERT(txp->tx_txreq.id == trp->id);
127310958Sdme@sun.com
127410958Sdme@sun.com switch (txp->tx_type) {
127510958Sdme@sun.com case TX_DATA:
127610958Sdme@sun.com if (gnttab_query_foreign_access(
127710958Sdme@sun.com txp->tx_txreq.gref) != 0)
127810958Sdme@sun.com cmn_err(CE_PANIC,
127910958Sdme@sun.com "tx grant %d still in use by "
128010958Sdme@sun.com "backend domain",
128110958Sdme@sun.com txp->tx_txreq.gref);
128210958Sdme@sun.com
128310958Sdme@sun.com if (txp->tx_bdesc == NULL) {
128410958Sdme@sun.com (void) gnttab_end_foreign_access_ref(
128510958Sdme@sun.com txp->tx_txreq.gref, 1);
128610958Sdme@sun.com gref_put(xnfp, txp->tx_txreq.gref);
128710958Sdme@sun.com (void) ddi_dma_unbind_handle(
128810958Sdme@sun.com txp->tx_dma_handle);
128910958Sdme@sun.com } else {
129010958Sdme@sun.com xnf_buf_put(xnfp, txp->tx_bdesc,
129110958Sdme@sun.com B_TRUE);
129210958Sdme@sun.com }
129310958Sdme@sun.com
129410958Sdme@sun.com freemsg(txp->tx_mp);
129510958Sdme@sun.com txid_put(xnfp, tidp);
129610958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
129710958Sdme@sun.com
129810958Sdme@sun.com break;
129910958Sdme@sun.com
130010958Sdme@sun.com case TX_MCAST_REQ:
130110958Sdme@sun.com txp->tx_type = TX_MCAST_RSP;
130210958Sdme@sun.com txp->tx_status = trp->status;
130310958Sdme@sun.com cv_broadcast(&xnfp->xnf_cv_multicast);
130410958Sdme@sun.com
130510958Sdme@sun.com break;
130610958Sdme@sun.com
130710958Sdme@sun.com case TX_MCAST_RSP:
130810958Sdme@sun.com break;
130910958Sdme@sun.com
131010958Sdme@sun.com default:
131110958Sdme@sun.com cmn_err(CE_PANIC, "xnf_tx_clean_ring: "
131210958Sdme@sun.com "invalid xnf_txbuf_t type: %d",
131310958Sdme@sun.com txp->tx_type);
131410958Sdme@sun.com break;
131510958Sdme@sun.com }
13165084Sjohnlev }
131710958Sdme@sun.com /*
131810958Sdme@sun.com * Record the last response we dealt with so that we
131910958Sdme@sun.com * know where to start next time around.
132010958Sdme@sun.com */
132110958Sdme@sun.com xnfp->xnf_tx_ring.rsp_cons = prod;
13225084Sjohnlev membar_enter();
13235990Sschuster }
13245990Sschuster
13256899Scz147101 /* LINTED: constant in conditional context */
13266899Scz147101 RING_FINAL_CHECK_FOR_RESPONSES(&xnfp->xnf_tx_ring, work_to_do);
13276899Scz147101 if (work_to_do)
13286899Scz147101 goto loop;
13296899Scz147101
13305990Sschuster return (RING_FREE_REQUESTS(&xnfp->xnf_tx_ring));
13315084Sjohnlev }
13325084Sjohnlev
13335084Sjohnlev /*
133410958Sdme@sun.com * Allocate and fill in a look-aside buffer for the packet `mp'. Used
133510958Sdme@sun.com * to ensure that the packet is physically contiguous and contained
133610958Sdme@sun.com * within a single page.
13375084Sjohnlev */
133810958Sdme@sun.com static xnf_buf_t *
xnf_tx_pullup(xnf_t * xnfp,mblk_t * mp)133910958Sdme@sun.com xnf_tx_pullup(xnf_t *xnfp, mblk_t *mp)
13405084Sjohnlev {
134110958Sdme@sun.com xnf_buf_t *bd;
134210958Sdme@sun.com caddr_t bp;
134310958Sdme@sun.com
134410958Sdme@sun.com bd = xnf_buf_get(xnfp, KM_SLEEP, B_TRUE);
134510958Sdme@sun.com if (bd == NULL)
134610958Sdme@sun.com return (NULL);
134710958Sdme@sun.com
134810958Sdme@sun.com bp = bd->buf;
134910958Sdme@sun.com while (mp != NULL) {
135010958Sdme@sun.com size_t len = MBLKL(mp);
135110958Sdme@sun.com
135210958Sdme@sun.com bcopy(mp->b_rptr, bp, len);
135310958Sdme@sun.com bp += len;
135410958Sdme@sun.com
135510958Sdme@sun.com mp = mp->b_cont;
135610958Sdme@sun.com }
135710958Sdme@sun.com
135810958Sdme@sun.com ASSERT((bp - bd->buf) <= PAGESIZE);
135910958Sdme@sun.com
13605741Smrj xnfp->xnf_stat_tx_pullup++;
136110958Sdme@sun.com
136210958Sdme@sun.com return (bd);
13635084Sjohnlev }
13645084Sjohnlev
136510958Sdme@sun.com /*
136610958Sdme@sun.com * Insert the pseudo-header checksum into the packet `buf'.
136710958Sdme@sun.com */
13687351Sdme@sun.com void
xnf_pseudo_cksum(caddr_t buf,int length)13697351Sdme@sun.com xnf_pseudo_cksum(caddr_t buf, int length)
13707351Sdme@sun.com {
13717351Sdme@sun.com struct ether_header *ehp;
13727351Sdme@sun.com uint16_t sap, len, *stuff;
13737351Sdme@sun.com uint32_t cksum;
13747351Sdme@sun.com size_t offset;
13757351Sdme@sun.com ipha_t *ipha;
13767351Sdme@sun.com ipaddr_t src, dst;
13777351Sdme@sun.com
13787351Sdme@sun.com ASSERT(length >= sizeof (*ehp));
13797351Sdme@sun.com ehp = (struct ether_header *)buf;
13807351Sdme@sun.com
13817351Sdme@sun.com if (ntohs(ehp->ether_type) == VLAN_TPID) {
13827351Sdme@sun.com struct ether_vlan_header *evhp;
13837351Sdme@sun.com
13847351Sdme@sun.com ASSERT(length >= sizeof (*evhp));
13857351Sdme@sun.com evhp = (struct ether_vlan_header *)buf;
13867351Sdme@sun.com sap = ntohs(evhp->ether_type);
13877351Sdme@sun.com offset = sizeof (*evhp);
13887351Sdme@sun.com } else {
13897351Sdme@sun.com sap = ntohs(ehp->ether_type);
13907351Sdme@sun.com offset = sizeof (*ehp);
13917351Sdme@sun.com }
13927351Sdme@sun.com
13937351Sdme@sun.com ASSERT(sap == ETHERTYPE_IP);
13947351Sdme@sun.com
13957351Sdme@sun.com /* Packet should have been pulled up by the caller. */
13967351Sdme@sun.com if ((offset + sizeof (ipha_t)) > length) {
13977351Sdme@sun.com cmn_err(CE_WARN, "xnf_pseudo_cksum: no room for checksum");
13987351Sdme@sun.com return;
13997351Sdme@sun.com }
14007351Sdme@sun.com
14017351Sdme@sun.com ipha = (ipha_t *)(buf + offset);
14027351Sdme@sun.com
14037351Sdme@sun.com ASSERT(IPH_HDR_LENGTH(ipha) == IP_SIMPLE_HDR_LENGTH);
14047351Sdme@sun.com
14057351Sdme@sun.com len = ntohs(ipha->ipha_length) - IP_SIMPLE_HDR_LENGTH;
14067351Sdme@sun.com
14077351Sdme@sun.com switch (ipha->ipha_protocol) {
14087351Sdme@sun.com case IPPROTO_TCP:
14097351Sdme@sun.com stuff = IPH_TCPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
14107351Sdme@sun.com cksum = IP_TCP_CSUM_COMP;
14117351Sdme@sun.com break;
14127351Sdme@sun.com case IPPROTO_UDP:
14137351Sdme@sun.com stuff = IPH_UDPH_CHECKSUMP(ipha, IP_SIMPLE_HDR_LENGTH);
14147351Sdme@sun.com cksum = IP_UDP_CSUM_COMP;
14157351Sdme@sun.com break;
14167351Sdme@sun.com default:
14177351Sdme@sun.com cmn_err(CE_WARN, "xnf_pseudo_cksum: unexpected protocol %d",
14187351Sdme@sun.com ipha->ipha_protocol);
14197351Sdme@sun.com return;
14207351Sdme@sun.com }
14217351Sdme@sun.com
14227351Sdme@sun.com src = ipha->ipha_src;
14237351Sdme@sun.com dst = ipha->ipha_dst;
14247351Sdme@sun.com
14257351Sdme@sun.com cksum += (dst >> 16) + (dst & 0xFFFF);
14267351Sdme@sun.com cksum += (src >> 16) + (src & 0xFFFF);
14277351Sdme@sun.com cksum += htons(len);
14287351Sdme@sun.com
14297351Sdme@sun.com cksum = (cksum >> 16) + (cksum & 0xFFFF);
14307351Sdme@sun.com cksum = (cksum >> 16) + (cksum & 0xFFFF);
14317351Sdme@sun.com
14327351Sdme@sun.com ASSERT(cksum <= 0xFFFF);
14337351Sdme@sun.com
14347351Sdme@sun.com *stuff = (uint16_t)(cksum ? cksum : ~cksum);
14357351Sdme@sun.com }
14367351Sdme@sun.com
14375084Sjohnlev /*
143810958Sdme@sun.com * Push a list of prepared packets (`txp') into the transmit ring.
14395084Sjohnlev */
144010958Sdme@sun.com static xnf_txbuf_t *
tx_push_packets(xnf_t * xnfp,xnf_txbuf_t * txp)144110958Sdme@sun.com tx_push_packets(xnf_t *xnfp, xnf_txbuf_t *txp)
144210958Sdme@sun.com {
144310958Sdme@sun.com int slots_free;
144410958Sdme@sun.com RING_IDX slot;
144510958Sdme@sun.com boolean_t notify;
144610958Sdme@sun.com
144710958Sdme@sun.com mutex_enter(&xnfp->xnf_txlock);
144810958Sdme@sun.com
144910958Sdme@sun.com ASSERT(xnfp->xnf_running);
145010958Sdme@sun.com
145110958Sdme@sun.com /*
145210958Sdme@sun.com * Wait until we are connected to the backend.
145310958Sdme@sun.com */
145410958Sdme@sun.com while (!xnfp->xnf_connected)
145510958Sdme@sun.com cv_wait(&xnfp->xnf_cv_state, &xnfp->xnf_txlock);
145610958Sdme@sun.com
145710958Sdme@sun.com slots_free = tx_slots_get(xnfp, 1, B_FALSE);
145810958Sdme@sun.com DTRACE_PROBE1(xnf_send_slotsfree, int, slots_free);
145910958Sdme@sun.com
146010958Sdme@sun.com slot = xnfp->xnf_tx_ring.req_prod_pvt;
146110958Sdme@sun.com
146210958Sdme@sun.com while ((txp != NULL) && (slots_free > 0)) {
146310958Sdme@sun.com xnf_txid_t *tidp;
146410958Sdme@sun.com netif_tx_request_t *txrp;
146510958Sdme@sun.com
146610958Sdme@sun.com tidp = txid_get(xnfp);
146710958Sdme@sun.com VERIFY(tidp != NULL);
146810958Sdme@sun.com
146910958Sdme@sun.com txrp = RING_GET_REQUEST(&xnfp->xnf_tx_ring, slot);
147010958Sdme@sun.com
147110958Sdme@sun.com txp->tx_slot = slot;
147210958Sdme@sun.com txp->tx_txreq.id = tidp->id;
147310958Sdme@sun.com *txrp = txp->tx_txreq;
147410958Sdme@sun.com
147510958Sdme@sun.com tidp->txbuf = txp;
147610958Sdme@sun.com
147710958Sdme@sun.com xnfp->xnf_stat_opackets++;
147810958Sdme@sun.com xnfp->xnf_stat_obytes += txp->tx_txreq.size;
147910958Sdme@sun.com
148010958Sdme@sun.com txp = txp->tx_next;
148110958Sdme@sun.com slots_free--;
148210958Sdme@sun.com slot++;
148310958Sdme@sun.com
148410958Sdme@sun.com }
148510958Sdme@sun.com
148610958Sdme@sun.com xnfp->xnf_tx_ring.req_prod_pvt = slot;
148710958Sdme@sun.com
148810958Sdme@sun.com /*
148910958Sdme@sun.com * Tell the peer that we sent something, if it cares.
149010958Sdme@sun.com */
149110958Sdme@sun.com /* LINTED: constant in conditional context */
149210958Sdme@sun.com RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_tx_ring,
149310958Sdme@sun.com notify);
149410958Sdme@sun.com if (notify)
149510958Sdme@sun.com ec_notify_via_evtchn(xnfp->xnf_evtchn);
149610958Sdme@sun.com
149710958Sdme@sun.com mutex_exit(&xnfp->xnf_txlock);
149810958Sdme@sun.com
149910958Sdme@sun.com return (txp);
150010958Sdme@sun.com }
150110958Sdme@sun.com
150210958Sdme@sun.com /*
150310958Sdme@sun.com * Send the chain of packets `mp'. Called by the MAC framework.
150410958Sdme@sun.com */
150510958Sdme@sun.com static mblk_t *
xnf_send(void * arg,mblk_t * mp)150610958Sdme@sun.com xnf_send(void *arg, mblk_t *mp)
15075084Sjohnlev {
150810958Sdme@sun.com xnf_t *xnfp = arg;
150910958Sdme@sun.com domid_t oeid;
151010958Sdme@sun.com xnf_txbuf_t *head, *tail;
151110958Sdme@sun.com mblk_t *ml;
151210958Sdme@sun.com int prepared;
151310958Sdme@sun.com
151410958Sdme@sun.com oeid = xvdi_get_oeid(xnfp->xnf_devinfo);
151510958Sdme@sun.com
151610958Sdme@sun.com /*
151710958Sdme@sun.com * Prepare packets for transmission.
151810958Sdme@sun.com */
151910958Sdme@sun.com head = tail = NULL;
152010958Sdme@sun.com prepared = 0;
152110958Sdme@sun.com while (mp != NULL) {
152210958Sdme@sun.com xnf_txbuf_t *txp;
152310958Sdme@sun.com int n_chunks, length;
152410958Sdme@sun.com boolean_t page_oops;
152510958Sdme@sun.com uint32_t pflags;
152610958Sdme@sun.com
152710958Sdme@sun.com for (ml = mp, n_chunks = length = 0, page_oops = B_FALSE;
152810958Sdme@sun.com ml != NULL;
152910958Sdme@sun.com ml = ml->b_cont, n_chunks++) {
153010958Sdme@sun.com
153110958Sdme@sun.com /*
153210958Sdme@sun.com * Test if this buffer includes a page
153310958Sdme@sun.com * boundary. The test assumes that the range
153410958Sdme@sun.com * b_rptr...b_wptr can include only a single
153510958Sdme@sun.com * boundary.
153610958Sdme@sun.com */
153710958Sdme@sun.com if (xnf_btop((size_t)ml->b_rptr) !=
153810958Sdme@sun.com xnf_btop((size_t)ml->b_wptr)) {
153910958Sdme@sun.com xnfp->xnf_stat_tx_pagebndry++;
154010958Sdme@sun.com page_oops = B_TRUE;
154110958Sdme@sun.com }
154210958Sdme@sun.com
154310958Sdme@sun.com length += MBLKL(ml);
154410958Sdme@sun.com }
154510958Sdme@sun.com DTRACE_PROBE1(xnf_send_b_cont, int, n_chunks);
154610958Sdme@sun.com
154710958Sdme@sun.com /*
154810958Sdme@sun.com * Make sure packet isn't too large.
154910958Sdme@sun.com */
155010958Sdme@sun.com if (length > XNF_FRAMESIZE) {
155110958Sdme@sun.com cmn_err(CE_WARN,
155210958Sdme@sun.com "xnf%d: oversized packet (%d bytes) dropped",
155310958Sdme@sun.com ddi_get_instance(xnfp->xnf_devinfo), length);
155410958Sdme@sun.com freemsg(mp);
155510958Sdme@sun.com continue;
155610958Sdme@sun.com }
155710958Sdme@sun.com
155810958Sdme@sun.com txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
155910958Sdme@sun.com
156010958Sdme@sun.com txp->tx_type = TX_DATA;
156110958Sdme@sun.com
156210958Sdme@sun.com if ((n_chunks > xnf_max_tx_frags) || page_oops) {
156310958Sdme@sun.com /*
156410958Sdme@sun.com * Loan a side buffer rather than the mblk
156510958Sdme@sun.com * itself.
156610958Sdme@sun.com */
156710958Sdme@sun.com txp->tx_bdesc = xnf_tx_pullup(xnfp, mp);
156810958Sdme@sun.com if (txp->tx_bdesc == NULL) {
156910958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
157010958Sdme@sun.com break;
157110958Sdme@sun.com }
157210958Sdme@sun.com
157310958Sdme@sun.com txp->tx_bufp = txp->tx_bdesc->buf;
157410958Sdme@sun.com txp->tx_mfn = txp->tx_bdesc->buf_mfn;
157510958Sdme@sun.com txp->tx_txreq.gref = txp->tx_bdesc->grant_ref;
157610958Sdme@sun.com
157710958Sdme@sun.com } else {
157810958Sdme@sun.com int rc;
157910958Sdme@sun.com ddi_dma_cookie_t dma_cookie;
158010958Sdme@sun.com uint_t ncookies;
158110958Sdme@sun.com
158210958Sdme@sun.com rc = ddi_dma_addr_bind_handle(txp->tx_dma_handle,
158310958Sdme@sun.com NULL, (char *)mp->b_rptr, length,
158410958Sdme@sun.com DDI_DMA_WRITE | DDI_DMA_STREAMING,
158510958Sdme@sun.com DDI_DMA_DONTWAIT, 0, &dma_cookie,
158610958Sdme@sun.com &ncookies);
158710958Sdme@sun.com if (rc != DDI_DMA_MAPPED) {
158810958Sdme@sun.com ASSERT(rc != DDI_DMA_INUSE);
158910958Sdme@sun.com ASSERT(rc != DDI_DMA_PARTIAL_MAP);
15905084Sjohnlev
15915084Sjohnlev #ifdef XNF_DEBUG
159210958Sdme@sun.com if (rc != DDI_DMA_NORESOURCES)
159310958Sdme@sun.com cmn_err(CE_WARN,
159410958Sdme@sun.com "xnf%d: bind_handle failed (%x)",
159510958Sdme@sun.com ddi_get_instance(xnfp->xnf_devinfo),
159610958Sdme@sun.com rc);
15975084Sjohnlev #endif
159810958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
159910958Sdme@sun.com break;
160010958Sdme@sun.com }
160110958Sdme@sun.com ASSERT(ncookies == 1);
160210958Sdme@sun.com
160310958Sdme@sun.com txp->tx_bdesc = NULL;
160410958Sdme@sun.com txp->tx_bufp = (caddr_t)mp->b_rptr;
160510958Sdme@sun.com txp->tx_mfn =
160610958Sdme@sun.com xnf_btop(pa_to_ma(dma_cookie.dmac_laddress));
160710958Sdme@sun.com txp->tx_txreq.gref = gref_get(xnfp);
160810958Sdme@sun.com if (txp->tx_txreq.gref == INVALID_GRANT_REF) {
160910958Sdme@sun.com (void) ddi_dma_unbind_handle(
161010958Sdme@sun.com txp->tx_dma_handle);
161110958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
161210958Sdme@sun.com break;
161310958Sdme@sun.com }
161410958Sdme@sun.com gnttab_grant_foreign_access_ref(txp->tx_txreq.gref,
161510958Sdme@sun.com oeid, txp->tx_mfn, 1);
161610958Sdme@sun.com }
161710958Sdme@sun.com
161810958Sdme@sun.com txp->tx_next = NULL;
161910958Sdme@sun.com txp->tx_mp = mp;
162010958Sdme@sun.com txp->tx_txreq.size = length;
162110958Sdme@sun.com txp->tx_txreq.offset = (uintptr_t)txp->tx_bufp & PAGEOFFSET;
162210958Sdme@sun.com txp->tx_txreq.flags = 0;
1623*11878SVenu.Iyer@Sun.COM mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
162410958Sdme@sun.com if (pflags != 0) {
162510958Sdme@sun.com /*
162610958Sdme@sun.com * If the local protocol stack requests checksum
162710958Sdme@sun.com * offload we set the 'checksum blank' flag,
162810958Sdme@sun.com * indicating to the peer that we need the checksum
162910958Sdme@sun.com * calculated for us.
163010958Sdme@sun.com *
163110958Sdme@sun.com * We _don't_ set the validated flag, because we haven't
163210958Sdme@sun.com * validated that the data and the checksum match.
163310958Sdme@sun.com */
163410958Sdme@sun.com xnf_pseudo_cksum(txp->tx_bufp, length);
163510958Sdme@sun.com txp->tx_txreq.flags |= NETTXF_csum_blank;
163610958Sdme@sun.com
163710958Sdme@sun.com xnfp->xnf_stat_tx_cksum_deferred++;
163810958Sdme@sun.com }
163910958Sdme@sun.com
164010958Sdme@sun.com if (head == NULL) {
164110958Sdme@sun.com ASSERT(tail == NULL);
164210958Sdme@sun.com
164310958Sdme@sun.com head = txp;
164410958Sdme@sun.com } else {
164510958Sdme@sun.com ASSERT(tail != NULL);
164610958Sdme@sun.com
164710958Sdme@sun.com tail->tx_next = txp;
164810958Sdme@sun.com }
164910958Sdme@sun.com tail = txp;
165010958Sdme@sun.com
165110958Sdme@sun.com mp = mp->b_next;
165210958Sdme@sun.com prepared++;
165310958Sdme@sun.com
165410958Sdme@sun.com /*
165510958Sdme@sun.com * There is no point in preparing more than
165610958Sdme@sun.com * NET_TX_RING_SIZE, as we won't be able to push them
165710958Sdme@sun.com * into the ring in one go and would hence have to
165810958Sdme@sun.com * un-prepare the extra.
165910958Sdme@sun.com */
166010958Sdme@sun.com if (prepared == NET_TX_RING_SIZE)
166110958Sdme@sun.com break;
16625084Sjohnlev }
16635084Sjohnlev
166410958Sdme@sun.com DTRACE_PROBE1(xnf_send_prepared, int, prepared);
166510958Sdme@sun.com
166610958Sdme@sun.com if (mp != NULL) {
166710958Sdme@sun.com #ifdef XNF_DEBUG
166810958Sdme@sun.com int notprepared = 0;
166910958Sdme@sun.com mblk_t *l = mp;
167010958Sdme@sun.com
167110958Sdme@sun.com while (l != NULL) {
167210958Sdme@sun.com notprepared++;
167310958Sdme@sun.com l = l->b_next;
167410958Sdme@sun.com }
167510958Sdme@sun.com
167610958Sdme@sun.com DTRACE_PROBE1(xnf_send_notprepared, int, notprepared);
167710958Sdme@sun.com #else /* !XNF_DEBUG */
167810958Sdme@sun.com DTRACE_PROBE1(xnf_send_notprepared, int, -1);
167910958Sdme@sun.com #endif /* XNF_DEBUG */
16805084Sjohnlev }
16815084Sjohnlev
16825084Sjohnlev /*
168310958Sdme@sun.com * Push the packets we have prepared into the ring. They may
168410958Sdme@sun.com * not all go.
16855084Sjohnlev */
168610958Sdme@sun.com if (head != NULL)
168710958Sdme@sun.com head = tx_push_packets(xnfp, head);
16885084Sjohnlev
16895084Sjohnlev /*
169010958Sdme@sun.com * If some packets that we prepared were not sent, unprepare
169110958Sdme@sun.com * them and add them back to the head of those we didn't
169210958Sdme@sun.com * prepare.
16935084Sjohnlev */
169410958Sdme@sun.com {
169510958Sdme@sun.com xnf_txbuf_t *loop;
169610958Sdme@sun.com mblk_t *mp_head, *mp_tail;
169710958Sdme@sun.com int unprepared = 0;
169810958Sdme@sun.com
169910958Sdme@sun.com mp_head = mp_tail = NULL;
170010958Sdme@sun.com loop = head;
170110958Sdme@sun.com
170210958Sdme@sun.com while (loop != NULL) {
170310958Sdme@sun.com xnf_txbuf_t *next = loop->tx_next;
170410958Sdme@sun.com
170510958Sdme@sun.com if (loop->tx_bdesc == NULL) {
170610958Sdme@sun.com (void) gnttab_end_foreign_access_ref(
170710958Sdme@sun.com loop->tx_txreq.gref, 1);
170810958Sdme@sun.com gref_put(xnfp, loop->tx_txreq.gref);
170910958Sdme@sun.com (void) ddi_dma_unbind_handle(
171010958Sdme@sun.com loop->tx_dma_handle);
171110958Sdme@sun.com } else {
171210958Sdme@sun.com xnf_buf_put(xnfp, loop->tx_bdesc, B_TRUE);
171310958Sdme@sun.com }
171410958Sdme@sun.com
171510958Sdme@sun.com ASSERT(loop->tx_mp != NULL);
171610958Sdme@sun.com if (mp_head == NULL)
171710958Sdme@sun.com mp_head = loop->tx_mp;
171810958Sdme@sun.com mp_tail = loop->tx_mp;
171910958Sdme@sun.com
172010958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, loop);
172110958Sdme@sun.com loop = next;
172210958Sdme@sun.com unprepared++;
17235084Sjohnlev }
172410958Sdme@sun.com
172510958Sdme@sun.com if (mp_tail == NULL) {
172610958Sdme@sun.com ASSERT(mp_head == NULL);
172710958Sdme@sun.com } else {
172810958Sdme@sun.com ASSERT(mp_head != NULL);
172910958Sdme@sun.com
173010958Sdme@sun.com mp_tail->b_next = mp;
173110958Sdme@sun.com mp = mp_head;
173210958Sdme@sun.com }
173310958Sdme@sun.com
173410958Sdme@sun.com DTRACE_PROBE1(xnf_send_unprepared, int, unprepared);
17355084Sjohnlev }
17365084Sjohnlev
17375084Sjohnlev /*
173810958Sdme@sun.com * If any mblks are left then we have deferred for some reason
173910958Sdme@sun.com * and need to ask for a re-schedule later. This is typically
174010958Sdme@sun.com * due to the ring filling.
17415084Sjohnlev */
174210958Sdme@sun.com if (mp != NULL) {
174310958Sdme@sun.com mutex_enter(&xnfp->xnf_schedlock);
174410958Sdme@sun.com xnfp->xnf_need_sched = B_TRUE;
174510958Sdme@sun.com mutex_exit(&xnfp->xnf_schedlock);
174610958Sdme@sun.com
174710958Sdme@sun.com xnfp->xnf_stat_tx_defer++;
17485084Sjohnlev }
17495084Sjohnlev
17505084Sjohnlev return (mp);
17515084Sjohnlev }
17525084Sjohnlev
17535084Sjohnlev /*
175410958Sdme@sun.com * Notification of RX packets. Currently no TX-complete interrupt is
175510958Sdme@sun.com * used, as we clean the TX ring lazily.
17565084Sjohnlev */
17575084Sjohnlev static uint_t
xnf_intr(caddr_t arg)17585084Sjohnlev xnf_intr(caddr_t arg)
17595084Sjohnlev {
17605084Sjohnlev xnf_t *xnfp = (xnf_t *)arg;
176110958Sdme@sun.com mblk_t *mp;
176210958Sdme@sun.com boolean_t need_sched, clean_ring;
176310958Sdme@sun.com
176410958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
176510958Sdme@sun.com
176610958Sdme@sun.com /*
176710958Sdme@sun.com * Interrupts before we are connected are spurious.
176810958Sdme@sun.com */
17696899Scz147101 if (!xnfp->xnf_connected) {
177010958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
17715741Smrj xnfp->xnf_stat_unclaimed_interrupts++;
17725084Sjohnlev return (DDI_INTR_UNCLAIMED);
17735084Sjohnlev }
17745084Sjohnlev
177510958Sdme@sun.com /*
177610958Sdme@sun.com * Receive side processing.
177710958Sdme@sun.com */
177810958Sdme@sun.com do {
177910958Sdme@sun.com /*
178010958Sdme@sun.com * Collect buffers from the ring.
178110958Sdme@sun.com */
178210958Sdme@sun.com xnf_rx_collect(xnfp);
178310958Sdme@sun.com
178410958Sdme@sun.com /*
178510958Sdme@sun.com * Interrupt me when the next receive buffer is consumed.
178610958Sdme@sun.com */
178710958Sdme@sun.com xnfp->xnf_rx_ring.sring->rsp_event =
178810958Sdme@sun.com xnfp->xnf_rx_ring.rsp_cons + 1;
178910958Sdme@sun.com xen_mb();
179010958Sdme@sun.com
179110958Sdme@sun.com } while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring));
179210958Sdme@sun.com
179310958Sdme@sun.com if (xnfp->xnf_rx_new_buffers_posted) {
179410958Sdme@sun.com boolean_t notify;
179510958Sdme@sun.com
179610958Sdme@sun.com /*
179710958Sdme@sun.com * Indicate to the peer that we have re-filled the
179810958Sdme@sun.com * receive ring, if it cares.
179910958Sdme@sun.com */
180010958Sdme@sun.com /* LINTED: constant in conditional context */
180110958Sdme@sun.com RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xnfp->xnf_rx_ring, notify);
180210958Sdme@sun.com if (notify)
180310958Sdme@sun.com ec_notify_via_evtchn(xnfp->xnf_evtchn);
180410958Sdme@sun.com xnfp->xnf_rx_new_buffers_posted = B_FALSE;
18055084Sjohnlev }
18065084Sjohnlev
180710958Sdme@sun.com mp = xnfp->xnf_rx_head;
180810958Sdme@sun.com xnfp->xnf_rx_head = xnfp->xnf_rx_tail = NULL;
180910958Sdme@sun.com
18107521Sdme@sun.com xnfp->xnf_stat_interrupts++;
181110958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
181210958Sdme@sun.com
181310958Sdme@sun.com if (mp != NULL)
181410958Sdme@sun.com mac_rx(xnfp->xnf_mh, NULL, mp);
18157521Sdme@sun.com
18165084Sjohnlev /*
181710958Sdme@sun.com * Transmit side processing.
181810958Sdme@sun.com *
181910958Sdme@sun.com * If a previous transmit attempt failed or we have pending
182010958Sdme@sun.com * multicast requests, clean the ring.
182110958Sdme@sun.com *
182210958Sdme@sun.com * If we previously stalled transmission and cleaning produces
182310958Sdme@sun.com * some free slots, tell upstream to attempt sending again.
182410958Sdme@sun.com *
182510958Sdme@sun.com * The odd style is to avoid acquiring xnf_txlock unless we
182610958Sdme@sun.com * will actually look inside the tx machinery.
18275084Sjohnlev */
182810958Sdme@sun.com mutex_enter(&xnfp->xnf_schedlock);
182910958Sdme@sun.com need_sched = xnfp->xnf_need_sched;
183010958Sdme@sun.com clean_ring = need_sched || (xnfp->xnf_pending_multicast > 0);
183110958Sdme@sun.com mutex_exit(&xnfp->xnf_schedlock);
183210958Sdme@sun.com
183310958Sdme@sun.com if (clean_ring) {
183410958Sdme@sun.com int free_slots;
183510958Sdme@sun.com
183610958Sdme@sun.com mutex_enter(&xnfp->xnf_txlock);
183710958Sdme@sun.com free_slots = tx_slots_get(xnfp, 0, B_FALSE);
183810958Sdme@sun.com
183910958Sdme@sun.com if (need_sched && (free_slots > 0)) {
184010958Sdme@sun.com mutex_enter(&xnfp->xnf_schedlock);
184110958Sdme@sun.com xnfp->xnf_need_sched = B_FALSE;
184210958Sdme@sun.com mutex_exit(&xnfp->xnf_schedlock);
184310958Sdme@sun.com
184410958Sdme@sun.com mac_tx_update(xnfp->xnf_mh);
184510958Sdme@sun.com }
184610958Sdme@sun.com mutex_exit(&xnfp->xnf_txlock);
18477521Sdme@sun.com }
18485084Sjohnlev
18497521Sdme@sun.com return (DDI_INTR_CLAIMED);
18505084Sjohnlev }
18515084Sjohnlev
18525084Sjohnlev /*
18535084Sjohnlev * xnf_start() -- start the board receiving and enable interrupts.
18545084Sjohnlev */
18555084Sjohnlev static int
xnf_start(void * arg)18565084Sjohnlev xnf_start(void *arg)
18575084Sjohnlev {
18585084Sjohnlev xnf_t *xnfp = arg;
18595084Sjohnlev
18605084Sjohnlev #ifdef XNF_DEBUG
186110958Sdme@sun.com if (xnf_debug & XNF_DEBUG_TRACE)
18625084Sjohnlev printf("xnf%d start(0x%p)\n",
18635741Smrj ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
18645084Sjohnlev #endif
18655084Sjohnlev
186610958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
18675741Smrj mutex_enter(&xnfp->xnf_txlock);
18685084Sjohnlev
18695084Sjohnlev /* Accept packets from above. */
18705741Smrj xnfp->xnf_running = B_TRUE;
18715084Sjohnlev
18725741Smrj mutex_exit(&xnfp->xnf_txlock);
187310958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
18745084Sjohnlev
18755084Sjohnlev return (0);
18765084Sjohnlev }
18775084Sjohnlev
18785084Sjohnlev /* xnf_stop() - disable hardware */
18795084Sjohnlev static void
xnf_stop(void * arg)18805084Sjohnlev xnf_stop(void *arg)
18815084Sjohnlev {
18825084Sjohnlev xnf_t *xnfp = arg;
18835084Sjohnlev
18845084Sjohnlev #ifdef XNF_DEBUG
188510958Sdme@sun.com if (xnf_debug & XNF_DEBUG_TRACE)
18865084Sjohnlev printf("xnf%d stop(0x%p)\n",
18875741Smrj ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
18885084Sjohnlev #endif
18895084Sjohnlev
189010958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
18915741Smrj mutex_enter(&xnfp->xnf_txlock);
18925084Sjohnlev
18935741Smrj xnfp->xnf_running = B_FALSE;
18945084Sjohnlev
18955741Smrj mutex_exit(&xnfp->xnf_txlock);
189610958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
18975084Sjohnlev }
18985084Sjohnlev
18995084Sjohnlev /*
190010958Sdme@sun.com * Hang buffer `bdesc' on the RX ring.
19015084Sjohnlev */
19025084Sjohnlev static void
xnf_rxbuf_hang(xnf_t * xnfp,xnf_buf_t * bdesc)190310958Sdme@sun.com xnf_rxbuf_hang(xnf_t *xnfp, xnf_buf_t *bdesc)
19045084Sjohnlev {
190510958Sdme@sun.com netif_rx_request_t *reqp;
190610958Sdme@sun.com RING_IDX hang_ix;
190710958Sdme@sun.com
190810958Sdme@sun.com ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
190910958Sdme@sun.com
19105741Smrj reqp = RING_GET_REQUEST(&xnfp->xnf_rx_ring,
19115741Smrj xnfp->xnf_rx_ring.req_prod_pvt);
19125741Smrj hang_ix = (RING_IDX) (reqp - RING_GET_REQUEST(&xnfp->xnf_rx_ring, 0));
191310958Sdme@sun.com ASSERT(xnfp->xnf_rx_pkt_info[hang_ix] == NULL);
191410958Sdme@sun.com
191510958Sdme@sun.com reqp->id = bdesc->id = hang_ix;
19165084Sjohnlev reqp->gref = bdesc->grant_ref;
191710958Sdme@sun.com
191810958Sdme@sun.com xnfp->xnf_rx_pkt_info[hang_ix] = bdesc;
19195741Smrj xnfp->xnf_rx_ring.req_prod_pvt++;
192010958Sdme@sun.com
192110958Sdme@sun.com xnfp->xnf_rx_new_buffers_posted = B_TRUE;
19225084Sjohnlev }
19235084Sjohnlev
192410958Sdme@sun.com /*
192510958Sdme@sun.com * Collect packets from the RX ring, storing them in `xnfp' for later
192610958Sdme@sun.com * use.
192710958Sdme@sun.com */
192810958Sdme@sun.com static void
xnf_rx_collect(xnf_t * xnfp)192910958Sdme@sun.com xnf_rx_collect(xnf_t *xnfp)
19305741Smrj {
193110958Sdme@sun.com mblk_t *head, *tail;
193210958Sdme@sun.com
193310958Sdme@sun.com ASSERT(MUTEX_HELD(&xnfp->xnf_rxlock));
19345741Smrj
19355741Smrj /*
193610958Sdme@sun.com * Loop over unconsumed responses:
19375741Smrj * 1. get a response
19385741Smrj * 2. take corresponding buffer off recv. ring
19395741Smrj * 3. indicate this by setting slot to NULL
19405741Smrj * 4. create a new message and
19415741Smrj * 5. copy data in, adjust ptr
19425741Smrj */
19435741Smrj
19445741Smrj head = tail = NULL;
19455741Smrj
19465741Smrj while (RING_HAS_UNCONSUMED_RESPONSES(&xnfp->xnf_rx_ring)) {
194710958Sdme@sun.com netif_rx_response_t *rxpkt;
194810958Sdme@sun.com xnf_buf_t *bdesc;
194910958Sdme@sun.com ssize_t len;
195010958Sdme@sun.com size_t off;
195110958Sdme@sun.com mblk_t *mp = NULL;
195210958Sdme@sun.com boolean_t hwcsum = B_FALSE;
195310958Sdme@sun.com grant_ref_t ref;
19545741Smrj
19555741Smrj /* 1. */
19565741Smrj rxpkt = RING_GET_RESPONSE(&xnfp->xnf_rx_ring,
19575741Smrj xnfp->xnf_rx_ring.rsp_cons);
19585741Smrj
195910958Sdme@sun.com DTRACE_PROBE4(xnf_rx_got_rsp, int, (int)rxpkt->id,
196010958Sdme@sun.com int, (int)rxpkt->offset,
196110958Sdme@sun.com int, (int)rxpkt->flags,
196210958Sdme@sun.com int, (int)rxpkt->status);
19635741Smrj
19645741Smrj /*
19655741Smrj * 2.
196610958Sdme@sun.com */
196710958Sdme@sun.com bdesc = xnfp->xnf_rx_pkt_info[rxpkt->id];
196810958Sdme@sun.com
196910958Sdme@sun.com /*
197010958Sdme@sun.com * 3.
19715741Smrj */
197210958Sdme@sun.com xnfp->xnf_rx_pkt_info[rxpkt->id] = NULL;
19735741Smrj ASSERT(bdesc->id == rxpkt->id);
197410958Sdme@sun.com
197510958Sdme@sun.com ref = bdesc->grant_ref;
197610958Sdme@sun.com off = rxpkt->offset;
197710958Sdme@sun.com len = rxpkt->status;
197810958Sdme@sun.com
19796899Scz147101 if (!xnfp->xnf_running) {
198010958Sdme@sun.com DTRACE_PROBE4(xnf_rx_not_running,
198110958Sdme@sun.com int, rxpkt->status,
19826899Scz147101 char *, bdesc->buf, int, rxpkt->offset,
19836899Scz147101 char *, ((char *)bdesc->buf) + rxpkt->offset);
198410958Sdme@sun.com
19856899Scz147101 xnfp->xnf_stat_drop++;
198610958Sdme@sun.com
198710958Sdme@sun.com } else if (len <= 0) {
198810958Sdme@sun.com DTRACE_PROBE4(xnf_rx_pkt_status_negative,
198910958Sdme@sun.com int, rxpkt->status,
19905741Smrj char *, bdesc->buf, int, rxpkt->offset,
19915741Smrj char *, ((char *)bdesc->buf) + rxpkt->offset);
199210958Sdme@sun.com
19935741Smrj xnfp->xnf_stat_errrx++;
199410958Sdme@sun.com
199510958Sdme@sun.com switch (len) {
199610958Sdme@sun.com case 0:
19975741Smrj xnfp->xnf_stat_runt++;
199810958Sdme@sun.com break;
199910958Sdme@sun.com case NETIF_RSP_ERROR:
20005741Smrj xnfp->xnf_stat_mac_rcv_error++;
200110958Sdme@sun.com break;
200210958Sdme@sun.com case NETIF_RSP_DROPPED:
20035741Smrj xnfp->xnf_stat_norxbuf++;
200410958Sdme@sun.com break;
200510958Sdme@sun.com }
200610958Sdme@sun.com
200710958Sdme@sun.com } else if (bdesc->grant_ref == INVALID_GRANT_REF) {
200810958Sdme@sun.com cmn_err(CE_WARN, "Bad rx grant reference %d "
200910958Sdme@sun.com "from domain %d", ref,
201010958Sdme@sun.com xvdi_get_oeid(xnfp->xnf_devinfo));
201110958Sdme@sun.com
201210958Sdme@sun.com } else if ((off + len) > PAGESIZE) {
201310958Sdme@sun.com cmn_err(CE_WARN, "Rx packet overflows page "
201410958Sdme@sun.com "(offset %ld, length %ld) from domain %d",
201510958Sdme@sun.com off, len, xvdi_get_oeid(xnfp->xnf_devinfo));
20165741Smrj } else {
201710958Sdme@sun.com xnf_buf_t *nbuf = NULL;
201810958Sdme@sun.com
201910958Sdme@sun.com DTRACE_PROBE4(xnf_rx_packet, int, len,
202010958Sdme@sun.com char *, bdesc->buf, int, off,
202110958Sdme@sun.com char *, ((char *)bdesc->buf) + off);
202210958Sdme@sun.com
20235741Smrj ASSERT(off + len <= PAGEOFFSET);
202410958Sdme@sun.com
20255741Smrj if (rxpkt->flags & NETRXF_data_validated)
20265741Smrj hwcsum = B_TRUE;
20275741Smrj
20285741Smrj /*
202910958Sdme@sun.com * If the packet is below a pre-determined
203010958Sdme@sun.com * size we will copy data out rather than
203110958Sdme@sun.com * replace it.
203210958Sdme@sun.com */
203310958Sdme@sun.com if (len > xnf_rx_copy_limit)
203410958Sdme@sun.com nbuf = xnf_buf_get(xnfp, KM_NOSLEEP, B_FALSE);
203510958Sdme@sun.com
203610958Sdme@sun.com /*
203710958Sdme@sun.com * If we have a replacement buffer, attempt to
203810958Sdme@sun.com * wrap the existing one with an mblk_t in
203910958Sdme@sun.com * order that the upper layers of the stack
204010958Sdme@sun.com * might use it directly.
20415741Smrj */
204210958Sdme@sun.com if (nbuf != NULL) {
204310958Sdme@sun.com mp = desballoc((unsigned char *)bdesc->buf,
204410958Sdme@sun.com bdesc->len, 0, &bdesc->free_rtn);
204510958Sdme@sun.com if (mp == NULL) {
204610958Sdme@sun.com xnfp->xnf_stat_rx_desballoc_fail++;
204710958Sdme@sun.com xnfp->xnf_stat_norxbuf++;
204810958Sdme@sun.com
204910958Sdme@sun.com xnf_buf_put(xnfp, nbuf, B_FALSE);
205010958Sdme@sun.com nbuf = NULL;
205110958Sdme@sun.com } else {
205210958Sdme@sun.com mp->b_rptr = mp->b_rptr + off;
205310958Sdme@sun.com mp->b_wptr = mp->b_rptr + len;
205410958Sdme@sun.com
205510958Sdme@sun.com /*
205610958Sdme@sun.com * Release the grant reference
205710958Sdme@sun.com * associated with this buffer
205810958Sdme@sun.com * - they are scarce and the
205910958Sdme@sun.com * upper layers of the stack
206010958Sdme@sun.com * don't need it.
206110958Sdme@sun.com */
206210958Sdme@sun.com (void) gnttab_end_foreign_access_ref(
206310958Sdme@sun.com bdesc->grant_ref, 0);
206410958Sdme@sun.com gref_put(xnfp, bdesc->grant_ref);
206510958Sdme@sun.com bdesc->grant_ref = INVALID_GRANT_REF;
206610958Sdme@sun.com
206710958Sdme@sun.com bdesc = nbuf;
206810958Sdme@sun.com }
20695741Smrj }
20705741Smrj
207110958Sdme@sun.com if (nbuf == NULL) {
20725084Sjohnlev /*
207310958Sdme@sun.com * No replacement buffer allocated -
207410958Sdme@sun.com * attempt to copy the data out and
207510958Sdme@sun.com * re-hang the existing buffer.
20765084Sjohnlev */
207710958Sdme@sun.com
207810958Sdme@sun.com /* 4. */
207910958Sdme@sun.com mp = allocb(len, BPRI_MED);
208010958Sdme@sun.com if (mp == NULL) {
208110958Sdme@sun.com xnfp->xnf_stat_rx_allocb_fail++;
20825741Smrj xnfp->xnf_stat_norxbuf++;
20835084Sjohnlev } else {
208410958Sdme@sun.com /* 5. */
20855084Sjohnlev bcopy(bdesc->buf + off, mp->b_wptr,
20865084Sjohnlev len);
208710958Sdme@sun.com mp->b_wptr += len;
20885084Sjohnlev }
20895084Sjohnlev }
20905084Sjohnlev }
209110958Sdme@sun.com
209210958Sdme@sun.com /* Re-hang the buffer. */
209310958Sdme@sun.com xnf_rxbuf_hang(xnfp, bdesc);
209410958Sdme@sun.com
209510958Sdme@sun.com if (mp != NULL) {
20965084Sjohnlev if (hwcsum) {
20975084Sjohnlev /*
20985084Sjohnlev * If the peer says that the data has
20995084Sjohnlev * been validated then we declare that
21005084Sjohnlev * the full checksum has been
21015084Sjohnlev * verified.
21025084Sjohnlev *
21035084Sjohnlev * We don't look at the "checksum
21045084Sjohnlev * blank" flag, and hence could have a
21055084Sjohnlev * packet here that we are asserting
21065084Sjohnlev * is good with a blank checksum.
21075084Sjohnlev */
2108*11878SVenu.Iyer@Sun.COM mac_hcksum_set(mp, 0, 0, 0, 0,
2109*11878SVenu.Iyer@Sun.COM HCK_FULLCKSUM_OK);
21105741Smrj xnfp->xnf_stat_rx_cksum_no_need++;
21115084Sjohnlev }
21125084Sjohnlev if (head == NULL) {
211310958Sdme@sun.com ASSERT(tail == NULL);
211410958Sdme@sun.com
211510958Sdme@sun.com head = mp;
21165084Sjohnlev } else {
211710958Sdme@sun.com ASSERT(tail != NULL);
211810958Sdme@sun.com
21195084Sjohnlev tail->b_next = mp;
21205084Sjohnlev }
212110958Sdme@sun.com tail = mp;
21225084Sjohnlev
21235084Sjohnlev ASSERT(mp->b_next == NULL);
21245084Sjohnlev
21255741Smrj xnfp->xnf_stat_ipackets++;
21265741Smrj xnfp->xnf_stat_rbytes += len;
21275084Sjohnlev }
21285084Sjohnlev
21295741Smrj xnfp->xnf_rx_ring.rsp_cons++;
21305084Sjohnlev }
21315084Sjohnlev
21325084Sjohnlev /*
213310958Sdme@sun.com * Store the mblks we have collected.
21345084Sjohnlev */
213510958Sdme@sun.com if (head != NULL) {
213610958Sdme@sun.com ASSERT(tail != NULL);
213710958Sdme@sun.com
213810958Sdme@sun.com if (xnfp->xnf_rx_head == NULL) {
213910958Sdme@sun.com ASSERT(xnfp->xnf_rx_tail == NULL);
214010958Sdme@sun.com
214110958Sdme@sun.com xnfp->xnf_rx_head = head;
214210958Sdme@sun.com } else {
214310958Sdme@sun.com ASSERT(xnfp->xnf_rx_tail != NULL);
214410958Sdme@sun.com
214510958Sdme@sun.com xnfp->xnf_rx_tail->b_next = head;
21465084Sjohnlev }
214710958Sdme@sun.com xnfp->xnf_rx_tail = tail;
21485084Sjohnlev }
21495084Sjohnlev }
21505084Sjohnlev
21515084Sjohnlev /*
21525084Sjohnlev * xnf_alloc_dma_resources() -- initialize the drivers structures
21535084Sjohnlev */
21545084Sjohnlev static int
xnf_alloc_dma_resources(xnf_t * xnfp)21555084Sjohnlev xnf_alloc_dma_resources(xnf_t *xnfp)
21565084Sjohnlev {
21575741Smrj dev_info_t *devinfo = xnfp->xnf_devinfo;
21585084Sjohnlev size_t len;
21595084Sjohnlev ddi_dma_cookie_t dma_cookie;
21605084Sjohnlev uint_t ncookies;
21615084Sjohnlev int rc;
21625084Sjohnlev caddr_t rptr;
21635084Sjohnlev
21645084Sjohnlev /*
21655084Sjohnlev * The code below allocates all the DMA data structures that
21665084Sjohnlev * need to be released when the driver is detached.
21675084Sjohnlev *
21685084Sjohnlev * Allocate page for the transmit descriptor ring.
21695084Sjohnlev */
21705084Sjohnlev if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
21715741Smrj DDI_DMA_SLEEP, 0, &xnfp->xnf_tx_ring_dma_handle) != DDI_SUCCESS)
21725084Sjohnlev goto alloc_error;
21735084Sjohnlev
21745741Smrj if (ddi_dma_mem_alloc(xnfp->xnf_tx_ring_dma_handle,
21755084Sjohnlev PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
21765084Sjohnlev DDI_DMA_SLEEP, 0, &rptr, &len,
21775741Smrj &xnfp->xnf_tx_ring_dma_acchandle) != DDI_SUCCESS) {
21785741Smrj ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
21795741Smrj xnfp->xnf_tx_ring_dma_handle = NULL;
21805084Sjohnlev goto alloc_error;
21815084Sjohnlev }
21825084Sjohnlev
21835741Smrj if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_tx_ring_dma_handle, NULL,
21845084Sjohnlev rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
21855084Sjohnlev DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
21865741Smrj ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
21875741Smrj ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
21885741Smrj xnfp->xnf_tx_ring_dma_handle = NULL;
21895741Smrj xnfp->xnf_tx_ring_dma_acchandle = NULL;
21905084Sjohnlev if (rc == DDI_DMA_NORESOURCES)
21915084Sjohnlev goto alloc_error;
21925084Sjohnlev else
21935084Sjohnlev goto error;
21945084Sjohnlev }
21955084Sjohnlev
21965084Sjohnlev ASSERT(ncookies == 1);
21975084Sjohnlev bzero(rptr, PAGESIZE);
21985084Sjohnlev /* LINTED: constant in conditional context */
21995084Sjohnlev SHARED_RING_INIT((netif_tx_sring_t *)rptr);
22005084Sjohnlev /* LINTED: constant in conditional context */
22015741Smrj FRONT_RING_INIT(&xnfp->xnf_tx_ring, (netif_tx_sring_t *)rptr, PAGESIZE);
22025741Smrj xnfp->xnf_tx_ring_phys_addr = dma_cookie.dmac_laddress;
22035084Sjohnlev
22045084Sjohnlev /*
22055084Sjohnlev * Allocate page for the receive descriptor ring.
22065084Sjohnlev */
22075084Sjohnlev if (ddi_dma_alloc_handle(devinfo, &ringbuf_dma_attr,
22085741Smrj DDI_DMA_SLEEP, 0, &xnfp->xnf_rx_ring_dma_handle) != DDI_SUCCESS)
22095084Sjohnlev goto alloc_error;
22105084Sjohnlev
22115741Smrj if (ddi_dma_mem_alloc(xnfp->xnf_rx_ring_dma_handle,
22125084Sjohnlev PAGESIZE, &accattr, DDI_DMA_CONSISTENT,
22135084Sjohnlev DDI_DMA_SLEEP, 0, &rptr, &len,
22145741Smrj &xnfp->xnf_rx_ring_dma_acchandle) != DDI_SUCCESS) {
22155741Smrj ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22165741Smrj xnfp->xnf_rx_ring_dma_handle = NULL;
22175084Sjohnlev goto alloc_error;
22185084Sjohnlev }
22195084Sjohnlev
22205741Smrj if ((rc = ddi_dma_addr_bind_handle(xnfp->xnf_rx_ring_dma_handle, NULL,
22215084Sjohnlev rptr, PAGESIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
22225084Sjohnlev DDI_DMA_SLEEP, 0, &dma_cookie, &ncookies)) != DDI_DMA_MAPPED) {
22235741Smrj ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
22245741Smrj ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22255741Smrj xnfp->xnf_rx_ring_dma_handle = NULL;
22265741Smrj xnfp->xnf_rx_ring_dma_acchandle = NULL;
22275084Sjohnlev if (rc == DDI_DMA_NORESOURCES)
22285084Sjohnlev goto alloc_error;
22295084Sjohnlev else
22305084Sjohnlev goto error;
22315084Sjohnlev }
22325084Sjohnlev
22335084Sjohnlev ASSERT(ncookies == 1);
22345084Sjohnlev bzero(rptr, PAGESIZE);
22355084Sjohnlev /* LINTED: constant in conditional context */
22365084Sjohnlev SHARED_RING_INIT((netif_rx_sring_t *)rptr);
22375084Sjohnlev /* LINTED: constant in conditional context */
22385741Smrj FRONT_RING_INIT(&xnfp->xnf_rx_ring, (netif_rx_sring_t *)rptr, PAGESIZE);
22395741Smrj xnfp->xnf_rx_ring_phys_addr = dma_cookie.dmac_laddress;
22405084Sjohnlev
22415084Sjohnlev return (DDI_SUCCESS);
22425084Sjohnlev
22435084Sjohnlev alloc_error:
22445084Sjohnlev cmn_err(CE_WARN, "xnf%d: could not allocate enough DMA memory",
22455741Smrj ddi_get_instance(xnfp->xnf_devinfo));
22465084Sjohnlev error:
22475084Sjohnlev xnf_release_dma_resources(xnfp);
22485084Sjohnlev return (DDI_FAILURE);
22495084Sjohnlev }
22505084Sjohnlev
22515084Sjohnlev /*
22525084Sjohnlev * Release all DMA resources in the opposite order from acquisition
22535084Sjohnlev */
22545084Sjohnlev static void
xnf_release_dma_resources(xnf_t * xnfp)22555084Sjohnlev xnf_release_dma_resources(xnf_t *xnfp)
22565084Sjohnlev {
22575084Sjohnlev int i;
22585084Sjohnlev
22595084Sjohnlev /*
22605084Sjohnlev * Free receive buffers which are currently associated with
226110958Sdme@sun.com * descriptors.
22625084Sjohnlev */
226310958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
226410958Sdme@sun.com for (i = 0; i < NET_RX_RING_SIZE; i++) {
226510958Sdme@sun.com xnf_buf_t *bp;
226610958Sdme@sun.com
226710958Sdme@sun.com if ((bp = xnfp->xnf_rx_pkt_info[i]) == NULL)
22685084Sjohnlev continue;
226910958Sdme@sun.com xnfp->xnf_rx_pkt_info[i] = NULL;
227010958Sdme@sun.com xnf_buf_put(xnfp, bp, B_FALSE);
22715084Sjohnlev }
227210958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
227310958Sdme@sun.com
227410958Sdme@sun.com /* Free the receive ring buffer. */
22755741Smrj if (xnfp->xnf_rx_ring_dma_acchandle != NULL) {
22765741Smrj (void) ddi_dma_unbind_handle(xnfp->xnf_rx_ring_dma_handle);
22775741Smrj ddi_dma_mem_free(&xnfp->xnf_rx_ring_dma_acchandle);
22785741Smrj ddi_dma_free_handle(&xnfp->xnf_rx_ring_dma_handle);
22795741Smrj xnfp->xnf_rx_ring_dma_acchandle = NULL;
22805084Sjohnlev }
228110958Sdme@sun.com /* Free the transmit ring buffer. */
22825741Smrj if (xnfp->xnf_tx_ring_dma_acchandle != NULL) {
22835741Smrj (void) ddi_dma_unbind_handle(xnfp->xnf_tx_ring_dma_handle);
22845741Smrj ddi_dma_mem_free(&xnfp->xnf_tx_ring_dma_acchandle);
22855741Smrj ddi_dma_free_handle(&xnfp->xnf_tx_ring_dma_handle);
22865741Smrj xnfp->xnf_tx_ring_dma_acchandle = NULL;
22875084Sjohnlev }
22886899Scz147101
22895084Sjohnlev }
22905084Sjohnlev
22915084Sjohnlev /*
229210958Sdme@sun.com * Release any packets and associated structures used by the TX ring.
22935084Sjohnlev */
22945084Sjohnlev static void
xnf_release_mblks(xnf_t * xnfp)229510958Sdme@sun.com xnf_release_mblks(xnf_t *xnfp)
22965084Sjohnlev {
229710958Sdme@sun.com RING_IDX i;
229810958Sdme@sun.com xnf_txid_t *tidp;
229910958Sdme@sun.com
230010958Sdme@sun.com for (i = 0, tidp = &xnfp->xnf_tx_pkt_id[0];
230110958Sdme@sun.com i < NET_TX_RING_SIZE;
230210958Sdme@sun.com i++, tidp++) {
230310958Sdme@sun.com xnf_txbuf_t *txp = tidp->txbuf;
230410958Sdme@sun.com
230510958Sdme@sun.com if (txp != NULL) {
230610958Sdme@sun.com ASSERT(txp->tx_mp != NULL);
230710958Sdme@sun.com freemsg(txp->tx_mp);
230810958Sdme@sun.com
230910958Sdme@sun.com txid_put(xnfp, tidp);
231010958Sdme@sun.com kmem_cache_free(xnfp->xnf_tx_buf_cache, txp);
231110958Sdme@sun.com }
23125741Smrj }
23135084Sjohnlev }
23145084Sjohnlev
231510958Sdme@sun.com static int
xnf_buf_constructor(void * buf,void * arg,int kmflag)231610958Sdme@sun.com xnf_buf_constructor(void *buf, void *arg, int kmflag)
23175084Sjohnlev {
231810958Sdme@sun.com int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
231910958Sdme@sun.com xnf_buf_t *bdesc = buf;
232010958Sdme@sun.com xnf_t *xnfp = arg;
232110958Sdme@sun.com ddi_dma_cookie_t dma_cookie;
232210958Sdme@sun.com uint_t ncookies;
23235084Sjohnlev size_t len;
23245084Sjohnlev
232510958Sdme@sun.com if (kmflag & KM_NOSLEEP)
232610958Sdme@sun.com ddiflags = DDI_DMA_DONTWAIT;
232710958Sdme@sun.com
232810958Sdme@sun.com /* Allocate a DMA access handle for the buffer. */
232910958Sdme@sun.com if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
233010958Sdme@sun.com ddiflags, 0, &bdesc->dma_handle) != DDI_SUCCESS)
23315084Sjohnlev goto failure;
23325084Sjohnlev
233310958Sdme@sun.com /* Allocate DMA-able memory for buffer. */
23345084Sjohnlev if (ddi_dma_mem_alloc(bdesc->dma_handle,
233510958Sdme@sun.com PAGESIZE, &data_accattr, DDI_DMA_STREAMING, ddiflags, 0,
23365084Sjohnlev &bdesc->buf, &len, &bdesc->acc_handle) != DDI_SUCCESS)
23375741Smrj goto failure_1;
23385084Sjohnlev
233910958Sdme@sun.com /* Bind to virtual address of buffer to get physical address. */
234010958Sdme@sun.com if (ddi_dma_addr_bind_handle(bdesc->dma_handle, NULL,
234110958Sdme@sun.com bdesc->buf, len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
234210958Sdme@sun.com ddiflags, 0, &dma_cookie, &ncookies) != DDI_DMA_MAPPED)
234310958Sdme@sun.com goto failure_2;
234410958Sdme@sun.com ASSERT(ncookies == 1);
234510958Sdme@sun.com
234610958Sdme@sun.com bdesc->free_rtn.free_func = xnf_buf_recycle;
234710958Sdme@sun.com bdesc->free_rtn.free_arg = (caddr_t)bdesc;
23485084Sjohnlev bdesc->xnfp = xnfp;
23495084Sjohnlev bdesc->buf_phys = dma_cookie.dmac_laddress;
235010958Sdme@sun.com bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
235110958Sdme@sun.com bdesc->len = dma_cookie.dmac_size;
235210958Sdme@sun.com bdesc->grant_ref = INVALID_GRANT_REF;
235310958Sdme@sun.com bdesc->gen = xnfp->xnf_gen;
235410958Sdme@sun.com
235510958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_buf_allocated, 1);
235610958Sdme@sun.com
235710958Sdme@sun.com return (0);
23585084Sjohnlev
23595741Smrj failure_2:
23605084Sjohnlev ddi_dma_mem_free(&bdesc->acc_handle);
23615084Sjohnlev
23625741Smrj failure_1:
23635084Sjohnlev ddi_dma_free_handle(&bdesc->dma_handle);
23645084Sjohnlev
23655084Sjohnlev failure:
236610958Sdme@sun.com
236711588Sdavid.edmondson@sun.com ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
236810958Sdme@sun.com return (-1);
236910958Sdme@sun.com }
237010958Sdme@sun.com
237110958Sdme@sun.com static void
xnf_buf_destructor(void * buf,void * arg)237210958Sdme@sun.com xnf_buf_destructor(void *buf, void *arg)
237310958Sdme@sun.com {
237410958Sdme@sun.com xnf_buf_t *bdesc = buf;
237510958Sdme@sun.com xnf_t *xnfp = arg;
237610958Sdme@sun.com
237710958Sdme@sun.com (void) ddi_dma_unbind_handle(bdesc->dma_handle);
237810958Sdme@sun.com ddi_dma_mem_free(&bdesc->acc_handle);
237910958Sdme@sun.com ddi_dma_free_handle(&bdesc->dma_handle);
238010958Sdme@sun.com
238110958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_buf_allocated, -1);
238210958Sdme@sun.com }
238310958Sdme@sun.com
238410958Sdme@sun.com static xnf_buf_t *
xnf_buf_get(xnf_t * xnfp,int flags,boolean_t readonly)238510958Sdme@sun.com xnf_buf_get(xnf_t *xnfp, int flags, boolean_t readonly)
238610958Sdme@sun.com {
238710958Sdme@sun.com grant_ref_t gref;
238810958Sdme@sun.com xnf_buf_t *bufp;
238910958Sdme@sun.com
239010958Sdme@sun.com /*
239110958Sdme@sun.com * Usually grant references are more scarce than memory, so we
239210958Sdme@sun.com * attempt to acquire a grant reference first.
239310958Sdme@sun.com */
239410958Sdme@sun.com gref = gref_get(xnfp);
239510958Sdme@sun.com if (gref == INVALID_GRANT_REF)
239610958Sdme@sun.com return (NULL);
239710958Sdme@sun.com
239810958Sdme@sun.com bufp = kmem_cache_alloc(xnfp->xnf_buf_cache, flags);
239910958Sdme@sun.com if (bufp == NULL) {
240010958Sdme@sun.com gref_put(xnfp, gref);
240110958Sdme@sun.com return (NULL);
240210958Sdme@sun.com }
240310958Sdme@sun.com
240410958Sdme@sun.com ASSERT(bufp->grant_ref == INVALID_GRANT_REF);
240510958Sdme@sun.com
240610958Sdme@sun.com bufp->grant_ref = gref;
240710958Sdme@sun.com
240810958Sdme@sun.com if (bufp->gen != xnfp->xnf_gen)
240910958Sdme@sun.com xnf_buf_refresh(bufp);
241010958Sdme@sun.com
241110958Sdme@sun.com gnttab_grant_foreign_access_ref(bufp->grant_ref,
241210958Sdme@sun.com xvdi_get_oeid(bufp->xnfp->xnf_devinfo),
241310958Sdme@sun.com bufp->buf_mfn, readonly ? 1 : 0);
241410958Sdme@sun.com
241510958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_buf_outstanding, 1);
241610958Sdme@sun.com
241710958Sdme@sun.com return (bufp);
241810958Sdme@sun.com }
241910958Sdme@sun.com
242010958Sdme@sun.com static void
xnf_buf_put(xnf_t * xnfp,xnf_buf_t * bufp,boolean_t readonly)242110958Sdme@sun.com xnf_buf_put(xnf_t *xnfp, xnf_buf_t *bufp, boolean_t readonly)
242210958Sdme@sun.com {
242310958Sdme@sun.com if (bufp->grant_ref != INVALID_GRANT_REF) {
242410958Sdme@sun.com (void) gnttab_end_foreign_access_ref(
242510958Sdme@sun.com bufp->grant_ref, readonly ? 1 : 0);
242610958Sdme@sun.com gref_put(xnfp, bufp->grant_ref);
242710958Sdme@sun.com bufp->grant_ref = INVALID_GRANT_REF;
242810958Sdme@sun.com }
242910958Sdme@sun.com
243010958Sdme@sun.com kmem_cache_free(xnfp->xnf_buf_cache, bufp);
243110958Sdme@sun.com
243210958Sdme@sun.com atomic_add_64(&xnfp->xnf_stat_buf_outstanding, -1);
243310958Sdme@sun.com }
243410958Sdme@sun.com
243510958Sdme@sun.com /*
243610958Sdme@sun.com * Refresh any cached data about a buffer after resume.
243710958Sdme@sun.com */
243810958Sdme@sun.com static void
xnf_buf_refresh(xnf_buf_t * bdesc)243910958Sdme@sun.com xnf_buf_refresh(xnf_buf_t *bdesc)
244010958Sdme@sun.com {
244110958Sdme@sun.com bdesc->buf_mfn = pfn_to_mfn(xnf_btop(bdesc->buf_phys));
244210958Sdme@sun.com bdesc->gen = bdesc->xnfp->xnf_gen;
244310958Sdme@sun.com }
244410958Sdme@sun.com
244510958Sdme@sun.com /*
244610958Sdme@sun.com * Streams `freeb' routine for `xnf_buf_t' when used as transmit
244710958Sdme@sun.com * look-aside buffers.
244810958Sdme@sun.com */
244910958Sdme@sun.com static void
xnf_buf_recycle(xnf_buf_t * bdesc)245010958Sdme@sun.com xnf_buf_recycle(xnf_buf_t *bdesc)
245110958Sdme@sun.com {
245210958Sdme@sun.com xnf_t *xnfp = bdesc->xnfp;
245310958Sdme@sun.com
245410958Sdme@sun.com xnf_buf_put(xnfp, bdesc, B_TRUE);
245510958Sdme@sun.com }
245610958Sdme@sun.com
245710958Sdme@sun.com static int
xnf_tx_buf_constructor(void * buf,void * arg,int kmflag)245810958Sdme@sun.com xnf_tx_buf_constructor(void *buf, void *arg, int kmflag)
245910958Sdme@sun.com {
246011588Sdavid.edmondson@sun.com int (*ddiflags)(caddr_t) = DDI_DMA_SLEEP;
246110958Sdme@sun.com xnf_txbuf_t *txp = buf;
246210958Sdme@sun.com xnf_t *xnfp = arg;
246310958Sdme@sun.com
246411588Sdavid.edmondson@sun.com if (kmflag & KM_NOSLEEP)
246511588Sdavid.edmondson@sun.com ddiflags = DDI_DMA_DONTWAIT;
246611588Sdavid.edmondson@sun.com
246710958Sdme@sun.com if (ddi_dma_alloc_handle(xnfp->xnf_devinfo, &buf_dma_attr,
246811588Sdavid.edmondson@sun.com ddiflags, 0, &txp->tx_dma_handle) != DDI_SUCCESS) {
246911588Sdavid.edmondson@sun.com ASSERT(kmflag & KM_NOSLEEP); /* Cannot fail for KM_SLEEP. */
247010958Sdme@sun.com return (-1);
247111588Sdavid.edmondson@sun.com }
247210958Sdme@sun.com
247310958Sdme@sun.com return (0);
247410958Sdme@sun.com }
247510958Sdme@sun.com
247610958Sdme@sun.com static void
xnf_tx_buf_destructor(void * buf,void * arg)247710958Sdme@sun.com xnf_tx_buf_destructor(void *buf, void *arg)
247810958Sdme@sun.com {
247910958Sdme@sun.com _NOTE(ARGUNUSED(arg));
248010958Sdme@sun.com xnf_txbuf_t *txp = buf;
248110958Sdme@sun.com
248210958Sdme@sun.com ddi_dma_free_handle(&txp->tx_dma_handle);
24835084Sjohnlev }
24845084Sjohnlev
24855741Smrj /*
24865741Smrj * Statistics.
24875741Smrj */
24885741Smrj static char *xnf_aux_statistics[] = {
24895741Smrj "tx_cksum_deferred",
24905741Smrj "rx_cksum_no_need",
24915741Smrj "interrupts",
24925741Smrj "unclaimed_interrupts",
24935741Smrj "tx_pullup",
24945741Smrj "tx_pagebndry",
24955741Smrj "tx_attempt",
249610958Sdme@sun.com "buf_allocated",
249710958Sdme@sun.com "buf_outstanding",
249810958Sdme@sun.com "gref_outstanding",
249910958Sdme@sun.com "gref_failure",
250010958Sdme@sun.com "gref_peak",
250110958Sdme@sun.com "rx_allocb_fail",
250210958Sdme@sun.com "rx_desballoc_fail",
25035741Smrj };
25045741Smrj
25055741Smrj static int
xnf_kstat_aux_update(kstat_t * ksp,int flag)25065741Smrj xnf_kstat_aux_update(kstat_t *ksp, int flag)
25075741Smrj {
25085741Smrj xnf_t *xnfp;
25095741Smrj kstat_named_t *knp;
25105741Smrj
25115741Smrj if (flag != KSTAT_READ)
25125741Smrj return (EACCES);
25135741Smrj
25145741Smrj xnfp = ksp->ks_private;
25155741Smrj knp = ksp->ks_data;
25165741Smrj
25175741Smrj /*
25185741Smrj * Assignment order must match that of the names in
25195741Smrj * xnf_aux_statistics.
25205741Smrj */
25215741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_tx_cksum_deferred;
25225741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_rx_cksum_no_need;
25235741Smrj
25245741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_interrupts;
25255741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_unclaimed_interrupts;
25265741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_tx_pullup;
25275741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_tx_pagebndry;
25285741Smrj (knp++)->value.ui64 = xnfp->xnf_stat_tx_attempt;
252910958Sdme@sun.com
253010958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_buf_allocated;
253110958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_buf_outstanding;
253210958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_gref_outstanding;
253310958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_gref_failure;
253410958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_gref_peak;
253510958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_rx_allocb_fail;
253610958Sdme@sun.com (knp++)->value.ui64 = xnfp->xnf_stat_rx_desballoc_fail;
25375741Smrj
25385741Smrj return (0);
25395741Smrj }
25405741Smrj
25415741Smrj static boolean_t
xnf_kstat_init(xnf_t * xnfp)25425741Smrj xnf_kstat_init(xnf_t *xnfp)
25435741Smrj {
25445741Smrj int nstat = sizeof (xnf_aux_statistics) /
25455741Smrj sizeof (xnf_aux_statistics[0]);
25465741Smrj char **cp = xnf_aux_statistics;
25475741Smrj kstat_named_t *knp;
25485741Smrj
25495741Smrj /*
25505741Smrj * Create and initialise kstats.
25515741Smrj */
25525741Smrj if ((xnfp->xnf_kstat_aux = kstat_create("xnf",
25535741Smrj ddi_get_instance(xnfp->xnf_devinfo),
25545741Smrj "aux_statistics", "net", KSTAT_TYPE_NAMED,
25555741Smrj nstat, 0)) == NULL)
25565741Smrj return (B_FALSE);
25575741Smrj
25585741Smrj xnfp->xnf_kstat_aux->ks_private = xnfp;
25595741Smrj xnfp->xnf_kstat_aux->ks_update = xnf_kstat_aux_update;
25605741Smrj
25615741Smrj knp = xnfp->xnf_kstat_aux->ks_data;
25625741Smrj while (nstat > 0) {
25635741Smrj kstat_named_init(knp, *cp, KSTAT_DATA_UINT64);
25645741Smrj
25655741Smrj knp++;
25665741Smrj cp++;
25675741Smrj nstat--;
25685741Smrj }
25695741Smrj
25705741Smrj kstat_install(xnfp->xnf_kstat_aux);
25715741Smrj
25725741Smrj return (B_TRUE);
25735741Smrj }
25745741Smrj
25755084Sjohnlev static int
xnf_stat(void * arg,uint_t stat,uint64_t * val)25765084Sjohnlev xnf_stat(void *arg, uint_t stat, uint64_t *val)
25775084Sjohnlev {
25785084Sjohnlev xnf_t *xnfp = arg;
25795084Sjohnlev
258010958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
25815741Smrj mutex_enter(&xnfp->xnf_txlock);
25825084Sjohnlev
25835741Smrj #define mac_stat(q, r) \
25845084Sjohnlev case (MAC_STAT_##q): \
25855741Smrj *val = xnfp->xnf_stat_##r; \
25865741Smrj break
25875741Smrj
25885741Smrj #define ether_stat(q, r) \
25895741Smrj case (ETHER_STAT_##q): \
25905741Smrj *val = xnfp->xnf_stat_##r; \
25915084Sjohnlev break
25925084Sjohnlev
25935084Sjohnlev switch (stat) {
25945084Sjohnlev
25955741Smrj mac_stat(IPACKETS, ipackets);
25965741Smrj mac_stat(OPACKETS, opackets);
25975741Smrj mac_stat(RBYTES, rbytes);
25985741Smrj mac_stat(OBYTES, obytes);
25995741Smrj mac_stat(NORCVBUF, norxbuf);
26005741Smrj mac_stat(IERRORS, errrx);
26015741Smrj mac_stat(NOXMTBUF, tx_defer);
26025741Smrj
26035741Smrj ether_stat(MACRCV_ERRORS, mac_rcv_error);
26045741Smrj ether_stat(TOOSHORT_ERRORS, runt);
26055084Sjohnlev
26067397SMax.Zhen@Sun.COM /* always claim to be in full duplex mode */
26077397SMax.Zhen@Sun.COM case ETHER_STAT_LINK_DUPLEX:
26087397SMax.Zhen@Sun.COM *val = LINK_DUPLEX_FULL;
26097397SMax.Zhen@Sun.COM break;
26107397SMax.Zhen@Sun.COM
26117397SMax.Zhen@Sun.COM /* always claim to be at 1Gb/s link speed */
26127397SMax.Zhen@Sun.COM case MAC_STAT_IFSPEED:
26137397SMax.Zhen@Sun.COM *val = 1000000000ull;
26147397SMax.Zhen@Sun.COM break;
26157397SMax.Zhen@Sun.COM
26165084Sjohnlev default:
26175741Smrj mutex_exit(&xnfp->xnf_txlock);
261810958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
26195084Sjohnlev
26205084Sjohnlev return (ENOTSUP);
26215084Sjohnlev }
26225084Sjohnlev
26235741Smrj #undef mac_stat
26245741Smrj #undef ether_stat
26255084Sjohnlev
26265741Smrj mutex_exit(&xnfp->xnf_txlock);
262710958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
26285084Sjohnlev
26295084Sjohnlev return (0);
26305084Sjohnlev }
26315084Sjohnlev
26325084Sjohnlev static boolean_t
xnf_getcapab(void * arg,mac_capab_t cap,void * cap_data)26335084Sjohnlev xnf_getcapab(void *arg, mac_capab_t cap, void *cap_data)
26345084Sjohnlev {
263510958Sdme@sun.com _NOTE(ARGUNUSED(arg));
26365084Sjohnlev
26375084Sjohnlev switch (cap) {
26385084Sjohnlev case MAC_CAPAB_HCKSUM: {
26395084Sjohnlev uint32_t *capab = cap_data;
26405084Sjohnlev
26415702Sdme /*
26427351Sdme@sun.com * Whilst the flag used to communicate with the IO
26437351Sdme@sun.com * domain is called "NETTXF_csum_blank", the checksum
26447351Sdme@sun.com * in the packet must contain the pseudo-header
26457351Sdme@sun.com * checksum and not zero.
26465702Sdme *
26477351Sdme@sun.com * To help out the IO domain, we might use
26487351Sdme@sun.com * HCKSUM_INET_PARTIAL. Unfortunately our stack will
26497351Sdme@sun.com * then use checksum offload for IPv6 packets, which
26507351Sdme@sun.com * the IO domain can't handle.
26517351Sdme@sun.com *
26527351Sdme@sun.com * As a result, we declare outselves capable of
26537351Sdme@sun.com * HCKSUM_INET_FULL_V4. This means that we receive
26547351Sdme@sun.com * IPv4 packets from the stack with a blank checksum
26557351Sdme@sun.com * field and must insert the pseudo-header checksum
26567351Sdme@sun.com * before passing the packet to the IO domain.
26575702Sdme */
265810958Sdme@sun.com *capab = HCKSUM_INET_FULL_V4;
26595084Sjohnlev break;
26605084Sjohnlev }
26615084Sjohnlev default:
26625084Sjohnlev return (B_FALSE);
26635084Sjohnlev }
26645084Sjohnlev
26655084Sjohnlev return (B_TRUE);
26665084Sjohnlev }
26675084Sjohnlev
266810958Sdme@sun.com /*
266910958Sdme@sun.com * The state of the peer has changed - react accordingly.
267010958Sdme@sun.com */
26715084Sjohnlev static void
oe_state_change(dev_info_t * dip,ddi_eventcookie_t id,void * arg,void * impl_data)26725084Sjohnlev oe_state_change(dev_info_t *dip, ddi_eventcookie_t id,
26735084Sjohnlev void *arg, void *impl_data)
26745084Sjohnlev {
267510958Sdme@sun.com _NOTE(ARGUNUSED(id, arg));
26765084Sjohnlev xnf_t *xnfp = ddi_get_driver_private(dip);
26775084Sjohnlev XenbusState new_state = *(XenbusState *)impl_data;
26785084Sjohnlev
26795084Sjohnlev ASSERT(xnfp != NULL);
26805084Sjohnlev
26815084Sjohnlev switch (new_state) {
268210958Sdme@sun.com case XenbusStateUnknown:
268310958Sdme@sun.com case XenbusStateInitialising:
268410958Sdme@sun.com case XenbusStateInitialised:
268510958Sdme@sun.com case XenbusStateClosing:
268610958Sdme@sun.com case XenbusStateClosed:
268710958Sdme@sun.com case XenbusStateReconfiguring:
268810958Sdme@sun.com case XenbusStateReconfigured:
268910958Sdme@sun.com break;
269010958Sdme@sun.com
269110958Sdme@sun.com case XenbusStateInitWait:
269210958Sdme@sun.com xnf_read_config(xnfp);
269310958Sdme@sun.com
269410958Sdme@sun.com if (!xnfp->xnf_be_rx_copy) {
269510958Sdme@sun.com cmn_err(CE_WARN,
269610958Sdme@sun.com "The xnf driver requires a dom0 that "
269710958Sdme@sun.com "supports 'feature-rx-copy'.");
269810958Sdme@sun.com (void) xvdi_switch_state(xnfp->xnf_devinfo,
269910958Sdme@sun.com XBT_NULL, XenbusStateClosed);
270010958Sdme@sun.com break;
270110958Sdme@sun.com }
270210958Sdme@sun.com
270310958Sdme@sun.com /*
270410958Sdme@sun.com * Connect to the backend.
270510958Sdme@sun.com */
270610958Sdme@sun.com xnf_be_connect(xnfp);
270710958Sdme@sun.com
270810958Sdme@sun.com /*
270910958Sdme@sun.com * Our MAC address as discovered by xnf_read_config().
271010958Sdme@sun.com */
271110958Sdme@sun.com mac_unicst_update(xnfp->xnf_mh, xnfp->xnf_mac_addr);
271210958Sdme@sun.com
271310958Sdme@sun.com break;
271410958Sdme@sun.com
27155084Sjohnlev case XenbusStateConnected:
271610958Sdme@sun.com mutex_enter(&xnfp->xnf_rxlock);
27175741Smrj mutex_enter(&xnfp->xnf_txlock);
27185084Sjohnlev
27195741Smrj xnfp->xnf_connected = B_TRUE;
27206899Scz147101 /*
272110958Sdme@sun.com * Wake up any threads waiting to send data to
272210958Sdme@sun.com * backend.
27236899Scz147101 */
272410958Sdme@sun.com cv_broadcast(&xnfp->xnf_cv_state);
27255084Sjohnlev
27265741Smrj mutex_exit(&xnfp->xnf_txlock);
272710958Sdme@sun.com mutex_exit(&xnfp->xnf_rxlock);
27285084Sjohnlev
27296899Scz147101 /*
273010958Sdme@sun.com * Kick the peer in case it missed any transmits
273110958Sdme@sun.com * request in the TX ring.
27326899Scz147101 */
27335741Smrj ec_notify_via_evtchn(xnfp->xnf_evtchn);
27346899Scz147101
27356899Scz147101 /*
273610958Sdme@sun.com * There may already be completed receive requests in
273710958Sdme@sun.com * the ring sent by backend after it gets connected
273810958Sdme@sun.com * but before we see its state change here, so we call
273910958Sdme@sun.com * xnf_intr() to handle them, if any.
27406899Scz147101 */
27416899Scz147101 (void) xnf_intr((caddr_t)xnfp);
27426899Scz147101
274310958Sdme@sun.com /*
274410958Sdme@sun.com * Mark the link up now that we are connected.
274510958Sdme@sun.com */
27467397SMax.Zhen@Sun.COM mac_link_update(xnfp->xnf_mh, LINK_STATE_UP);
27477397SMax.Zhen@Sun.COM
274810958Sdme@sun.com /*
274910958Sdme@sun.com * Tell the backend about the multicast addresses in
275010958Sdme@sun.com * which we are interested.
275110958Sdme@sun.com */
275210958Sdme@sun.com mac_multicast_refresh(xnfp->xnf_mh, NULL, xnfp, B_TRUE);
275310958Sdme@sun.com
27545084Sjohnlev break;
27555084Sjohnlev
27565084Sjohnlev default:
27575084Sjohnlev break;
27585084Sjohnlev }
27595084Sjohnlev }
2760