On Wed, Jan 06, 2016 at 04:37:36PM +0100, Mike Belopuhov wrote:
> There's still stuff to do, but it receives and transmits reliably
> (at least on modern Xen) so I'd like to get it in. Man page will
> follow.
>
> OK?
>
I can see it works now and as mentioned in icb:
I just had the first contact with OpenBSD in an EC2 instance.
(once again, we need emoji in xterm to see the U+1F596)
Two bugs:
- I didn't work on m4.10xlarge (see cvs:~reyk/dmesg.m4.10xlarge).
- One time, xnf stopped while coping a large file to a remote machine.
I think it is good enough to go in and be tweaked in the tree.
OK reyk@
> diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC
> index fca4459..77e07cc 100644
> --- sys/arch/amd64/conf/GENERIC
> +++ sys/arch/amd64/conf/GENERIC
> @@ -67,10 +67,11 @@ mpbios0 at bios0
> ipmi0 at mainbus? disable # IPMI
>
> vmt0 at pvbus? # VMware Tools
>
> #xen0 at pvbus? # Xen HVM domU
> +#xnf* at xen? # Xen Netfront
>
> option PCIVERBOSE
> option USBVERBOSE
>
> pchb* at pci? # PCI-Host bridges
> diff --git sys/dev/pv/files.pv sys/dev/pv/files.pv
> index d0e3b8c..e1272b2 100644
> --- sys/dev/pv/files.pv
> +++ sys/dev/pv/files.pv
> @@ -16,5 +16,9 @@ file dev/pv/vmt.c vmt
> needs-flag
> # Xen
> device xen {}
> attach xen at pvbus
> file dev/pv/xen.c xen needs-flag
> file dev/pv/xenstore.c xen
> +
> +device xnf: ether, ifnet, ifmedia
> +attach xnf at xen
> +file dev/pv/if_xnf.c xnf
> diff --git sys/dev/pv/if_xnf.c sys/dev/pv/if_xnf.c
> new file mode 100644
> index 0000000..7f8b08e
> --- /dev/null
> +++ sys/dev/pv/if_xnf.c
> @@ -0,0 +1,1022 @@
> +/*
> + * Copyright (c) 2015 Mike Belopuhov
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include "bpfilter.h"
> +#include "vlan.h"
> +#include "xen.h"
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/atomic.h>
> +#include <sys/malloc.h>
> +#include <sys/mbuf.h>
> +#include <sys/kernel.h>
> +#include <sys/device.h>
> +#include <sys/socket.h>
> +#include <sys/sockio.h>
> +#include <sys/queue.h>
> +#include <sys/timeout.h>
> +#include <sys/pool.h>
> +
> +#include <machine/bus.h>
> +
> +#include <dev/pv/xenreg.h>
> +#include <dev/pv/xenvar.h>
> +
> +#include <net/if.h>
> +#include <net/if_media.h>
> +
> +#include <netinet/in.h>
> +#include <netinet/if_ether.h>
> +
> +#ifdef INET6
> +#include <netinet/ip6.h>
> +#endif
> +
> +#if NBPFILTER > 0
> +#include <net/bpf.h>
> +#endif
> +
> +
> +/*
> + * Rx ring
> + */
> +
> +struct xnf_rx_req {
> + uint16_t rxq_id;
> + uint16_t rxq_pad;
> + uint32_t rxq_ref;
> +} __packed;
> +
> +struct xnf_rx_rsp {
> + uint16_t rxp_id;
> + uint16_t rxp_offset;
> + uint16_t rxp_flags;
> +#define XNF_RXF_CSUM 0x0001
> +#define XNF_RXF_BLANK 0x0002
> +#define XNF_RXF_CHUNK 0x0004
> +#define XNF_RXF_EXTRA 0x0008
> + int16_t rxp_status;
> +} __packed;
> +
> +union xnf_rx_desc {
> + struct xnf_rx_req rxd_req;
> + struct xnf_rx_rsp rxd_rsp;
> +} __packed;
> +
> +#define XNF_RX_DESC 256
> +#define XNF_MCLEN PAGE_SIZE
> +#define XNF_RX_MIN 32
> +
> +struct xnf_rx_ring {
> + uint32_t rxr_prod;
> + uint32_t rxr_req_evt;
> + uint32_t rxr_cons;
> + uint32_t rxr_rsp_evt;
> + uint32_t rxr_reserved[12];
> + union xnf_rx_desc rxr_desc[XNF_RX_DESC];
> +} __packed;
> +
> +
> +/*
> + * Tx ring
> + */
> +
> +struct xnf_tx_req {
> + uint32_t txq_ref;
> + uint16_t txq_offset;
> + uint16_t txq_flags;
> +#define XNF_TXF_CSUM 0x0001
> +#define XNF_TXF_VALID 0x0002
> +#define XNF_TXF_CHUNK 0x0004
> +#define XNF_TXF_ETXRA 0x0008
> + uint16_t txq_id;
> + uint16_t txq_size;
> +} __packed;
> +
> +struct xnf_tx_rsp {
> + uint16_t txp_id;
> + int16_t txp_status;
> +} __packed;
> +
> +union xnf_tx_desc {
> + struct xnf_tx_req txd_req;
> + struct xnf_tx_rsp txd_rsp;
> +} __packed;
> +
> +#define XNF_TX_DESC 256
> +#define XNF_TX_FRAG 8 /* down from 18 */
> +
> +struct xnf_tx_ring {
> + uint32_t txr_prod;
> + uint32_t txr_req_evt;
> + uint32_t txr_cons;
> + uint32_t txr_rsp_evt;
> + uint32_t txr_reserved[12];
> + union xnf_tx_desc txr_desc[XNF_TX_DESC];
> +} __packed;
> +
> +
> +/* Management frame, "extra info" in Xen parlance */
> +struct xnf_mgmt {
> + uint8_t mg_type;
> +#define XNF_MGMT_MCAST_ADD 2
> +#define XNF_MGMT_MCAST_DEL 3
> + uint8_t mg_flags;
> + union {
> + uint8_t mgu_mcaddr[ETHER_ADDR_LEN];
> + uint16_t mgu_pad[3];
> + } u;
> +#define mg_mcaddr u.mgu_mcaddr
> +} __packed;
> +
> +
> +struct xnf_softc {
> + struct device sc_dev;
> + struct xen_attach_args sc_xa;
> + struct xen_softc *sc_xen;
> + bus_dma_tag_t sc_dmat;
> +
> + struct arpcom sc_ac;
> + struct ifmedia sc_media;
> +
> + xen_intr_handle_t sc_xih;
> +
> + /* Rx ring */
> + struct xnf_rx_ring *sc_rx_ring;
> + int sc_rx_cons;
> + bus_dmamap_t sc_rx_rmap; /* map for the ring */
> + bus_dma_segment_t sc_rx_seg;
> + uint32_t sc_rx_ref; /* grant table ref */
> + struct mbuf *sc_rx_buf[XNF_RX_DESC];
> + bus_dmamap_t sc_rx_dmap[XNF_RX_DESC]; /* maps for packets */
> + struct mbuf *sc_rx_cbuf[2]; /* chain handling */
> + struct if_rxring sc_rx_slots;
> + struct timeout sc_rx_fill;
> +
> + /* Tx ring */
> + struct xnf_tx_ring *sc_tx_ring;
> + int sc_tx_cons;
> + bus_dmamap_t sc_tx_rmap; /* map for the ring */
> + bus_dma_segment_t sc_tx_seg;
> + uint32_t sc_tx_ref; /* grant table ref */
> + struct mbuf *sc_tx_buf[XNF_TX_DESC];
> + bus_dmamap_t sc_tx_dmap[XNF_TX_DESC]; /* maps for packets */
> +};
> +
> +int xnf_match(struct device *, void *, void *);
> +void xnf_attach(struct device *, struct device *, void *);
> +int xnf_lladdr(struct xnf_softc *);
> +int xnf_ioctl(struct ifnet *, u_long, caddr_t);
> +int xnf_media_change(struct ifnet *);
> +void xnf_media_status(struct ifnet *, struct ifmediareq *);
> +int xnf_iff(struct xnf_softc *);
> +void xnf_init(struct xnf_softc *);
> +void xnf_stop(struct xnf_softc *);
> +void xnf_start(struct ifnet *);
> +int xnf_encap(struct xnf_softc *, struct mbuf *, uint32_t *);
> +void xnf_intr(void *);
> +int xnf_txeof(struct xnf_softc *);
> +int xnf_rxeof(struct xnf_softc *);
> +void xnf_rx_ring_fill(void *);
> +int xnf_rx_ring_create(struct xnf_softc *);
> +void xnf_rx_ring_drain(struct xnf_softc *);
> +void xnf_rx_ring_destroy(struct xnf_softc *);
> +int xnf_tx_ring_create(struct xnf_softc *);
> +void xnf_tx_ring_drain(struct xnf_softc *);
> +void xnf_tx_ring_destroy(struct xnf_softc *);
> +int xnf_init_backend(struct xnf_softc *);
> +int xnf_stop_backend(struct xnf_softc *);
> +
> +struct cfdriver xnf_cd = {
> + NULL, "xnf", DV_IFNET
> +};
> +
> +const struct cfattach xnf_ca = {
> + sizeof(struct xnf_softc), xnf_match, xnf_attach
> +};
> +
> +int
> +xnf_match(struct device *parent, void *match, void *aux)
> +{
> + struct xen_attach_args *xa = aux;
> + char type[64];
> +
> + if (strcmp("vif", xa->xa_name))
> + return (0);
> +
> + if (xs_getprop(xa, "type", type, sizeof(type)) == 0 &&
> + ((strcmp("vif", type) == 0) || (strcmp("front", type) == 0)))
> + return (1);
> +
> + return (0);
> +}
> +
> +void
> +xnf_attach(struct device *parent, struct device *self, void *aux)
> +{
> + struct xen_attach_args *xa = aux;
> + struct xnf_softc *sc = (struct xnf_softc *)self;
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> + sc->sc_xa = *xa;
> + sc->sc_xen = xa->xa_parent;
> + sc->sc_dmat = xa->xa_dmat;
> +
> + strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
> +
> + if (xnf_lladdr(sc)) {
> + printf(": failed to obtain MAC address\n");
> + return;
> + }
> +
> + if (xen_intr_establish(0, &sc->sc_xih, xnf_intr, sc, ifp->if_xname)) {
> + printf("%s: failed to establish an interrupt\n", ifp->if_xname);
> + return;
> + }
> +
> + printf(": event channel %u, address %s\n", sc->sc_xih,
> + ether_sprintf(sc->sc_ac.ac_enaddr));
> +
> + if (xnf_rx_ring_create(sc)) {
> + xen_intr_disestablish(sc->sc_xih);
> + return;
> + }
> + if (xnf_tx_ring_create(sc)) {
> + xen_intr_disestablish(sc->sc_xih);
> + xnf_rx_ring_destroy(sc);
> + return;
> + }
> + if (xnf_init_backend(sc)) {
> + xen_intr_disestablish(sc->sc_xih);
> + xnf_rx_ring_destroy(sc);
> + xnf_tx_ring_destroy(sc);
> + return;
> + }
> +
> + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
> + ifp->if_xflags = IFXF_MPSAFE;
> + ifp->if_ioctl = xnf_ioctl;
> + ifp->if_start = xnf_start;
> + ifp->if_softc = sc;
> +
> + ifp->if_capabilities = IFCAP_VLAN_MTU;
> +
> + IFQ_SET_MAXLEN(&ifp->if_snd, XNF_TX_DESC - 1);
> + IFQ_SET_READY(&ifp->if_snd);
> +
> + ifmedia_init(&sc->sc_media, IFM_IMASK, xnf_media_change,
> + xnf_media_status);
> + ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
> + ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL);
> +
> + if_attach(ifp);
> + ether_ifattach(ifp);
> +
> + timeout_set(&sc->sc_rx_fill, xnf_rx_ring_fill, sc);
> +}
> +
> +static int
> +nibble(int ch)
> +{
> + if (ch >= '0' && ch <= '9')
> + return (ch - '0');
> + if (ch >= 'A' && ch <= 'F')
> + return (10 + ch - 'A');
> + if (ch >= 'a' && ch <= 'f')
> + return (10 + ch - 'a');
> + return (-1);
> +}
> +
> +int
> +xnf_lladdr(struct xnf_softc *sc)
> +{
> + char enaddr[ETHER_ADDR_LEN];
> + char mac[32];
> + int i, j, lo, hi;
> +
> + if (xs_getprop(&sc->sc_xa, "mac", mac, sizeof(mac)))
> + return (-1);
> +
> + for (i = 0, j = 0; j < ETHER_ADDR_LEN; i += 3) {
> + if ((hi = nibble(mac[i])) == -1 ||
> + (lo = nibble(mac[i+1])) == -1)
> + return (-1);
> + enaddr[j++] = hi << 4 | lo;
> + }
> +
> + memcpy(sc->sc_ac.ac_enaddr, enaddr, ETHER_ADDR_LEN);
> + return (0);
> +}
> +
> +int
> +xnf_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
> +{
> + struct xnf_softc *sc = ifp->if_softc;
> + struct ifreq *ifr = (struct ifreq *)data;
> + int s, error = 0;
> +
> + s = splnet();
> +
> + switch (command) {
> + case SIOCSIFADDR:
> + ifp->if_flags |= IFF_UP;
> + if (!(ifp->if_flags & IFF_RUNNING))
> + xnf_init(sc);
> + break;
> + case SIOCSIFFLAGS:
> + if (ifp->if_flags & IFF_UP) {
> + if (ifp->if_flags & IFF_RUNNING)
> + error = ENETRESET;
> + else
> + xnf_init(sc);
> + } else {
> + if (ifp->if_flags & IFF_RUNNING)
> + xnf_stop(sc);
> + }
> + break;
> + case SIOCGIFMEDIA:
> + case SIOCSIFMEDIA:
> + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command);
> + break;
> + case SIOCGIFRXR:
> + error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
> + NULL, XNF_MCLEN, &sc->sc_rx_slots);
> + break;
> + default:
> + error = ether_ioctl(ifp, &sc->sc_ac, command, data);
> + break;
> + }
> +
> + if (error == ENETRESET) {
> + if (ifp->if_flags & IFF_RUNNING)
> + xnf_iff(sc);
> + error = 0;
> + }
> +
> + splx(s);
> +
> + return (error);
> +}
> +
> +int
> +xnf_media_change(struct ifnet *ifp)
> +{
> + return (0);
> +}
> +
> +void
> +xnf_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
> +{
> + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
> + ifmr->ifm_active = IFM_ETHER | IFM_MANUAL;
> +}
> +
> +int
> +xnf_iff(struct xnf_softc *sc)
> +{
> + return (0);
> +}
> +
> +void
> +xnf_init(struct xnf_softc *sc)
> +{
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> + xnf_stop(sc);
> +
> + xnf_iff(sc);
> +
> + if (xen_intr_unmask(sc->sc_xih)) {
> + printf("%s: failed to enable interrupts\n", ifp->if_xname);
> + xnf_stop(sc);
> + return;
> + }
> +
> + ifp->if_flags |= IFF_RUNNING;
> + ifq_clr_oactive(&ifp->if_snd);
> +}
> +
> +void
> +xnf_stop(struct xnf_softc *sc)
> +{
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> + ifp->if_flags &= ~IFF_RUNNING;
> +
> + xen_intr_mask(sc->sc_xih);
> +
> + timeout_del(&sc->sc_rx_fill);
> +
> + ifq_barrier(&ifp->if_snd);
> + intr_barrier(&sc->sc_xih);
> +
> + ifq_clr_oactive(&ifp->if_snd);
> +
> + if (sc->sc_tx_ring)
> + xnf_tx_ring_drain(sc);
> + if (sc->sc_rx_ring)
> + xnf_rx_ring_drain(sc);
> +}
> +
> +void
> +xnf_start(struct ifnet *ifp)
> +{
> + struct xnf_softc *sc = ifp->if_softc;
> + struct xnf_tx_ring *txr = sc->sc_tx_ring;
> + struct mbuf *m;
> + int error, pkts = 0;
> + uint32_t prod;
> +
> + if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
> + return;
> +
> + prod = txr->txr_prod;
> + membar_consumer();
> +
> + for (;;) {
> + m = ifq_deq_begin(&ifp->if_snd);
> + if (m == NULL)
> + break;
> +
> + error = xnf_encap(sc, m, &prod);
> + if (error == ENOENT) {
> + /* transient */
> + ifq_deq_rollback(&ifp->if_snd, m);
> + ifq_set_oactive(&ifp->if_snd);
> + break;
> + } else if (error) {
> + /* the chain is too large */
> + ifq_deq_commit(&ifp->if_snd, m);
> + m_freem(m);
> + continue;
> + }
> + ifq_deq_commit(&ifp->if_snd, m);
> +
> +#if NBPFILTER > 0
> + if (ifp->if_bpf)
> + bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
> +#endif
> + pkts++;
> + }
> + if (pkts > 0) {
> + txr->txr_prod = prod;
> + xen_intr_signal(sc->sc_xih);
> + }
> +}
> +
> +int
> +xnf_encap(struct xnf_softc *sc, struct mbuf *m, uint32_t *prod)
> +{
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> + struct xnf_tx_ring *txr = sc->sc_tx_ring;
> + union xnf_tx_desc *txd;
> + bus_dmamap_t dmap;
> + int error, i, n = 0;
> +
> + if (((txr->txr_cons - *prod - 1) & (XNF_TX_DESC - 1)) < XNF_TX_FRAG) {
> + error = ENOENT;
> + goto errout;
> + }
> +
> + i = *prod & (XNF_TX_DESC - 1);
> + dmap = sc->sc_tx_dmap[i];
> +
> + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m, BUS_DMA_WRITE |
> + BUS_DMA_NOWAIT);
> + if (error == EFBIG) {
> + if (m_defrag(m, M_DONTWAIT) ||
> + bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m, BUS_DMA_WRITE |
> + BUS_DMA_NOWAIT))
> + goto errout;
> + } else if (error)
> + goto errout;
> +
> + for (n = 0; n < dmap->dm_nsegs; n++, (*prod)++) {
> + i = *prod & (XNF_TX_DESC - 1);
> + if (sc->sc_tx_buf[i])
> + panic("%s: save vs spell: %d\n", ifp->if_xname, i);
> + txd = &txr->txr_desc[i];
> + if (n == 0) {
> + sc->sc_tx_buf[i] = m;
> + if (0 && m->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
> + txd->txd_req.txq_flags = XNF_TXF_CSUM |
> + XNF_TXF_VALID;
> + txd->txd_req.txq_size = m->m_pkthdr.len;
> + } else
> + txd->txd_req.txq_size = dmap->dm_segs[n].ds_len;
> + if (n != dmap->dm_nsegs - 1)
> + txd->txd_req.txq_flags |= XNF_TXF_CHUNK;
> + txd->txd_req.txq_ref = dmap->dm_segs[n].ds_addr;
> + txd->txd_req.txq_offset = dmap->dm_segs[n].ds_offset;
> + }
> +
> + ifp->if_opackets++;
> + return (0);
> +
> + errout:
> + ifp->if_oerrors++;
> + return (error);
> +}
> +
> +void
> +xnf_intr(void *arg)
> +{
> + struct xnf_softc *sc = arg;
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> +
> + if (ifp->if_flags & IFF_RUNNING) {
> + xnf_rxeof(sc);
> + xnf_txeof(sc);
> + }
> +}
> +
> +int
> +xnf_txeof(struct xnf_softc *sc)
> +{
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> + struct xnf_tx_ring *txr = sc->sc_tx_ring;
> + union xnf_tx_desc *txd;
> + struct mbuf *m;
> + bus_dmamap_t dmap;
> + volatile uint32_t r;
> + uint32_t cons;
> + int i, id, pkts = 0;
> +
> + do {
> + for (cons = sc->sc_tx_cons; cons != txr->txr_cons; cons++) {
> + membar_consumer();
> + i = cons & (XNF_TX_DESC - 1);
> + txd = &txr->txr_desc[i];
> + id = txd->txd_rsp.txp_id;
> + memset(txd, 0, sizeof(*txd));
> + txd->txd_req.txq_id = id;
> + membar_producer();
> + if (sc->sc_tx_buf[i]) {
> + dmap = sc->sc_tx_dmap[i];
> + bus_dmamap_unload(sc->sc_dmat, dmap);
> + m = sc->sc_tx_buf[i];
> + sc->sc_tx_buf[i] = NULL;
> + m_freem(m);
> + }
> + pkts++;
> + }
> +
> + if (pkts > 0) {
> + sc->sc_tx_cons = cons;
> + membar_producer();
> + txr->txr_rsp_evt = cons + 1;
> + pkts = 0;
> + }
> +
> + r = txr->txr_cons - sc->sc_tx_cons;
> + membar_consumer();
> + } while (r > 0);
> +
> + if (ifq_is_oactive(&ifp->if_snd))
> + ifq_restart(&ifp->if_snd);
> +
> + return (0);
> +}
> +
> +int
> +xnf_rxeof(struct xnf_softc *sc)
> +{
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> + struct xnf_rx_ring *rxr = sc->sc_rx_ring;
> + union xnf_rx_desc *rxd;
> + struct mbuf_list ml = MBUF_LIST_INITIALIZER();
> + struct mbuf *fmp = sc->sc_rx_cbuf[0];
> + struct mbuf *lmp = sc->sc_rx_cbuf[1];
> + struct mbuf *m;
> + bus_dmamap_t dmap;
> + volatile uint32_t r;
> + uint32_t cons;
> + int i, id, flags, len, offset, pkts = 0;
> +
> + do {
> + for (cons = sc->sc_rx_cons; cons != rxr->rxr_cons; cons++) {
> + membar_consumer();
> + i = cons & (XNF_RX_DESC - 1);
> + rxd = &rxr->rxr_desc[i];
> + dmap = sc->sc_rx_dmap[i];
> +
> + len = rxd->rxd_rsp.rxp_status;
> + flags = rxd->rxd_rsp.rxp_flags;
> + offset = rxd->rxd_rsp.rxp_offset;
> + id = rxd->rxd_rsp.rxp_id;
> + memset(rxd, 0, sizeof(*rxd));
> + rxd->rxd_req.rxq_id = id;
> + membar_producer();
> +
> + bus_dmamap_unload(sc->sc_dmat, dmap);
> +
> + m = sc->sc_rx_buf[i];
> + KASSERT(m != NULL);
> + sc->sc_rx_buf[i] = NULL;
> +
> + if (flags & XNF_RXF_EXTRA)
> + printf("%s: management data present\n",
> + ifp->if_xname);
> +
> + if (flags & XNF_RXF_CSUM)
> + m->m_pkthdr.csum_flags = M_IPV4_CSUM_IN_OK;
> +
> + if_rxr_put(&sc->sc_rx_slots, 1);
> + pkts++;
> +
> + if (len < 0 || (len + offset > PAGE_SIZE)) {
> + ifp->if_ierrors++;
> + m_freem(m);
> + continue;
> + }
> +
> + m->m_len = len;
> + m->m_data += offset;
> +
> + if (fmp == NULL) {
> + m->m_pkthdr.len = len;
> + fmp = m;
> + } else {
> + m->m_flags &= ~M_PKTHDR;
> + lmp->m_next = m;
> + fmp->m_pkthdr.len += m->m_len;
> + }
> + lmp = m;
> +
> + if (flags & XNF_RXF_CHUNK) {
> + sc->sc_rx_cbuf[0] = fmp;
> + sc->sc_rx_cbuf[1] = lmp;
> + continue;
> + }
> +
> + m = fmp;
> +
> + ml_enqueue(&ml, m);
> + sc->sc_rx_cbuf[0] = sc->sc_rx_cbuf[1] =
> + fmp = lmp = NULL;
> + }
> +
> + if (pkts > 0) {
> + sc->sc_rx_cons = cons;
> + membar_producer();
> + rxr->rxr_rsp_evt = cons + 1;
> + pkts = 0;
> + }
> +
> + r = rxr->rxr_cons - sc->sc_rx_cons;
> + membar_consumer();
> + } while (r > 0);
> +
> + if (!ml_empty(&ml)) {
> + if_input(ifp, &ml);
> +
> + xnf_rx_ring_fill(sc);
> + }
> +
> + return (0);
> +}
> +
> +void
> +xnf_rx_ring_fill(void *arg)
> +{
> + struct xnf_softc *sc = arg;
> + struct ifnet *ifp = &sc->sc_ac.ac_if;
> + struct xnf_rx_ring *rxr = sc->sc_rx_ring;
> + bus_dmamap_t dmap;
> + struct mbuf *m;
> + uint32_t cons, prod;
> + static int timer = 0;
> + int i, n;
> +
> + cons = rxr->rxr_cons;
> + prod = rxr->rxr_prod;
> +
> + n = if_rxr_get(&sc->sc_rx_slots, XNF_RX_DESC);
> +
> + /* Less than XNF_RX_MIN slots available? */
> + if (n == 0 && prod - cons < XNF_RX_MIN) {
> + if (ifp->if_flags & IFF_RUNNING)
> + timeout_add(&sc->sc_rx_fill, 1 << timer);
> + if (timer < 10)
> + timer++;
> + return;
> + }
> +
> + for (; n > 0; prod++, n--) {
> + i = prod & (XNF_RX_DESC - 1);
> + if (sc->sc_rx_buf[i])
> + break;
> + m = MCLGETI(NULL, M_DONTWAIT, NULL, XNF_MCLEN);
> + if (m == NULL)
> + break;
> + m->m_len = m->m_pkthdr.len = XNF_MCLEN;
> + dmap = sc->sc_rx_dmap[i];
> + if (bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m, BUS_DMA_READ |
> + BUS_DMA_NOWAIT)) {
> + m_freem(m);
> + break;
> + }
> + sc->sc_rx_buf[i] = m;
> + rxr->rxr_desc[i].rxd_req.rxq_ref = dmap->dm_segs[0].ds_addr;
> + }
> +
> + if (n > 0)
> + if_rxr_put(&sc->sc_rx_slots, n);
> +
> + membar_producer();
> + rxr->rxr_prod = prod;
> +
> + xen_intr_signal(sc->sc_xih);
> +}
> +
> +int
> +xnf_rx_ring_create(struct xnf_softc *sc)
> +{
> + int i, rsegs;
> +
> + /* Allocate a page of memory for the ring */
> + if (bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, 0,
> + &sc->sc_rx_seg, 1, &rsegs, BUS_DMA_ZERO | BUS_DMA_WAITOK)) {
> + printf("%s: failed to allocate memory for the rx ring\n",
> + sc->sc_dev.dv_xname);
> + return (-1);
> + }
> + /* Map in the allocated memory into the ring structure */
> + if (bus_dmamem_map(sc->sc_dmat, &sc->sc_rx_seg, 1, PAGE_SIZE,
> + (caddr_t *)(&sc->sc_rx_ring), BUS_DMA_WAITOK)) {
> + printf("%s: failed to map memory for the rx ring\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + /* Create a map to load the ring memory into */
> + if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
> + BUS_DMA_WAITOK, &sc->sc_rx_rmap)) {
> + printf("%s: failed to create a memory map for the rx ring\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + /* Load the ring into the ring map to extract the PA */
> + if (bus_dmamap_load(sc->sc_dmat, sc->sc_rx_rmap, sc->sc_rx_ring,
> + PAGE_SIZE, NULL, BUS_DMA_WAITOK)) {
> + printf("%s: failed to load the rx ring map\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + sc->sc_rx_ref = sc->sc_rx_rmap->dm_segs[0].ds_addr;
> +
> + sc->sc_rx_ring->rxr_req_evt = sc->sc_rx_ring->rxr_rsp_evt = 1;
> +
> + for (i = 0; i < XNF_RX_DESC; i++) {
> + if (bus_dmamap_create(sc->sc_dmat, XNF_MCLEN, 1,
> + XNF_MCLEN, 0, BUS_DMA_WAITOK, &sc->sc_rx_dmap[i])) {
> + printf("%s: failed to create a memory map for the rx "
> + "slot %d/%d\n", sc->sc_dev.dv_xname, i,
> + XNF_RX_DESC);
> + goto errout;
> + }
> + sc->sc_rx_ring->rxr_desc[i].rxd_req.rxq_id = i;
> + }
> +
> + if_rxr_init(&sc->sc_rx_slots, XNF_RX_MIN, XNF_RX_DESC);
> + xnf_rx_ring_fill(sc);
> +
> + return (0);
> +
> + errout:
> + xnf_rx_ring_destroy(sc);
> + return (-1);
> +}
> +
> +void
> +xnf_rx_ring_drain(struct xnf_softc *sc)
> +{
> + struct xnf_rx_ring *rxr = sc->sc_rx_ring;
> +
> + if (sc->sc_rx_cons != rxr->rxr_cons)
> + xnf_rxeof(sc);
> +}
> +
> +void
> +xnf_rx_ring_destroy(struct xnf_softc *sc)
> +{
> + int i, slots = 0;
> +
> + for (i = 0; i < XNF_RX_DESC; i++) {
> + if (sc->sc_rx_buf[i] == NULL)
> + continue;
> + bus_dmamap_unload(sc->sc_dmat, sc->sc_rx_dmap[i]);
> + m_freem(sc->sc_rx_buf[i]);
> + sc->sc_rx_buf[i] = NULL;
> + slots++;
> + }
> + printf("%s: unload done\n", __func__);
> + if_rxr_put(&sc->sc_rx_slots, slots);
> + printf("%s: rxr_put done\n", __func__);
> +
> + for (i = 0; i < XNF_RX_DESC; i++) {
> + if (sc->sc_rx_dmap[i] == NULL)
> + continue;
> + bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmap[i]);
> + sc->sc_rx_dmap[i] = NULL;
> + }
> + printf("%s: desc map destroy done\n", __func__);
> + if (sc->sc_rx_rmap) {
> + bus_dmamap_unload(sc->sc_dmat, sc->sc_rx_rmap);
> + bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_rmap);
> + }
> + printf("%s: ring map destroy done\n", __func__);
> + if (sc->sc_rx_ring) {
> + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_rx_ring,
> + PAGE_SIZE);
> + bus_dmamem_free(sc->sc_dmat, &sc->sc_rx_seg, 1);
> + }
> + printf("%s: ring mem free done\n", __func__);
> + sc->sc_rx_ring = NULL;
> + sc->sc_rx_rmap = NULL;
> + sc->sc_rx_cons = 0;
> +}
> +
> +int
> +xnf_tx_ring_create(struct xnf_softc *sc)
> +{
> + int i, rsegs;
> +
> + /* Allocate a page of memory for the ring */
> + if (bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, 0,
> + &sc->sc_tx_seg, 1, &rsegs, BUS_DMA_ZERO | BUS_DMA_WAITOK)) {
> + printf("%s: failed to allocate memory for the tx ring\n",
> + sc->sc_dev.dv_xname);
> + return (-1);
> + }
> + /* Map in the allocated memory into the ring structure */
> + if (bus_dmamem_map(sc->sc_dmat, &sc->sc_tx_seg, 1, PAGE_SIZE,
> + (caddr_t *)&sc->sc_tx_ring, BUS_DMA_WAITOK)) {
> + printf("%s: failed to map memory for the tx ring\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + /* Create a map to load the ring memory into */
> + if (bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
> + BUS_DMA_WAITOK, &sc->sc_tx_rmap)) {
> + printf("%s: failed to create a memory map for the tx ring\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + /* Load the ring into the ring map to extract the PA */
> + if (bus_dmamap_load(sc->sc_dmat, sc->sc_tx_rmap, sc->sc_tx_ring,
> + PAGE_SIZE, NULL, BUS_DMA_WAITOK)) {
> + printf("%s: failed to load the tx ring map\n",
> + sc->sc_dev.dv_xname);
> + goto errout;
> + }
> + sc->sc_tx_ref = sc->sc_tx_rmap->dm_segs[0].ds_addr;
> +
> + sc->sc_tx_ring->txr_req_evt = sc->sc_tx_ring->txr_rsp_evt = 1;
> +
> + for (i = 0; i < XNF_TX_DESC; i++) {
> + if (bus_dmamap_create(sc->sc_dmat, XNF_MCLEN, XNF_TX_FRAG,
> + XNF_MCLEN, 0, BUS_DMA_WAITOK, &sc->sc_tx_dmap[i])) {
> + printf("%s: failed to create a memory map for the tx "
> + "slot %d/%d\n", sc->sc_dev.dv_xname, i,
> + XNF_TX_DESC);
> + goto errout;
> + }
> + sc->sc_tx_ring->txr_desc[i].txd_req.txq_id = i;
> + }
> +
> + return (0);
> +
> + errout:
> + xnf_tx_ring_destroy(sc);
> + return (-1);
> +}
> +
> +void
> +xnf_tx_ring_drain(struct xnf_softc *sc)
> +{
> + struct xnf_tx_ring *txr = sc->sc_tx_ring;
> +
> + if (sc->sc_tx_cons != txr->txr_cons)
> + xnf_txeof(sc);
> +}
> +
> +void
> +xnf_tx_ring_destroy(struct xnf_softc *sc)
> +{
> + int i;
> +
> + for (i = 0; i < XNF_TX_DESC; i++) {
> + if (sc->sc_tx_dmap[i] == NULL)
> + continue;
> + bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_dmap[i]);
> + if (sc->sc_tx_buf[i] == NULL)
> + continue;
> + m_freem(sc->sc_tx_buf[i]);
> + sc->sc_tx_buf[i] = NULL;
> + }
> + for (i = 0; i < XNF_TX_DESC; i++) {
> + if (sc->sc_tx_dmap[i] == NULL)
> + continue;
> + bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmap[i]);
> + sc->sc_tx_dmap[i] = NULL;
> + }
> + if (sc->sc_tx_rmap) {
> + bus_dmamap_unload(sc->sc_dmat, sc->sc_tx_rmap);
> + bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_rmap);
> + }
> + if (sc->sc_tx_ring) {
> + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_tx_ring,
> + PAGE_SIZE);
> + bus_dmamem_free(sc->sc_dmat, &sc->sc_tx_seg, 1);
> + }
> + sc->sc_tx_ring = NULL;
> + sc->sc_tx_rmap = NULL;
> +}
> +
> +int
> +xnf_init_backend(struct xnf_softc *sc)
> +{
> + const char *prop;
> + char val[32];
> +
> + /* Plumb the Rx ring */
> + prop = "rx-ring-ref";
> + snprintf(val, sizeof(val), "%u", sc->sc_rx_ref);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> + /* Enable "copy" mode */
> + prop = "request-rx-copy";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> + /* Enable notify mode */
> + prop = "feature-rx-notify";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> + /* Request multicast filtering */
> + prop = "request-multicast-control";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> +
> + /* Plumb the Tx ring */
> + prop = "tx-ring-ref";
> + snprintf(val, sizeof(val), "%u", sc->sc_tx_ref);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> + /* Enable transmit scatter-gather mode */
> + prop = "feature-sg";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> +
> + /* Disable TCP/UDP checksum offload */
> + prop = "feature-csum-offload";
> + if (xs_setprop(&sc->sc_xa, prop, NULL, 0))
> + goto errout;
> + prop = "feature-no-csum-offload";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> + prop = "feature-ipv6-csum-offload";
> + if (xs_setprop(&sc->sc_xa, prop, NULL, 0))
> + goto errout;
> + prop = "feature-no-ipv6-csum-offload";
> + snprintf(val, sizeof(val), "%u", 1);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> +
> + /* Plumb the event channel port */
> + prop = "event-channel";
> + snprintf(val, sizeof(val), "%u", sc->sc_xih);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> +
> + /* Connect the device */
> + prop = "state";
> + snprintf(val, sizeof(val), "%u", 4);
> + if (xs_setprop(&sc->sc_xa, prop, val, strlen(val)))
> + goto errout;
> +
> + return (0);
> +
> + errout:
> + printf("%s: failed to set \"%s\" property to \"%s\"\n",
> + sc->sc_dev.dv_xname, prop, val);
> + return (-1);
> +}
>
--