Date: Sun, 20 Oct 2013 01:31:09 +0000 (UTC) From: Nathan Whitehorn <nwhitehorn@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r256778 - in head/sys: conf powerpc/conf powerpc/pseries Message-ID: <201310200131.r9K1V9U5039632@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: nwhitehorn Date: Sun Oct 20 01:31:09 2013 New Revision: 256778 URL: http://svnweb.freebsd.org/changeset/base/256778 Log: Add initial driver for POWER hypervisor interpartition ethernet. This is sufficient to pass traffic but needs some more work before merging to STABLE. Added: head/sys/powerpc/pseries/phyp_llan.c (contents, props changed) Modified: head/sys/conf/files.powerpc head/sys/powerpc/conf/GENERIC64 Modified: head/sys/conf/files.powerpc ============================================================================== --- head/sys/conf/files.powerpc Sun Oct 20 01:28:39 2013 (r256777) +++ head/sys/conf/files.powerpc Sun Oct 20 01:31:09 2013 (r256778) @@ -228,6 +228,7 @@ powerpc/ps3/ps3-hvcall.S optional ps3 sc powerpc/pseries/phyp-hvcall.S optional pseries powerpc64 powerpc/pseries/mmu_phyp.c optional pseries powerpc64 powerpc/pseries/phyp_console.c optional pseries powerpc64 uart +powerpc/pseries/phyp_llan.c optional llan powerpc/pseries/phyp_vscsi.c optional pseries powerpc64 scbus powerpc/pseries/platform_chrp.c optional pseries powerpc/pseries/plpar_iommu.c optional pseries powerpc64 Modified: head/sys/powerpc/conf/GENERIC64 ============================================================================== --- head/sys/powerpc/conf/GENERIC64 Sun Oct 20 01:28:39 2013 (r256777) +++ head/sys/powerpc/conf/GENERIC64 Sun Oct 20 01:31:09 2013 (r256778) @@ -136,6 +136,7 @@ device em # Intel PRO/1000 Gigabit Eth device igb # Intel PRO/1000 PCIE Server Gigabit Family device ixgbe # Intel PRO/10GbE PCIE Ethernet Family device glc # Sony Playstation 3 Ethernet +device llan # IBM pSeries Virtual Ethernet # PCI Ethernet NICs that use the common MII bus controller code. device miibus # MII bus support Added: head/sys/powerpc/pseries/phyp_llan.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/powerpc/pseries/phyp_llan.c Sun Oct 20 01:31:09 2013 (r256778) @@ -0,0 +1,454 @@ +/*- + * Copyright 2013 Nathan Whitehorn + * 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 + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <powerpc/pseries/phyp-hvcall.h> + +#define LLAN_MAX_RX_PACKETS 100 +#define LLAN_MAX_TX_PACKETS 100 +#define LLAN_RX_BUF_LEN 8*PAGE_SIZE + +struct llan_xfer { + struct mbuf *rx_mbuf; + bus_dmamap_t rx_dmamap; + uint64_t rx_bufdesc; +}; + +struct llan_receive_queue_entry { /* PAPR page 539 */ + uint8_t control; + uint8_t reserved; + uint16_t offset; + uint32_t length; + uint64_t handle; +} __packed; + +struct llan_softc { + device_t dev; + struct mtx io_lock; + + cell_t unit; + uint8_t mac_address[8]; + + int irqid; + struct resource *irq; + void *irq_cookie; + + bus_dma_tag_t rx_dma_tag; + bus_dma_tag_t rxbuf_dma_tag; + bus_dma_tag_t tx_dma_tag; + + bus_dmamap_t tx_dma_map; + + struct llan_receive_queue_entry *rx_buf; + int rx_dma_slot; + int rx_valid_val; + bus_dmamap_t rx_buf_map; + bus_addr_t rx_buf_phys; + bus_size_t rx_buf_len; + bus_addr_t input_buf_phys; + bus_addr_t filter_buf_phys; + struct llan_xfer rx_xfer[LLAN_MAX_RX_PACKETS]; + + struct ifnet *ifp; +}; + +static int llan_probe(device_t); +static int llan_attach(device_t); +static void llan_intr(void *xsc); +static void llan_init(void *xsc); +static void llan_start(struct ifnet *ifp); +static int llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +static void llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, + int err); +static int llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx); + +static devclass_t llan_devclass; +static device_method_t llan_methods[] = { + DEVMETHOD(device_probe, llan_probe), + DEVMETHOD(device_attach, llan_attach), + + DEVMETHOD_END +}; +static driver_t llan_driver = { + "llan", + llan_methods, + sizeof(struct llan_softc) +}; +DRIVER_MODULE(llan, vdevice, llan_driver, llan_devclass, 0, 0); + +static int +llan_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev,"IBM,l-lan")) + return (ENXIO); + + device_set_desc(dev, "POWER Hypervisor Virtual Ethernet"); + return (0); +} + +static int +llan_attach(device_t dev) +{ + struct llan_softc *sc; + phandle_t node; + int error, i; + + sc = device_get_softc(dev); + sc->dev = dev; + + /* Get firmware properties */ + node = ofw_bus_get_node(dev); + OF_getprop(node, "local-mac-address", sc->mac_address, + sizeof(sc->mac_address)); + OF_getprop(node, "reg", &sc->unit, sizeof(sc->unit)); + + mtx_init(&sc->io_lock, "llan", NULL, MTX_DEF); + + /* Setup interrupt */ + sc->irqid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE); + + if (!sc->irq) { + device_printf(dev, "Could not allocate IRQ\n"); + mtx_destroy(&sc->io_lock); + return (ENXIO); + } + + bus_setup_intr(dev, sc->irq, INTR_TYPE_MISC | INTR_MPSAFE | + INTR_ENTROPY, NULL, llan_intr, sc, &sc->irq_cookie); + + /* Setup DMA */ + error = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + LLAN_RX_BUF_LEN, 1, BUS_SPACE_MAXSIZE_32BIT, + 0, NULL, NULL, &sc->rx_dma_tag); + error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE, 1, BUS_SPACE_MAXSIZE_32BIT, + 0, NULL, NULL, &sc->rxbuf_dma_tag); + error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE, 6, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &sc->io_lock, &sc->tx_dma_tag); + + error = bus_dmamem_alloc(sc->rx_dma_tag, (void **)&sc->rx_buf, + BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx_buf_map); + error = bus_dmamap_load(sc->rx_dma_tag, sc->rx_buf_map, sc->rx_buf, + LLAN_RX_BUF_LEN, llan_rx_load_cb, sc, 0); + + /* TX DMA maps */ + bus_dmamap_create(sc->tx_dma_tag, 0, &sc->tx_dma_map); + + /* RX DMA */ + for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) { + error = bus_dmamap_create(sc->rxbuf_dma_tag, 0, + &sc->rx_xfer[i].rx_dmamap); + sc->rx_xfer[i].rx_mbuf = NULL; + } + + /* Attach to network stack */ + sc->ifp = if_alloc(IFT_ETHER); + sc->ifp->if_softc = sc; + + if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); + sc->ifp->if_mtu = ETHERMTU; /* XXX max-frame-size from OF? */ + sc->ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + sc->ifp->if_hwassist = 0; /* XXX: ibm,illan-options */ + sc->ifp->if_capabilities = 0; + sc->ifp->if_capenable = 0; + sc->ifp->if_start = llan_start; + sc->ifp->if_ioctl = llan_ioctl; + sc->ifp->if_init = llan_init; + + IFQ_SET_MAXLEN(&sc->ifp->if_snd, LLAN_MAX_TX_PACKETS); + sc->ifp->if_snd.ifq_drv_maxlen = LLAN_MAX_TX_PACKETS; + IFQ_SET_READY(&sc->ifp->if_snd); + + ether_ifattach(sc->ifp, &sc->mac_address[2]); + + return (0); +} + +static void +llan_rx_load_cb(void *xsc, bus_dma_segment_t *segs, int nsegs, int err) +{ + struct llan_softc *sc = xsc; + + sc->rx_buf_phys = segs[0].ds_addr; + sc->rx_buf_len = segs[0].ds_len - 2*PAGE_SIZE; + sc->input_buf_phys = segs[0].ds_addr + segs[0].ds_len - PAGE_SIZE; + sc->filter_buf_phys = segs[0].ds_addr + segs[0].ds_len - 2*PAGE_SIZE; +} + +static void +llan_init(void *xsc) +{ + struct llan_softc *sc = xsc; + uint64_t rx_buf_desc; + uint64_t macaddr; + int err, i; + + mtx_lock(&sc->io_lock); + + phyp_hcall(H_FREE_LOGICAL_LAN, sc->unit); + + /* Create buffers (page 539) */ + sc->rx_dma_slot = 0; + sc->rx_valid_val = 1; + + rx_buf_desc = (1UL << 63); /* valid */ + rx_buf_desc |= (sc->rx_buf_len << 32); + rx_buf_desc |= sc->rx_buf_phys; + memcpy(&macaddr, sc->mac_address, 8); + err = phyp_hcall(H_REGISTER_LOGICAL_LAN, sc->unit, sc->input_buf_phys, + rx_buf_desc, sc->filter_buf_phys, macaddr); + + for (i = 0; i < LLAN_MAX_RX_PACKETS; i++) + llan_add_rxbuf(sc, &sc->rx_xfer[i]); + + phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); /* Enable interrupts */ + + /* Tell stack we're up */ + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + mtx_unlock(&sc->io_lock); +} + +static int +llan_add_rxbuf(struct llan_softc *sc, struct llan_xfer *rx) +{ + struct mbuf *m; + bus_dma_segment_t segs[1]; + int error, nsegs; + + mtx_assert(&sc->io_lock, MA_OWNED); + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); + + m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; + if (rx->rx_mbuf != NULL) { + bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->rxbuf_dma_tag, rx->rx_dmamap); + } + + /* Save pointer to buffer structure */ + m_copyback(m, 0, 8, (void *)&rx); + + error = bus_dmamap_load_mbuf_sg(sc->rxbuf_dma_tag, rx->rx_dmamap, m, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->dev, + "cannot load RX DMA map %p, error = %d\n", rx, error); + m_freem(m); + return (error); + } + + /* If nsegs is wrong then the stack is corrupt. */ + KASSERT(nsegs == 1, + ("%s: too many DMA segments (%d)", __func__, nsegs)); + rx->rx_mbuf = m; + + bus_dmamap_sync(sc->rxbuf_dma_tag, rx->rx_dmamap, BUS_DMASYNC_PREREAD); + + rx->rx_bufdesc = (1UL << 63); /* valid */ + rx->rx_bufdesc |= (((uint64_t)segs[0].ds_len) << 32); + rx->rx_bufdesc |= segs[0].ds_addr; + error = phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, rx->rx_bufdesc); + if (error != 0) { + m_freem(m); + rx->rx_mbuf = NULL; + return (ENOBUFS); + } + + return (0); +} + +static void +llan_intr(void *xsc) +{ + struct llan_softc *sc = xsc; + struct llan_xfer *rx; + struct mbuf *m; + + mtx_lock(&sc->io_lock); + phyp_hcall(H_VIO_SIGNAL, sc->unit, 0); + + while ((sc->rx_buf[sc->rx_dma_slot].control >> 7) == sc->rx_valid_val) { + rx = (struct llan_xfer *)sc->rx_buf[sc->rx_dma_slot].handle; + m = rx->rx_mbuf; + m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset - 8); + m->m_len = sc->rx_buf[sc->rx_dma_slot].length; + + /* llan_add_rxbuf does DMA sync and unload as well as requeue */ + if (llan_add_rxbuf(sc, rx) != 0) { + sc->ifp->if_ierrors++; + phyp_hcall(H_ADD_LOGICAL_LAN_BUFFER, sc->unit, + rx->rx_bufdesc); + continue; + } + + sc->ifp->if_ipackets++; + m_adj(m, sc->rx_buf[sc->rx_dma_slot].offset); + m->m_len = sc->rx_buf[sc->rx_dma_slot].length; + m->m_pkthdr.rcvif = sc->ifp; + m->m_pkthdr.len = m->m_len; + sc->rx_dma_slot++; + + if (sc->rx_dma_slot >= sc->rx_buf_len/sizeof(sc->rx_buf[0])) { + sc->rx_dma_slot = 0; + sc->rx_valid_val = !sc->rx_valid_val; + } + + mtx_unlock(&sc->io_lock); + (*sc->ifp->if_input)(sc->ifp, m); + mtx_lock(&sc->io_lock); + } + + phyp_hcall(H_VIO_SIGNAL, sc->unit, 1); + mtx_unlock(&sc->io_lock); +} + +static void +llan_send_packet(void *xsc, bus_dma_segment_t *segs, int nsegs, + bus_size_t mapsize, int error) +{ + struct llan_softc *sc = xsc; + uint64_t bufdescs[6]; + int i; + + bzero(bufdescs, sizeof(bufdescs)); + + for (i = 0; i < nsegs; i++) { + bufdescs[i] = (1UL << 63); /* valid */ + bufdescs[i] |= (((uint64_t)segs[i].ds_len) << 32); + bufdescs[i] |= segs[i].ds_addr; + } + + error = phyp_hcall(H_SEND_LOGICAL_LAN, sc->unit, bufdescs[0], + bufdescs[1], bufdescs[2], bufdescs[3], bufdescs[4], bufdescs[5], 0); +#if 0 + if (error) + sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; + + /* XXX: handle H_BUSY? */ + /* H_SEND_LOGICAL_LAN returning 0 implies completion of the send op */ +#endif +} + +static void +llan_start_locked(struct ifnet *ifp) +{ + struct llan_softc *sc = ifp->if_softc; + bus_addr_t first; + int nsegs; + struct mbuf *mb_head, *m; + + mtx_assert(&sc->io_lock, MA_OWNED); + first = 0; + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); + + if (mb_head == NULL) + break; + + BPF_MTAP(ifp, mb_head); + + for (m = mb_head, nsegs = 0; m != NULL; m = m->m_next) + nsegs++; + if (nsegs > 6) { + m = m_collapse(mb_head, M_NOWAIT, 6); + if (m == NULL) { + m_freem(mb_head); + continue; + } + } + + bus_dmamap_load_mbuf(sc->tx_dma_tag, sc->tx_dma_map, //xfer->dmamap, + mb_head, llan_send_packet, sc, 0); + bus_dmamap_unload(sc->tx_dma_tag, sc->tx_dma_map); // XXX + m_freem(mb_head); + } +} + +static void +llan_start(struct ifnet *ifp) +{ + struct llan_softc *sc = ifp->if_softc; + + mtx_lock(&sc->io_lock); + llan_start_locked(ifp); + mtx_unlock(&sc->io_lock); +} + +static int +llan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + int err; + + err = ether_ioctl(ifp, cmd, data); + + return (err); +} +
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201310200131.r9K1V9U5039632>