Skip site navigation (1)Skip section navigation (2)
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>