summaryrefslogtreecommitdiffstats
path: root/sys/dev/sfxge/sfxge_mcdi.c
diff options
context:
space:
mode:
authorphilip <philip@FreeBSD.org>2011-11-16 17:11:13 +0000
committerphilip <philip@FreeBSD.org>2011-11-16 17:11:13 +0000
commitd8198c572a23ba57ab4a97b436b0cfc41401268c (patch)
tree9a743f33d1ace33f6ed4c5c3d2fcd59a0496a32d /sys/dev/sfxge/sfxge_mcdi.c
parent3692d0165926e89d0ad0d746888b510d4e49348c (diff)
downloadFreeBSD-src-d8198c572a23ba57ab4a97b436b0cfc41401268c.zip
FreeBSD-src-d8198c572a23ba57ab4a97b436b0cfc41401268c.tar.gz
Add the sfxge(4) device driver, providing support for 10Gb Ethernet adapters
based on Solarflare SFC9000 family controllers. The driver supports jumbo frames, transmit/receive checksum offload, TCP Segmentation Offload (TSO), Large Receive Offload (LRO), VLAN checksum offload, VLAN TSO, and Receive Side Scaling (RSS) using MSI-X interrupts. This work was sponsored by Solarflare Communications, Inc. My sincere thanks to Ben Hutchings for doing a lot of the hard work! Sponsored by: Solarflare Communications, Inc. MFC after: 3 weeks
Diffstat (limited to 'sys/dev/sfxge/sfxge_mcdi.c')
-rw-r--r--sys/dev/sfxge/sfxge_mcdi.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/sys/dev/sfxge/sfxge_mcdi.c b/sys/dev/sfxge/sfxge_mcdi.c
new file mode 100644
index 0000000..6368ab4
--- /dev/null
+++ b/sys/dev/sfxge/sfxge_mcdi.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 2010-2011 Solarflare Communications, Inc.
+ * All rights reserved.
+ *
+ * This software was developed in part by Philip Paeps under contract for
+ * Solarflare Communications, Inc.
+ *
+ * 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/condvar.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/taskqueue.h>
+
+#include "common/efx.h"
+#include "common/efx_mcdi.h"
+#include "common/efx_regs_mcdi.h"
+
+#include "sfxge.h"
+
+#define SFXGE_MCDI_POLL_INTERVAL_MIN 10 /* 10us in 1us units */
+#define SFXGE_MCDI_POLL_INTERVAL_MAX 100000 /* 100ms in 1us units */
+#define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */
+
+/* Acquire exclusive access to MCDI for the duration of a request. */
+static void
+sfxge_mcdi_acquire(struct sfxge_mcdi *mcdi)
+{
+
+ mtx_lock(&mcdi->lock);
+ KASSERT(mcdi->state != SFXGE_MCDI_UNINITIALIZED,
+ ("MCDI not initialized"));
+
+ while (mcdi->state != SFXGE_MCDI_INITIALIZED)
+ (void)cv_wait_sig(&mcdi->cv, &mcdi->lock);
+ mcdi->state = SFXGE_MCDI_BUSY;
+
+ mtx_unlock(&mcdi->lock);
+}
+
+/* Release ownership of MCDI on request completion. */
+static void
+sfxge_mcdi_release(struct sfxge_mcdi *mcdi)
+{
+
+ mtx_lock(&mcdi->lock);
+ KASSERT((mcdi->state == SFXGE_MCDI_BUSY ||
+ mcdi->state == SFXGE_MCDI_COMPLETED),
+ ("MCDI not busy or task not completed"));
+
+ mcdi->state = SFXGE_MCDI_INITIALIZED;
+ cv_broadcast(&mcdi->cv);
+
+ mtx_unlock(&mcdi->lock);
+}
+
+static void
+sfxge_mcdi_timeout(struct sfxge_softc *sc)
+{
+ device_t dev = sc->dev;
+
+ log(LOG_WARNING, "[%s%d] MC_TIMEOUT", device_get_name(dev),
+ device_get_unit(dev));
+
+ EFSYS_PROBE(mcdi_timeout);
+ sfxge_schedule_reset(sc);
+}
+
+static void
+sfxge_mcdi_poll(struct sfxge_softc *sc)
+{
+ efx_nic_t *enp;
+ clock_t delay_total;
+ clock_t delay_us;
+ boolean_t aborted;
+
+ delay_total = 0;
+ delay_us = SFXGE_MCDI_POLL_INTERVAL_MIN;
+ enp = sc->enp;
+
+ do {
+ if (efx_mcdi_request_poll(enp)) {
+ EFSYS_PROBE1(mcdi_delay, clock_t, delay_total);
+ return;
+ }
+
+ if (delay_total > SFXGE_MCDI_WATCHDOG_INTERVAL) {
+ aborted = efx_mcdi_request_abort(enp);
+ KASSERT(aborted, ("abort failed"));
+ sfxge_mcdi_timeout(sc);
+ return;
+ }
+
+ /* Spin or block depending on delay interval. */
+ if (delay_us < 1000000)
+ DELAY(delay_us);
+ else
+ pause("mcdi wait", delay_us * hz / 1000000);
+
+ delay_total += delay_us;
+
+ /* Exponentially back off the poll frequency. */
+ delay_us = delay_us * 2;
+ if (delay_us > SFXGE_MCDI_POLL_INTERVAL_MAX)
+ delay_us = SFXGE_MCDI_POLL_INTERVAL_MAX;
+
+ } while (1);
+}
+
+static void
+sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
+{
+ struct sfxge_softc *sc;
+ struct sfxge_mcdi *mcdi;
+
+ sc = (struct sfxge_softc *)arg;
+ mcdi = &sc->mcdi;
+
+ sfxge_mcdi_acquire(mcdi);
+
+ /* Issue request and poll for completion. */
+ efx_mcdi_request_start(sc->enp, emrp, B_FALSE);
+ sfxge_mcdi_poll(sc);
+
+ sfxge_mcdi_release(mcdi);
+}
+
+static void
+sfxge_mcdi_ev_cpl(void *arg)
+{
+ struct sfxge_softc *sc;
+ struct sfxge_mcdi *mcdi;
+
+ sc = (struct sfxge_softc *)arg;
+ mcdi = &sc->mcdi;
+
+ mtx_lock(&mcdi->lock);
+ KASSERT(mcdi->state == SFXGE_MCDI_BUSY, ("MCDI not busy"));
+ mcdi->state = SFXGE_MCDI_COMPLETED;
+ cv_broadcast(&mcdi->cv);
+ mtx_unlock(&mcdi->lock);
+}
+
+static void
+sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
+{
+ struct sfxge_softc *sc;
+ device_t dev;
+
+ sc = (struct sfxge_softc *)arg;
+ dev = sc->dev;
+
+ log(LOG_WARNING, "[%s%d] MC_%s", device_get_name(dev),
+ device_get_unit(dev),
+ (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
+ ? "REBOOT"
+ : (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
+ ? "BADASSERT" : "UNKNOWN");
+
+ EFSYS_PROBE(mcdi_exception);
+
+ sfxge_schedule_reset(sc);
+}
+
+int
+sfxge_mcdi_init(struct sfxge_softc *sc)
+{
+ efx_nic_t *enp;
+ struct sfxge_mcdi *mcdi;
+ efx_mcdi_transport_t *emtp;
+ int rc;
+
+ enp = sc->enp;
+ mcdi = &sc->mcdi;
+ emtp = &mcdi->transport;
+
+ KASSERT(mcdi->state == SFXGE_MCDI_UNINITIALIZED,
+ ("MCDI already initialized"));
+
+ mtx_init(&mcdi->lock, "sfxge_mcdi", NULL, MTX_DEF);
+
+ mcdi->state = SFXGE_MCDI_INITIALIZED;
+
+ emtp->emt_context = sc;
+ emtp->emt_execute = sfxge_mcdi_execute;
+ emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl;
+ emtp->emt_exception = sfxge_mcdi_exception;
+
+ cv_init(&mcdi->cv, "sfxge_mcdi");
+
+ if ((rc = efx_mcdi_init(enp, emtp)) != 0)
+ goto fail;
+
+ return (0);
+
+fail:
+ mtx_destroy(&mcdi->lock);
+ mcdi->state = SFXGE_MCDI_UNINITIALIZED;
+ return (rc);
+}
+
+void
+sfxge_mcdi_fini(struct sfxge_softc *sc)
+{
+ struct sfxge_mcdi *mcdi;
+ efx_nic_t *enp;
+ efx_mcdi_transport_t *emtp;
+
+ enp = sc->enp;
+ mcdi = &sc->mcdi;
+ emtp = &mcdi->transport;
+
+ mtx_lock(&mcdi->lock);
+ KASSERT(mcdi->state == SFXGE_MCDI_INITIALIZED,
+ ("MCDI not initialized"));
+
+ efx_mcdi_fini(enp);
+ bzero(emtp, sizeof(*emtp));
+
+ cv_destroy(&mcdi->cv);
+ mtx_unlock(&mcdi->lock);
+
+ mtx_destroy(&mcdi->lock);
+}
OpenPOWER on IntegriCloud