/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* Copyright (c) 2004 Christian Limpach.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. This section intentionally left blank.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Section 3 of the above license was updated in response to bug 6379571.
*/
/*
* xnf.c - GLDv3 network driver for domU.
*/
/*
* This driver uses four per-instance locks:
*
* xnf_gref_lock:
*
* Protects access to the grant reference list stored in
* xnf_gref_head. Grant references should be acquired and released
* using gref_get() and gref_put() respectively.
*
* xnf_schedlock:
*
* Protects:
* xnf_need_sched - used to record that a previous transmit attempt
* failed (and consequently it will be necessary to call
* mac_tx_update() when transmit resources are available).
* xnf_pending_multicast - the number of multicast requests that
* have been submitted to the backend for which we have not
* processed responses.
*
* xnf_txlock:
*
* Protects the transmit ring (xnf_tx_ring) and associated
* structures (notably xnf_tx_pkt_id and xnf_tx_pkt_id_head).
*
* xnf_rxlock:
*
* Protects the receive ring (xnf_rx_ring) and associated
* structures (notably xnf_rx_pkt_info).
*
* If driver-global state that affects both the transmit and receive
* rings is manipulated, both xnf_txlock and xnf_rxlock should be
* held, in that order.
*
* xnf_schedlock is acquired both whilst holding xnf_txlock and
* without. It should always be acquired after xnf_txlock if both are
* held.
*
* Notes:
* - atomic_add_64() is used to manipulate counters where we require
* accuracy. For counters intended only for observation by humans,
*/
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
#include <sys/mac_provider.h>
#include <sys/mac_ether.h>
#include <sys/bootinfo.h>
#include <sys/mach_mmu.h>
#ifdef XPV_HVM_DRIVER
#include <sys/xpv_support.h>
#include <sys/hypervisor.h>
#else
#include <sys/hypervisor.h>
#include <sys/evtchn_impl.h>
#include <sys/balloon_impl.h>
#endif
#define XNF_DEBUG
#endif
#ifdef XNF_DEBUG
int xnf_debug = 0;
#endif
/*
* On a 32 bit PAE system physical and machine addresses are larger
* than 32 bits. ddi_btop() on such systems take an unsigned long
* argument, and so addresses above 4G are truncated before ddi_btop()
* gets to see them. To avoid this, code the shift operation here.
*/
/*
* Should we use the multicast control feature if the backend provides
* it?
*/
/*
* Received packets below this size are copied to a new streams buffer
* rather than being desballoc'ed.
*
* This value is chosen to accommodate traffic where there are a large
* number of small packets. For data showing a typical distribution,
* see:
*
* Sinha07a:
* Rishi Sinha, Christos Papadopoulos, and John
* Heidemann. Internet Packet Size Distributions: Some
* Observations. Technical Report ISI-TR-2007-643,
* USC/Information Sciences Institute, May, 2007. Orignally
* released October 2005 as web page
*/
/* Required system entry points */
/* Required driver entry points for Nemo */
static int xnf_start(void *);
static void xnf_stop(void *);
static int xnf_set_mac_addr(void *, const uint8_t *);
static int xnf_set_promiscuous(void *, boolean_t);
/* Driver private functions */
static int xnf_alloc_dma_resources(xnf_t *);
static void xnf_release_dma_resources(xnf_t *);
static void xnf_release_mblks(xnf_t *);
static int xnf_buf_constructor(void *, void *, int);
static void xnf_buf_destructor(void *, void *);
#pragma inline(xnf_buf_get)
#pragma inline(xnf_buf_put)
static void xnf_buf_refresh(xnf_buf_t *);
#pragma inline(xnf_buf_refresh)
static void xnf_buf_recycle(xnf_buf_t *);
static int xnf_tx_buf_constructor(void *, void *, int);
static void xnf_tx_buf_destructor(void *, void *);
#pragma inline(gref_get)
#pragma inline(gref_put)
#pragma inline(txid_get)
#pragma inline(txid_put)
void xnf_send_driver_status(int, int);
static int xnf_tx_clean_ring(xnf_t *);
void *, void *);
static void xnf_rx_collect(xnf_t *);
NULL,
NULL,
};
/* DMA attributes for network ring buffer */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffffffffffULL, /* highest usable address */
0x7fffffff, /* maximum DMAable byte count */
MMU_PAGESIZE, /* alignment in bytes */
0x7ff, /* bitmap of burst sizes */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffffffffffULL, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA attributes for transmit and receive data */
DMA_ATTR_V0, /* version of this structure */
0, /* lowest usable address */
0xffffffffffffffffULL, /* highest usable address */
0x7fffffff, /* maximum DMAable byte count */
MMU_PAGESIZE, /* alignment in bytes */
0x7ff, /* bitmap of burst sizes */
1, /* minimum transfer */
0xffffffffU, /* maximum transfer */
0xffffffffffffffffULL, /* maximum segment length */
1, /* maximum number of segments */
1, /* granularity */
0, /* flags (reserved) */
};
/* DMA access attributes for registers and descriptors */
DDI_STRUCTURE_LE_ACC, /* This is a little-endian device */
};
/* DMA access attributes for data: NOT to be byte swapped. */
};
"Virtual Ethernet driver",
};
};
int
_init(void)
{
int r;
r = mod_install(&modlinkage);
if (r != DDI_SUCCESS)
return (r);
}
int
_fini(void)
{
return (EBUSY); /* XXPV should be removable */
}
int
{
}
/*
* Acquire a grant reference.
*/
static grant_ref_t
{
do {
} while ((gref == INVALID_GRANT_REF) &&
if (gref == INVALID_GRANT_REF) {
} else {
}
return (gref);
}
/*
* Release a grant reference.
*/
static void
{
}
/*
* Acquire a transmit id.
*/
static xnf_txid_t *
{
return (NULL);
return (tidp);
}
/*
* Release a transmit id.
*/
static void
{
}
/*
* Get `wanted' slots in the transmit ring, waiting for at least that
* number if `wait' is B_TRUE. Force the ring to be cleaned by setting
* `wanted' to zero.
*
* Return the number of slots available.
*/
static int
{
int slotsfree;
/* LINTED: constant in conditional context */
while (B_TRUE) {
/*
* If there are more than we need free, tell other
* people to come looking again. We hold txlock, so we
* are able to take our slots before anyone else runs.
*/
break;
if (!wait)
break;
}
return (slotsfree);
}
static int
{
RING_IDX i;
int err;
if (err <= 0) {
goto out;
}
if (err <= 0) {
goto out;
}
/*
* after a resume, but we expect to stagger on.
*/
i < NET_TX_RING_SIZE;
i++, tidp++) {
continue;
}
case TX_DATA:
== 0);
(void) gnttab_end_foreign_access_ref(
(void) ddi_dma_unbind_handle(
txp->tx_dma_handle);
} else {
}
break;
case TX_MCAST_REQ:
/*
* The request consumed two slots in the ring,
* yet only a single xnf_txid_t is used. Step
* over the empty slot.
*/
i++;
ASSERT(i < NET_TX_RING_SIZE);
break;
case TX_MCAST_RSP:
break;
}
}
/* LINTED: constant in conditional context */
/* LINTED: constant in conditional context */
/*
* Clean out any buffers currently posted to the receive ring
* before we reset it.
*/
i < NET_RX_RING_SIZE;
i++, bdescp++) {
}
}
/* LINTED: constant in conditional context */
/* LINTED: constant in conditional context */
/*
* Fill the ring with buffers.
*/
for (i = 0; i < NET_RX_RING_SIZE; i++) {
}
/* LINTED: constant in conditional context */
return (0);
out:
return (err);
}
/*
* Connect driver to back end, called to set up communication with
*/
void
{
const char *message;
char *xsname;
int err;
if (err != 0) {
return;
}
if (err != 0) {
return;
}
if (err != 0) {
message = "writing tx ring-ref";
goto abort_transaction;
}
if (err != 0) {
message = "writing rx ring-ref";
goto abort_transaction;
}
xnfp->xnf_evtchn);
if (err != 0) {
message = "writing event-channel";
goto abort_transaction;
}
if (err != 0) {
message = "writing feature-rx-notify";
goto abort_transaction;
}
if (err != 0) {
message = "writing request-rx-copy";
goto abort_transaction;
}
if (xnfp->xnf_be_mcast_control) {
"%d", 1);
if (err != 0) {
message = "writing request-multicast-control";
goto abort_transaction;
}
}
if (err != 0) {
message = "switching state to XenbusStateConnected";
goto abort_transaction;
}
if (err != 0) {
goto again;
}
return;
}
/*
* Read configuration information from xenstore.
*/
void
{
"%s", (char *)&mac[0]);
if (err != 0) {
/*
* bad: we're supposed to be set up with a proper mac
* addr. at this point
*/
return;
}
return;
}
/*
* If we fail to read the store we assume that the key is
* absent, implying an older domain at the far end. Older
* domains cannot do HV copy.
*/
if (err != 0)
be_cap = 0;
/*
* If we fail to read the store we assume that the key is
* absent, implying an older domain at the far end. Older
* domains do not support multicast control.
*/
if (err != 0)
be_cap = 0;
}
/*
* attach(9E) -- Attach a device to the system
*/
static int
{
int err;
#ifdef XNF_DEBUG
if (xnf_debug & XNF_DEBUG_DDI)
(void *)devinfo);
#endif
switch (cmd) {
case DDI_RESUME:
(void) xvdi_resume(devinfo);
(void) xvdi_alloc_evtchn(devinfo);
#ifdef XPV_HVM_DRIVER
xnfp);
#else
#endif
return (DDI_SUCCESS);
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
/*
* Allocate gld_mac_info_t and xnf_instance structures
*/
return (DDI_FAILURE);
#ifdef XPV_HVM_DRIVER
/*
* Report our version to dom0.
*/
#endif
/*
* Get the iblock cookie with which to initialize the mutexes.
*/
!= DDI_SUCCESS)
goto failure;
sizeof (xnf_buf_t), 0,
goto failure_0;
sizeof (xnf_txbuf_t), 0,
goto failure_1;
"driver data structures",
goto failure_2;
}
/* set driver private pointer now */
if (!xnf_kstat_init(xnfp))
goto failure_3;
/*
* Allocate an event channel, add the interrupt handler and
* bind it to the event channel.
*/
(void) xvdi_alloc_evtchn(devinfo);
#ifdef XPV_HVM_DRIVER
#else
#endif
if (err != 0)
goto failure_4;
!= DDI_SUCCESS)
goto failure_5;
#ifdef XPV_HVM_DRIVER
/*
* In the HVM case, this driver essentially replaces a driver for
* a 'real' PCI NIC. Without the "model" property set to
* "Ethernet controller", like the PCI code does, netbooting does
* not work correctly, as strplumb_get_netdev_path() will not find
* this interface.
*/
"Ethernet controller");
#endif
#ifdef XNF_DEBUG
if (xnf_debug_instance == NULL)
#endif
return (DDI_SUCCESS);
#ifdef XPV_HVM_DRIVER
#else
#endif
return (DDI_FAILURE);
}
/* detach(9E) -- Detach a device from the system */
static int
{
#ifdef XNF_DEBUG
if (xnf_debug & XNF_DEBUG_DDI)
#endif
switch (cmd) {
case DDI_SUSPEND:
#ifdef XPV_HVM_DRIVER
#else
#endif
/* claim link to be down after disconnect */
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
if (xnfp->xnf_connected)
return (DDI_FAILURE);
/*
* Cannot detach if we have xnf_buf_t outstanding.
*/
if (xnfp->xnf_stat_buf_allocated > 0)
return (DDI_FAILURE);
return (DDI_FAILURE);
/* Stop the receiver */
/* Remove the interrupt */
#ifdef XPV_HVM_DRIVER
#else
#endif
/* Release any pending xmit mblks */
/* Release all DMA resources */
return (DDI_SUCCESS);
}
/*
* xnf_set_mac_addr() -- set the physical network address on the board.
*/
static int
{
/*
* We can't set our macaddr.
*/
return (ENOTSUP);
}
/*
* xnf_set_multicast() -- set (enable) or disable a multicast address.
*
* in "mca". Enable if "add" is true, disable if false.
*/
static int
{
int n_slots;
/*
* If the backend does not support multicast control then we
* must assume that the right packets will just arrive.
*/
if (!xnfp->xnf_be_mcast_control)
return (0);
/*
* If we're not yet connected then claim success. This is
* acceptable because we refresh the entire set of multicast
* addresses when we get connected.
*
* We can't wait around here because the MAC layer expects
* this to be a non-blocking operation - waiting ends up
* causing a deadlock during resume.
*/
if (!xnfp->xnf_connected) {
return (0);
}
/*
* 1. Acquire two slots in the ring.
* 2. Fill in the slots.
* 3. Request notification when the operation is done.
* 4. Kick the peer.
* 5. Wait for the response via xnf_tx_clean_ring().
*/
erp = (struct netif_extra_info *)
/* Set tx_txreq.id to appease xnf_tx_clean_ring(). */
/* LINTED: constant in conditional context */
notify);
if (notify)
&xnfp->xnf_txlock);
return (result ? 0 : 1);
}
/*
* xnf_set_promiscuous() -- set or reset promiscuous mode on the board
*
*/
static int
{
/*
* We can't really do this, but we pretend that we can in
* order that snoop will work.
*/
return (0);
}
/*
* Clean buffers that we have responses for from the transmit ring.
*/
static int
{
loop:
/*
* Clean tx requests from ring that we have responses
* for.
*/
case TX_DATA:
"tx grant %d still in use by "
"backend domain",
(void) gnttab_end_foreign_access_ref(
(void) ddi_dma_unbind_handle(
txp->tx_dma_handle);
} else {
B_TRUE);
}
break;
case TX_MCAST_REQ:
break;
case TX_MCAST_RSP:
break;
default:
"invalid xnf_txbuf_t type: %d",
break;
}
}
/*
* Record the last response we dealt with so that we
* know where to start next time around.
*/
membar_enter();
}
/* LINTED: constant in conditional context */
if (work_to_do)
goto loop;
}
/*
* Allocate and fill in a look-aside buffer for the packet `mp'. Used
* to ensure that the packet is physically contiguous and contained
* within a single page.
*/
static xnf_buf_t *
{
return (NULL);
}
return (bd);
}
/*
* Insert the pseudo-header checksum into the packet `buf'.
*/
void
{
} else {
}
/* Packet should have been pulled up by the caller. */
return;
}
switch (ipha->ipha_protocol) {
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
default:
return;
}
}
/*
* Push a list of prepared packets (`txp') into the transmit ring.
*/
static xnf_txbuf_t *
{
int slots_free;
/*
* Wait until we are connected to the backend.
*/
while (!xnfp->xnf_connected)
slots_free--;
slot++;
}
/*
* Tell the peer that we sent something, if it cares.
*/
/* LINTED: constant in conditional context */
notify);
if (notify)
return (txp);
}
/*
* Send the chain of packets `mp'. Called by the MAC framework.
*/
static mblk_t *
{
int prepared;
/*
* Prepare packets for transmission.
*/
prepared = 0;
/*
* Test if this buffer includes a page
* boundary. The test assumes that the range
* b_rptr...b_wptr can include only a single
* boundary.
*/
}
}
/*
* Make sure packet isn't too large.
*/
if (length > XNF_FRAMESIZE) {
"xnf%d: oversized packet (%d bytes) dropped",
continue;
}
/*
* Loan a side buffer rather than the mblk
* itself.
*/
break;
}
} else {
int rc;
DDI_DMA_DONTWAIT, 0, &dma_cookie,
&ncookies);
if (rc != DDI_DMA_MAPPED) {
#ifdef XNF_DEBUG
if (rc != DDI_DMA_NORESOURCES)
"xnf%d: bind_handle failed (%x)",
rc);
#endif
break;
}
(void) ddi_dma_unbind_handle(
txp->tx_dma_handle);
break;
}
}
if (pflags != 0) {
/*
* If the local protocol stack requests checksum
* offload we set the 'checksum blank' flag,
* indicating to the peer that we need the checksum
* calculated for us.
*
* We _don't_ set the validated flag, because we haven't
* validated that the data and the checksum match.
*/
}
} else {
}
prepared++;
/*
* There is no point in preparing more than
* NET_TX_RING_SIZE, as we won't be able to push them
* into the ring in one go and would hence have to
* un-prepare the extra.
*/
if (prepared == NET_TX_RING_SIZE)
break;
}
#ifdef XNF_DEBUG
int notprepared = 0;
while (l != NULL) {
notprepared++;
l = l->b_next;
}
#else /* !XNF_DEBUG */
#endif /* XNF_DEBUG */
}
/*
* Push the packets we have prepared into the ring. They may
* not all go.
*/
/*
* If some packets that we prepared were not sent, unprepare
* them and add them back to the head of those we didn't
* prepare.
*/
{
int unprepared = 0;
(void) gnttab_end_foreign_access_ref(
(void) ddi_dma_unbind_handle(
} else {
}
unprepared++;
}
} else {
}
}
/*
* If any mblks are left then we have deferred for some reason
* and need to ask for a re-schedule later. This is typically
* due to the ring filling.
*/
}
return (mp);
}
/*
* Notification of RX packets. Currently no TX-complete interrupt is
* used, as we clean the TX ring lazily.
*/
static uint_t
{
/*
* Interrupts before we are connected are spurious.
*/
if (!xnfp->xnf_connected) {
return (DDI_INTR_UNCLAIMED);
}
/*
* Receive side processing.
*/
do {
/*
* Collect buffers from the ring.
*/
/*
* Interrupt me when the next receive buffer is consumed.
*/
xen_mb();
if (xnfp->xnf_rx_new_buffers_posted) {
/*
* Indicate to the peer that we have re-filled the
* receive ring, if it cares.
*/
/* LINTED: constant in conditional context */
if (notify)
}
/*
* Transmit side processing.
*
* If a previous transmit attempt failed or we have pending
* multicast requests, clean the ring.
*
* If we previously stalled transmission and cleaning produces
* some free slots, tell upstream to attempt sending again.
*
* The odd style is to avoid acquiring xnf_txlock unless we
* will actually look inside the tx machinery.
*/
if (clean_ring) {
int free_slots;
if (need_sched && (free_slots > 0)) {
}
}
return (DDI_INTR_CLAIMED);
}
/*
* xnf_start() -- start the board receiving and enable interrupts.
*/
static int
{
#ifdef XNF_DEBUG
if (xnf_debug & XNF_DEBUG_TRACE)
printf("xnf%d start(0x%p)\n",
#endif
/* Accept packets from above. */
return (0);
}
/* xnf_stop() - disable hardware */
static void
{
#ifdef XNF_DEBUG
if (xnf_debug & XNF_DEBUG_TRACE)
printf("xnf%d stop(0x%p)\n",
#endif
}
/*
* Hang buffer `bdesc' on the RX ring.
*/
static void
{
}
/*
* Collect packets from the RX ring, storing them in `xnfp' for later
* use.
*/
static void
{
/*
* Loop over unconsumed responses:
* 1. get a response
* 2. take corresponding buffer off recv. ring
* 3. indicate this by setting slot to NULL
* 4. create a new message and
* 5. copy data in, adjust ptr
*/
/* 1. */
/*
* 2.
*/
/*
* 3.
*/
if (!xnfp->xnf_running) {
xnfp->xnf_stat_drop++;
} else if (len <= 0) {
xnfp->xnf_stat_errrx++;
switch (len) {
case 0:
xnfp->xnf_stat_runt++;
break;
case NETIF_RSP_ERROR:
break;
case NETIF_RSP_DROPPED:
xnfp->xnf_stat_norxbuf++;
break;
}
"from domain %d", ref,
"(offset %ld, length %ld) from domain %d",
} else {
/*
* If the packet is below a pre-determined
* size we will copy data out rather than
* replace it.
*/
if (len > xnf_rx_copy_limit)
/*
* If we have a replacement buffer, attempt to
* wrap the existing one with an mblk_t in
* order that the upper layers of the stack
* might use it directly.
*/
xnfp->xnf_stat_norxbuf++;
} else {
/*
* Release the grant reference
* associated with this buffer
* - they are scarce and the
* upper layers of the stack
* don't need it.
*/
(void) gnttab_end_foreign_access_ref(
}
}
/*
* No replacement buffer allocated -
* attempt to copy the data out and
* re-hang the existing buffer.
*/
/* 4. */
xnfp->xnf_stat_norxbuf++;
} else {
/* 5. */
len);
}
}
}
/* Re-hang the buffer. */
if (hwcsum) {
/*
* If the peer says that the data has
* been validated then we declare that
* the full checksum has been
* verified.
*
* We don't look at the "checksum
* blank" flag, and hence could have a
* packet here that we are asserting
* is good with a blank checksum.
*/
mac_hcksum_set(mp, 0, 0, 0, 0,
}
} else {
}
}
}
/*
* Store the mblks we have collected.
*/
} else {
}
}
}
/*
* xnf_alloc_dma_resources() -- initialize the drivers structures
*/
static int
{
int rc;
/*
* The code below allocates all the DMA data structures that
* need to be released when the driver is detached.
*
* Allocate page for the transmit descriptor ring.
*/
goto alloc_error;
goto alloc_error;
}
if (rc == DDI_DMA_NORESOURCES)
goto alloc_error;
else
goto error;
}
/* LINTED: constant in conditional context */
/* LINTED: constant in conditional context */
/*
* Allocate page for the receive descriptor ring.
*/
goto alloc_error;
goto alloc_error;
}
if (rc == DDI_DMA_NORESOURCES)
goto alloc_error;
else
goto error;
}
/* LINTED: constant in conditional context */
/* LINTED: constant in conditional context */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* Release all DMA resources in the opposite order from acquisition
*/
static void
{
int i;
/*
* Free receive buffers which are currently associated with
* descriptors.
*/
for (i = 0; i < NET_RX_RING_SIZE; i++) {
continue;
}
/* Free the receive ring buffer. */
}
/* Free the transmit ring buffer. */
}
}
/*
* Release any packets and associated structures used by the TX ring.
*/
static void
{
RING_IDX i;
i < NET_TX_RING_SIZE;
i++, tidp++) {
}
}
}
static int
{
if (kmflag & KM_NOSLEEP)
/* Allocate a DMA access handle for the buffer. */
goto failure;
/* Allocate DMA-able memory for buffer. */
goto failure_1;
/* Bind to virtual address of buffer to get physical address. */
goto failure_2;
return (0);
return (-1);
}
static void
{
}
static xnf_buf_t *
{
/*
* Usually grant references are more scarce than memory, so we
* attempt to acquire a grant reference first.
*/
if (gref == INVALID_GRANT_REF)
return (NULL);
return (NULL);
}
return (bufp);
}
static void
{
(void) gnttab_end_foreign_access_ref(
}
}
/*
* Refresh any cached data about a buffer after resume.
*/
static void
{
}
/*
* Streams `freeb' routine for `xnf_buf_t' when used as transmit
* look-aside buffers.
*/
static void
{
}
static int
{
if (kmflag & KM_NOSLEEP)
return (-1);
}
return (0);
}
static void
{
}
/*
* Statistics.
*/
static char *xnf_aux_statistics[] = {
"tx_cksum_deferred",
"rx_cksum_no_need",
"interrupts",
"unclaimed_interrupts",
"tx_pullup",
"tx_pagebndry",
"tx_attempt",
"buf_allocated",
"buf_outstanding",
"gref_outstanding",
"gref_failure",
"gref_peak",
"rx_allocb_fail",
"rx_desballoc_fail",
};
static int
{
if (flag != KSTAT_READ)
return (EACCES);
/*
* Assignment order must match that of the names in
* xnf_aux_statistics.
*/
return (0);
}
static boolean_t
{
sizeof (xnf_aux_statistics[0]);
/*
* Create and initialise kstats.
*/
return (B_FALSE);
while (nstat > 0) {
knp++;
cp++;
nstat--;
}
return (B_TRUE);
}
static int
{
#define mac_stat(q, r) \
case (MAC_STAT_##q): \
break
#define ether_stat(q, r) \
case (ETHER_STAT_##q): \
break
switch (stat) {
/* always claim to be in full duplex mode */
case ETHER_STAT_LINK_DUPLEX:
*val = LINK_DUPLEX_FULL;
break;
/* always claim to be at 1Gb/s link speed */
case MAC_STAT_IFSPEED:
*val = 1000000000ull;
break;
default:
return (ENOTSUP);
}
return (0);
}
static boolean_t
{
switch (cap) {
case MAC_CAPAB_HCKSUM: {
/*
* Whilst the flag used to communicate with the IO
* domain is called "NETTXF_csum_blank", the checksum
* in the packet must contain the pseudo-header
* checksum and not zero.
*
* To help out the IO domain, we might use
* HCKSUM_INET_PARTIAL. Unfortunately our stack will
* then use checksum offload for IPv6 packets, which
* the IO domain can't handle.
*
* As a result, we declare outselves capable of
* HCKSUM_INET_FULL_V4. This means that we receive
* IPv4 packets from the stack with a blank checksum
* field and must insert the pseudo-header checksum
* before passing the packet to the IO domain.
*/
break;
}
default:
return (B_FALSE);
}
return (B_TRUE);
}
/*
* The state of the peer has changed - react accordingly.
*/
static void
{
switch (new_state) {
case XenbusStateUnknown:
case XenbusStateInitialising:
case XenbusStateInitialised:
case XenbusStateClosing:
case XenbusStateClosed:
case XenbusStateReconfiguring:
case XenbusStateReconfigured:
break;
case XenbusStateInitWait:
if (!xnfp->xnf_be_rx_copy) {
"The xnf driver requires a dom0 that "
"supports 'feature-rx-copy'.");
break;
}
/*
* Connect to the backend.
*/
/*
* Our MAC address as discovered by xnf_read_config().
*/
break;
case XenbusStateConnected:
/*
* Wake up any threads waiting to send data to
* backend.
*/
/*
* Kick the peer in case it missed any transmits
* request in the TX ring.
*/
/*
* There may already be completed receive requests in
* the ring sent by backend after it gets connected
* but before we see its state change here, so we call
* xnf_intr() to handle them, if any.
*/
/*
* Mark the link up now that we are connected.
*/
/*
* Tell the backend about the multicast addresses in
* which we are interested.
*/
break;
default:
break;
}
}