lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:	Fri, 13 Jun 2008 21:12:52 +0100
From:	Robert Stonehouse <rstonehouse@...arflare.com>
To:	netdev@...r.kernel.org, linux-net-drivers@...arflare.com
Subject: [RFC][PATCH 1/2] Making use of VNICs in the sfc driver

---
 drivers/net/sfc/Makefile         |    4 +-
 drivers/net/sfc/driverlink.c     |  525 ++++++++++++++++++++++++++++++++
 drivers/net/sfc/driverlink.h     |   76 +++++
 drivers/net/sfc/driverlink_api.h |  614 ++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/efx.c            |   47 +++-
 drivers/net/sfc/falcon.c         |   88 ++++++-
 drivers/net/sfc/net_driver.h     |   15 +
 7 files changed, 1365 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/sfc/driverlink.c
 create mode 100644 drivers/net/sfc/driverlink.h
 create mode 100644 drivers/net/sfc/driverlink_api.h

diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index c8f5704..aa3bff7 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,5 +1,5 @@
 sfc-y			+= efx.o falcon.o tx.o rx.o falcon_xmac.o \
 			   selftest.o ethtool.o xfp_phy.o \
-			   mdio_10g.o tenxpress.o boards.o sfe4001.o
-
+			   mdio_10g.o tenxpress.o boards.o sfe4001.o \
+			   driverlink.o
 obj-$(CONFIG_SFC)	+= sfc.o
diff --git a/drivers/net/sfc/driverlink.c b/drivers/net/sfc/driverlink.c
new file mode 100644
index 0000000..93f29d5
--- /dev/null
+++ b/drivers/net/sfc/driverlink.c
@@ -0,0 +1,525 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005      Fen Systems Ltd.
+ * Copyright 2005-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "driverlink_api.h"
+#include "driverlink.h"
+
+/* Driverlink semaphore
+ * This semaphore must be held for any operation that modifies any of
+ * the driverlink lists.
+ */
+static DEFINE_MUTEX(efx_driverlink_lock);
+
+/* List of all registered drivers */
+static LIST_HEAD(efx_driver_list);
+
+/* List of all registered Efx ports */
+static LIST_HEAD(efx_port_list);
+
+/* Driver link handle used internally to track devices */
+struct efx_dl_handle {
+	/* The efx_dl_device consumers see */
+	struct efx_dl_device efx_dev;
+	/* The efx_nic providers provide */
+	struct efx_nic *efx;
+	/* Per-device list */
+	struct list_head port_node;
+	/* Per-driver list */
+	struct list_head driver_node;
+};
+
+/* Get the handle for an efx_dl_device */
+static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev)
+{
+	return container_of(efx_dev, struct efx_dl_handle, efx_dev);
+}
+
+/* Remove an Efx device
+ * You must hold the efx_driverlink_lock before calling this
+ * function.
+ */
+static void efx_dl_del_device(struct efx_dl_device *efx_dev)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+
+	EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n",
+		 efx_dev->driver->name);
+
+	/* Call driver's remove() routine */
+	if (efx_dev->driver->remove)
+		efx_dev->driver->remove(efx_dev);
+
+	/* Remove handle from per-driver and per-NIC lists */
+	list_del(&efx_handle->driver_node);
+	list_del(&efx_handle->port_node);
+
+	/* Free efx_handle structure */
+	kfree(efx_handle);
+}
+
+/* Try to add an Efx device
+ * Attempt to probe the given device with the driver, creating a
+ * new efx_dl_device. If the probe routine fails, because the driver
+ * doesn't support this port, then the efx_dl_device is destroyed,
+ */
+static void efx_dl_try_add_device(struct efx_nic *efx,
+				  struct efx_dl_driver *driver)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+	int rc;
+
+	/* Allocate and initialise new efx_dl_device structure */
+	efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL);
+	if (!efx_handle)
+		goto fail;
+	efx_dev = &efx_handle->efx_dev;
+	efx_handle->efx = efx;
+	efx_dev->driver = driver;
+	efx_dev->pci_dev = efx->pci_dev;
+	INIT_LIST_HEAD(&efx_handle->port_node);
+	INIT_LIST_HEAD(&efx_handle->driver_node);
+
+	/* Attempt driver probe */
+	rc = driver->probe(efx_dev, efx->net_dev,
+			   efx->dl_info, efx->silicon_rev);
+	if (rc)
+		goto fail;
+
+	/* Add device to per-driver and per-NIC lists */
+	list_add_tail(&efx_handle->driver_node, &driver->device_list);
+	list_add_tail(&efx_handle->port_node, &efx->dl_device_list);
+
+	EFX_INFO(efx, "%s driverlink client registered\n", driver->name);
+	return;
+
+ fail:
+	EFX_INFO(efx, "%s driverlink client skipped\n", driver->name);
+
+	kfree(efx_handle);
+}
+
+/**
+ * efx_dl_unregister_driver - unregister an Efx device driver
+ * @driver:		Efx driverlink driver
+ *
+ * Unregisters an Efx driver.  The driver's remove() method will be
+ * called for all Efx devices currently claimed by the driver.
+ */
+void efx_dl_unregister_driver(struct efx_dl_driver *driver)
+{
+	struct efx_dl_handle *efx_handle, *efx_handle_n;
+
+	printk(KERN_INFO "Efx driverlink unregistering %s driver\n",
+		 driver->name);
+
+	/* Acquire lock.  We can't return failure */
+	mutex_lock(&efx_driverlink_lock);
+
+	list_for_each_entry_safe(efx_handle, efx_handle_n,
+				 &driver->device_list, driver_node)
+		efx_dl_del_device(&efx_handle->efx_dev);
+
+	list_del(&driver->node);
+
+	mutex_unlock(&efx_driverlink_lock);
+}
+EXPORT_SYMBOL(efx_dl_unregister_driver);
+
+/**
+ * efx_dl_register_driver - register an Efx device driver
+ * @driver:		Efx driverlink driver
+ *
+ * Registers a new Efx driver.  The driver's probe() method will be
+ * called for all Efx NICs currently registered.
+ *
+ * Return a negative error code or 0 on success.
+ */
+int efx_dl_register_driver(struct efx_dl_driver *driver)
+{
+	struct efx_nic *efx;
+	int rc;
+
+	printk(KERN_INFO "Efx driverlink registering %s driver\n",
+		 driver->name);
+
+	/* Initialise driver list structures */
+	INIT_LIST_HEAD(&driver->node);
+	INIT_LIST_HEAD(&driver->device_list);
+
+	/* Acquire lock */
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	/* Add driver to driver list */
+	list_add_tail(&driver->node, &efx_driver_list);
+
+	/* Feed all existing devices to driver */
+	list_for_each_entry(efx, &efx_port_list, dl_node)
+		efx_dl_try_add_device(efx, driver);
+
+	/* Release locks */
+	mutex_unlock(&efx_driverlink_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(efx_dl_register_driver);
+
+void efx_dl_unregister_nic(struct efx_nic *efx)
+{
+	struct efx_dl_handle *efx_handle, *efx_handle_n;
+
+	if (!efx)
+		return;
+
+	/* Acquire lock.  We can't return failure, so have to use
+	 * down() instead of down_interruptible()
+	 */
+	mutex_lock(&efx_driverlink_lock);
+
+	/* Remove all devices related to this NIC */
+	list_for_each_entry_safe_reverse(efx_handle, efx_handle_n,
+					 &efx->dl_device_list,
+					 port_node)
+		efx_dl_del_device(&efx_handle->efx_dev);
+
+	/* Remove port from port list */
+	list_del(&efx->dl_node);
+
+	/* Release lock */
+	mutex_unlock(&efx_driverlink_lock);
+}
+
+int efx_dl_register_nic(struct efx_nic *efx)
+{
+	struct efx_dl_driver *driver;
+	int rc;
+
+	/* Acquire lock */
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	/* Add port to port list */
+	list_add_tail(&efx->dl_node, &efx_port_list);
+
+	/* Feed port to all existing drivers */
+	list_for_each_entry(driver, &efx_driver_list, node)
+		efx_dl_try_add_device(efx, driver);
+
+	/* Release lock */
+	mutex_unlock(&efx_driverlink_lock);
+
+	return 0;
+}
+
+/*
+ * Dummy callback implementations.
+ *
+ * To avoid a branch point on the fast-path, the callbacks are always
+ * implemented - they are never NULL.
+ */
+#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL)
+static enum efx_veto fastcall
+#else
+static enum efx_veto
+#endif
+efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, struct sk_buff *skb)
+{
+	/* Never veto the packet */
+	return EFX_ALLOW_PACKET;
+}
+
+#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL)
+static enum efx_veto fastcall
+#else
+static enum efx_veto
+#endif
+efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev,
+			     const char *pkt_buf, int len)
+{
+	/* Never veto the packet */
+	return EFX_ALLOW_PACKET;
+}
+
+static void
+efx_dummy_link_change_callback(struct efx_dl_device *efx_dev, int link_up)
+{
+}
+
+static int
+efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev, int new_mtu)
+{
+	/* Always allow */
+	return 0;
+}
+
+static void
+efx_dummy_mtu_changed_callback(struct efx_dl_device *efx_dev, int mtu)
+{
+	return;
+}
+
+static void efx_dummy_event_callback(struct efx_dl_device *efx_dev, void *event)
+{
+	return;
+}
+
+struct efx_dl_callbacks efx_default_callbacks = {
+	.tx_packet	= efx_dummy_tx_packet_callback,
+	.rx_packet	= efx_dummy_rx_packet_callback,
+	.link_change	= efx_dummy_link_change_callback,
+	.request_mtu	= efx_dummy_request_mtu_callback,
+	.mtu_changed	= efx_dummy_mtu_changed_callback,
+	.event		= efx_dummy_event_callback,
+};
+
+#define EFX_DL_UNREGISTER_CALLBACK(_port, _dev, _member)		\
+	do {								\
+		BUG_ON((_port)->dl_cb_dev._member != (_dev));		\
+		(_port)->dl_cb._member =				\
+			efx_default_callbacks._member;			\
+		(_port)->dl_cb_dev._member = NULL;			\
+	} while (0)
+
+
+#define EFX_DL_REGISTER_CALLBACK(_port, _dev, _from, _member)		\
+	if ((_from)->_member) {						\
+		BUG_ON((_port)->dl_cb_dev._member != NULL);		\
+		(_port)->dl_cb._member = (_from)->_member;		\
+		(_port)->dl_cb_dev._member = _dev;			\
+	}
+
+/**
+ * efx_dl_unregister_callbacks - unregister callbacks for an Efx NIC
+ * @efx_dev:		Efx driverlink device
+ * @callbacks:		Callback list
+ *
+ * This removes a set of callbacks registered with
+ * efx_dl_register_callbacks().  It should be called as part of the
+ * client's remove() method.
+ *
+ * The net driver will ensure that all callback functions have
+ * returned to the net driver before efx_dl_unregister_callbacks()
+ * returns.  Note that the device itself may still be running when the
+ * client's remove() method is called.  The client must therefore
+ * unhook its callbacks using efx_dl_unregister_callbacks() and only
+ * then ensure that any delayed tasks triggered by callback methods
+ * (e.g. scheduled tasklets) have completed.
+ */
+void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev,
+				 struct efx_dl_callbacks *callbacks)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+
+	/* Suspend net driver operations */
+	efx_suspend(efx);
+
+	EFX_INFO(efx, "removing callback hooks into %s driver\n",
+		 efx_dev->driver->name);
+
+	if (callbacks->tx_packet)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, tx_packet);
+
+	if (callbacks->rx_packet)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, rx_packet);
+
+	if (callbacks->link_change)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, link_change);
+
+	if (callbacks->request_mtu)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, request_mtu);
+
+	if (callbacks->mtu_changed)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, mtu_changed);
+
+	if (callbacks->event)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, event);
+
+	/* Resume net driver operations */
+	efx_resume(efx);
+}
+EXPORT_SYMBOL(efx_dl_unregister_callbacks);
+
+/**
+ * efx_dl_register_callbacks - register callbacks for an Efx NIC
+ * @efx_dev:		Efx driverlink device
+ * @callbacks:		Callback list
+ *
+ * This registers a set of callback functions with the net driver.
+ * These functions will be called at various key points to allow
+ * external code to monitor and/or modify the behaviour of the network
+ * driver.  Any of the callback function pointers may be %NULL if a
+ * callback is not required.  The intended user of this mechanism is
+ * the SFC char driver.
+ *
+ * This client should call efx_dl_register_callbacks() during its
+ * probe() method.  The client must ensure that it also calls
+ * efx_dl_unregister_callbacks() as part of its remove() method.
+ *
+ * Only one function may be registered for each callback per NIC.
+ * If a requested callback is already registered for this NIC, this
+ * function will return -%EBUSY.
+ *
+ * The device may already be running, so the client must be prepared
+ * for callbacks to be triggered immediately after calling
+ * efx_dl_register_callbacks().
+ *
+ * Return a negative error code or 0 on success.
+ */
+int efx_dl_register_callbacks(struct efx_dl_device *efx_dev,
+			      struct efx_dl_callbacks *callbacks)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+	int rc = 0;
+
+	/* Suspend net driver operations */
+	efx_suspend(efx);
+
+	/* Check that the requested callbacks are not already hooked. */
+	if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) ||
+	    (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) ||
+	    (callbacks->link_change && efx->dl_cb_dev.link_change) ||
+	    (callbacks->request_mtu && efx->dl_cb_dev.request_mtu) ||
+	    (callbacks->mtu_changed && efx->dl_cb_dev.mtu_changed) ||
+	    (callbacks->event && efx->dl_cb_dev.event)) {
+		rc = -EBUSY;
+		goto out;
+	}
+
+	EFX_INFO(efx, "adding callback hooks to %s driver\n",
+		 efx_dev->driver->name);
+
+	/* Hook in callbacks.  For maximum speed, we never check to
+	 * see whether these are NULL before calling; therefore we
+	 * must ensure that they are never NULL.  If the set we're
+	 * being asked to hook in is sparse, we leave the default
+	 * values in place for the empty hooks.
+	 */
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, tx_packet);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, rx_packet);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, link_change);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, request_mtu);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, mtu_changed);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, event);
+
+ out:
+	/* Resume net driver operations */
+	efx_resume(efx);
+
+	return rc;
+}
+EXPORT_SYMBOL(efx_dl_register_callbacks);
+
+/**
+ * efx_dl_schedule_reset - schedule an Efx NIC reset
+ * @efx_dev:		Efx driverlink device
+ *
+ * This schedules a hardware reset for a short time in the future.  It
+ * can be called from any context, and so can be used when
+ * efx_dl_reset() cannot be called.
+ */
+void efx_dl_schedule_reset(struct efx_dl_device *efx_dev)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+
+	efx_schedule_reset(efx, RESET_TYPE_ALL);
+}
+EXPORT_SYMBOL(efx_dl_schedule_reset);
+
+/*
+ * Lock the driverlink layer before a reset
+ * To avoid deadlock, efx_driverlink_lock needs to be acquired before
+ * efx->suspend_lock.
+ */
+void efx_dl_reset_lock(void)
+{
+	/* Acquire lock */
+	mutex_lock(&efx_driverlink_lock);
+}
+
+/*
+ * Unlock the driverlink layer after a reset
+ * This call must be matched against efx_dl_reset_lock.
+ */
+void efx_dl_reset_unlock(void)
+{
+	/* Acquire lock */
+	mutex_unlock(&efx_driverlink_lock);
+}
+
+/*
+ * Suspend ready for reset
+ * This calls the reset_suspend method of all drivers registered to
+ * the specified NIC.  It must only be called between
+ * efx_dl_reset_lock and efx_dl_reset_unlock.
+ */
+void efx_dl_reset_suspend(struct efx_nic *efx)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	BUG_ON(!mutex_is_locked(&efx_driverlink_lock));
+
+	/* Call suspend method of each driver in turn */
+	list_for_each_entry_reverse(efx_handle,
+				    &efx->dl_device_list,
+				    port_node) {
+		efx_dev = &efx_handle->efx_dev;
+		if (efx_dev->driver->reset_suspend)
+			efx_dev->driver->reset_suspend(efx_dev);
+	}
+}
+
+/*
+ * Resume after a reset
+ * This calls the reset_resume method of all drivers registered to the
+ * specified NIC.  It must only be called between efx_dl_reset_lock
+ * and efx_dl_reset_unlock.
+ */
+void efx_dl_reset_resume(struct efx_nic *efx, int ok)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	BUG_ON(!mutex_is_locked(&efx_driverlink_lock));
+
+	/* Call resume method of each driver in turn */
+	list_for_each_entry(efx_handle, &efx->dl_device_list,
+			    port_node) {
+		efx_dev = &efx_handle->efx_dev;
+		if (efx_dev->driver->reset_resume)
+			efx_dev->driver->reset_resume(efx_dev, ok);
+	}
+}
+
+/**
+ * efx_dl_get_nic - obtain the Efx NIC for the given driverlink device
+ * @efx_dev:		Efx driverlink device
+ *
+ * Get a pointer to the &struct efx_nic corresponding to
+ * @efx_dev.  This can be used by driverlink clients built along with
+ * the sfc driver, which may have intimate knowledge of its internals.
+ */
+struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev)
+{
+	return efx_dl_handle(efx_dev)->efx;
+}
+EXPORT_SYMBOL(efx_dl_get_nic);
diff --git a/drivers/net/sfc/driverlink.h b/drivers/net/sfc/driverlink.h
new file mode 100644
index 0000000..889acf5
--- /dev/null
+++ b/drivers/net/sfc/driverlink.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005      Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_DRIVERLINK_H
+#define EFX_DRIVERLINK_H
+
+/* Forward declarations */
+struct efx_dl_device;
+struct efx_nic;
+
+/*
+ * Efx driverlink
+ *
+ * This header file defines the portions of the Efx driverlink
+ * interface that are used only within the sfc module.  It also
+ * declares efx_dl_get_nic(), which may be used by sfc_mtd
+ * and any other module built along with sfc.
+ */
+
+
+/* Efx callback devices
+ *
+ * A list of the devices that own each callback. The partner to
+ * struct efx_dl_callbacks
+ */
+struct efx_dl_cb_devices {
+	/* Device owning the tx_packet callback */
+	struct efx_dl_device *tx_packet;
+	/* Device owning the rx_packet callback */
+	struct efx_dl_device *rx_packet;
+	/* Device owning the link_change callback. */
+	struct efx_dl_device *link_change;
+	/* Device owning the request_mtu callback. */
+	struct efx_dl_device *request_mtu;
+	/* Device owning the mtu_changed callback. */
+	struct efx_dl_device *mtu_changed;
+	/* Device owning the event callback. */
+	struct efx_dl_device *event;
+};
+
+/* No-op callbacks used for initialisation */
+extern struct efx_dl_callbacks efx_default_callbacks;
+
+/* Macro used to invoke callbacks */
+#define EFX_DL_CALLBACK(_port, _name, ...)				\
+	(_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__)
+
+/* Register an Efx NIC */
+extern int efx_dl_register_nic(struct efx_nic *efx);
+
+/* Unregister an Efx NIC */
+extern void efx_dl_unregister_nic(struct efx_nic *efx);
+
+/* Lock the driverlink layer prior to a reset */
+extern void efx_dl_reset_lock(void);
+
+/* Unlock the driverlink layer following a reset */
+extern void efx_dl_reset_unlock(void);
+
+/* Suspend all drivers prior to a hardware reset */
+extern void efx_dl_reset_suspend(struct efx_nic *efx);
+
+/* Resume all drivers after a hardware reset */
+extern void efx_dl_reset_resume(struct efx_nic *efx, int ok);
+
+/* Obtain the Efx NIC for the given driverlink device. */
+extern struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev);
+
+#endif /* EFX_DRIVERLINK_H */
diff --git a/drivers/net/sfc/driverlink_api.h b/drivers/net/sfc/driverlink_api.h
new file mode 100644
index 0000000..1dc6050
--- /dev/null
+++ b/drivers/net/sfc/driverlink_api.h
@@ -0,0 +1,614 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2005-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_DRIVERLINK_API_H
+#define EFX_DRIVERLINK_API_H
+
+#include <linux/list.h> /* for struct list_head */
+#if defined(EFX_USE_KCOMPAT) && !defined(EFX_USE_FASTCALL)
+	#include <linux/version.h>
+	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+		#define EFX_USE_FASTCALL yes
+		#include <linux/linkage.h>
+	#endif
+#endif
+
+/**
+ * DOC: Efx driverlink API
+ *
+ * This file must be included by any driver that wishes to attach to
+ * devices claimed by the Solarflare NIC driver (sfc). It allows separate
+ * kernel modules to expose other functionality offered by the NIC, with
+ * the sfc driver remaining in overall control.
+ *
+ * Overview:
+ *
+ * Driverlink clients define a &struct efx_dl_driver, and register
+ * this structure with the driverlink layer using
+ * efx_dl_register_driver(), which is exported by the sfc driver.
+ *
+ * The probe() routine of each driverlink client driver is called by
+ * the driverlink layer for each physical port in the system, after
+ * the sfc driver has performed start-of-day hardware initialisation
+ * and self-test. If ports are added or removed via pci hotplug then
+ * the &struct efx_dl_driver probe() or remove() routines are called
+ * as appropriate.
+ *
+ * If the port doesn't provide the necessary hardware resources for a
+ * client, then that client can return failure from its probe()
+ * routine. Information provided to the client driver at probe time
+ * includes
+ *
+ * Each probe() routine is given a unique &struct efx_dl_device per
+ * port, which means it can safely use the @priv member to store any
+ * useful state it needs. The probe routine also has the opportunity
+ * to provide a &struct efx_dl_callbacks via
+ * efx_dl_register_callbacks(), which allows the client to intercept
+ * the sfc driver's operations at strategic points.
+ *
+ * Occasionally, the underlying Efx device may need to be reset to
+ * recover from an error condition.  The client's reset_suspend() and
+ * reset_resume() methods [if provided] will be called to enable the
+ * client to suspend operations and preserve any state before the
+ * reset.  The client can itself request a reset using efx_dl_reset()
+ * or efx_dl_schedule_reset(), should it detect an error condition
+ * necessitating a reset.
+ *
+ * Example:
+ *
+ * The MTD driver (mtd.c) uses the driverlink layer.
+ */
+#ifdef EFX_NOT_UPSTREAM
+/**
+ * DOC: Out-of-tree use of Efx driverlink API
+ *
+ * The file demo.c illustrates most aspects of using the
+ * driverlink API, and is well worth reading.
+ *
+ * By design, this file does *not* drag in any of the private header
+ * files used by the sfc driver.  In particular, this means that
+ * &struct efx_nic is not defined by including this header.  Include
+ * net_driver.h if you need access to the internals of &struct efx_nic.
+ * DO NOT use sfc driver internals in any driver that must work as an
+ * out-of-tree driver together with an in-tree (kernel.org or
+ * distribution) version of the sfc driver.  Changes in net_driver.h
+ * will NOT be reflected in %EFX_DRIVERLINK_API_VERSION, but the
+ * standard symbol versioning mechanism works between modules that are
+ * both built in-tree or both built in the v5 tree.
+ */
+#endif
+
+/* Forward declarations */
+struct pci_dev;
+struct net_device;
+struct sk_buff;
+struct efx_dl_device;
+struct efx_dl_device_info;
+
+/*
+ * This is used to guard against the registration of driverlink
+ * clients using an incorrect version of the API.
+ */
+#define EFX_DRIVERLINK_API_VERSION 1
+
+
+/**
+ * struct efx_dl_driver - An Efx driverlink device driver
+ *
+ * This is the analogue of a struct pci_driver for a normal PCI
+ * driver.  Driverlink clients should register themselves using
+ * efx_dl_register_driver() at module initialisation, and deregister
+ * themselves using efx_dl_unregister_driver() at module exit.
+ *
+ * All calls to members of efx_dl_driver are serialised by a single
+ * semaphore, so you are allowed to sleep in these functions. Take care
+ * to not call driverlink methods from within these callbacks, otherwise
+ * a deadlock is possible.
+ *
+ * @name: Name of the driver
+ * @probe: Called when device added
+ * @remove: Called when device removed
+ * @reset_suspend: Called before device is reset
+ * @reset_resume: Called after device is reset
+ */
+struct efx_dl_driver {
+	const char *name;
+
+	/*
+	 * probe - Handle device addition.
+	 * @efx_dev:		Efx driverlink device
+	 * @net_dev:		The net_dev relevant to this port
+	 * @dev_info:		A linked list of device information.
+	 * @silicon_rev:	Silicon revision name.
+	 *
+	 * This will be called after driverlink client registration for
+	 * every port on the system, and for every port that appears
+	 * thereafter via hotplug.
+	 *
+	 * The client may use either @efx_dev->pci_dev, the dev_info linked
+	 * list of available driver information, or the silicon revision
+	 * name to determine if they can support this port. If they can,
+	 * they should return 0 to indicate the probe was successful. Any
+	 * other return code indicates that the probe failed, and the
+	 * @efx_dl_dev will be invalidated.
+	 *
+	 * The client should perform whatever initialisation it
+	 * requires, and store a pointer to its private data in
+	 * @efx_dl_dev->priv (which is not shared between clients).
+	 * It may also wish to hook in a callbacks table using
+	 * efx_dl_register_callbacks().
+	 *
+	 * Return a negative error code or 0 on success.
+	 */
+	int (*probe) (struct efx_dl_device *efx_dl_dev,
+		      const struct net_device *net_dev,
+		      const struct efx_dl_device_info *dev_info,
+		      const char *silicon_rev);
+
+	/*
+	 * remove - Handle device removal.
+	 * @efx_dev:		Efx driverlink device
+	 *
+	 * This will be called at driver exit (or hotplug removal) for
+	 * each registered driverlink client.
+	 *
+	 * The client must ensure that it has finished all operations
+	 * using this device before returning from this method.  If it
+	 * has hooked in a callbacks table using
+	 * efx_dl_register_callbacks(), it must unhook it using
+	 * efx_dl_unregister_callbacks(), and then ensure that all
+	 * callback-triggered operations (e.g. scheduled tasklets)
+	 * have completed before returning.  (It does not need to
+	 * explicitly wait for callback methods to finish executing,
+	 * since efx_dl_unregister_callbacks() will sleep until all
+	 * callbacks have returned anyway.)
+	 *
+	 * Note that the device itself may not have been removed; it
+	 * may be simply that the client is being unloaded
+	 * via efx_dl_unregister_driver(). In this case other clients
+	 * (and the sfc driver itself) will still be using the device,
+	 * so the client cannot assume that the device itself is quiescent.
+	 * In particular, callbacks may continue to be triggered at any
+	 * point until efx_dl_unregister_callbacks() is called.
+	 */
+	void (*remove) (struct efx_dl_device *efx_dev);
+
+	/*
+	 * reset_suspend - Suspend ready for reset.
+	 * @efx_dev:		Efx driverlink device
+	 *
+	 * This method will be called immediately before a hardware
+	 * reset (which may or may not have been initiated by the
+	 * driverlink client).  This client must save any state that it
+	 * will need to restore after the reset, and suspend all
+	 * operations that might access the hardware.  It must not
+	 * return until the client can guarantee to have stopped
+	 * touching the hardware.
+	 *
+	 * It is guaranteed that callbacks will be inactive by the
+	 * time this method is called; the driverlink layer will
+	 * already have prevented new callbacks being made and waited
+	 * for all callbacks functions to return before calling
+	 * reset_suspend().  However, any delayed work scheduled by
+	 * the callback functions (e.g. tasklets) may not yet have
+	 * completed.
+	 *
+	 * This method is allowed to sleep, so waiting on tasklets,
+	 * work queues etc. is permitted.  There will always be a
+	 * corresponding call to the reset_resume() method, so it is
+	 * safe to e.g. down a semaphore within reset_suspend() and up
+	 * it within reset_resume().  (However, you obviously cannot
+	 * do the same with a spinlock).
+	 *
+	 * Note that the reset operation may be being carried out in
+	 * the context of scheduled work, so you cannot use
+	 * flush_scheduled_work() to ensure that any work you may have
+	 * scheduled has completed.
+	 *
+	 * During hardware reset, there is a chance of receiving
+	 * spurious interrupts, so the client's ISR (if any) should be
+	 * unhooked or otherwise disabled.
+	 */
+	void (*reset_suspend) (struct efx_dl_device *efx_dev);
+
+	/*
+	 * reset_resume - Restore after a reset.
+	 * @efx_dev:		Efx driverlink device
+	 * @ok:			Reset success indicator
+	 *
+	 * This method will be called after a hardware reset.  There
+	 * will always have been a corresponding call to the
+	 * reset_suspend() method beforehand.
+	 *
+	 * If @ok is non-zero, the client should restore the state
+	 * that it saved during the call to reset_suspend() and resume
+	 * normal operations.
+	 *
+	 * If @ok is zero, the reset operation has failed and the
+	 * hardware is currently in an unusable state.  In this case,
+	 * the client should release any locks taken out by
+	 * reset_suspend(), but should not take any other action; in
+	 * particular, it must not access the hardware, nor resume
+	 * normal operations.  The hardware is effectively dead at
+	 * this point, and our sole aim is to avoid deadlocking or
+	 * crashing the host.
+	 *
+	 * The driverlink layer will still be locked when
+	 * reset_resume() is called, so the client may not call
+	 * driverlink functions.  In particular, if the reset failed,
+	 * the client must not call efx_dl_unregister_callbacks() at
+	 * this point; it should wait until remove() is called.
+	 */
+	void (*reset_resume) (struct efx_dl_device *efx_dev, int ok);
+
+/* private: */
+	struct list_head node;
+	struct list_head device_list;
+};
+
+/**
+ * DOC: Efx driverlink device information
+ *
+ * Each &struct efx_dl_device makes certain hardware resources visible
+ * to driverlink clients, and they describe which resources are
+ * available by passing a linked list of &struct efx_dl_device_info
+ * into the probe() routine.
+ *
+ * The driverlink client's probe function can iterate through the linked list,
+ * and provided that it understands the resources that are exported, it can
+ * choose to make use of them through an external interface.
+ */
+
+/**
+ * enum efx_dl_device_info_type - Device information identifier.
+ *
+ * Each distinct hardware resource API will have a member in this
+ * enumeration.
+ *
+ * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources
+ */
+enum efx_dl_device_info_type {
+	/** Falcon resources available for export */
+	EFX_DL_FALCON_RESOURCES = 0,
+};
+
+/**
+ * struct efx_dl_device_info - device information structure
+ * @next: Link to next structure, if any
+ * @type: Type code for this structure
+ *
+ * This structure is embedded in other structures provided by the
+ * driverlink device provider, and implements a linked list of
+ * resources pertinent to a driverlink client.
+ *
+ * Example: &struct efx_dl_falcon_resources
+ */
+struct efx_dl_device_info {
+	struct efx_dl_device_info *next;
+	enum efx_dl_device_info_type type;
+};
+
+/**
+ * enum efx_dl_falcon_resource_flags - Falcon resource information flags.
+ *
+ * Flags that describe hardware variations for the described Falcon based port.
+ *
+ * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function.
+ *	Certain silicon revisions have two pci functions, and require
+ *	certain hardware resources to be accessed via the secondary
+ *	function. See the discussion of @pci_dev in &struct efx_dl_device
+ *	below.
+ * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts.
+ *	Falcon supports traditional legacy interrupts and MSI/MSI-X
+ *	interrupts. Since the sfc driver supports either, as a run
+ *	time configuration, driverlink drivers need to be aware of which
+ *	one to use for their interrupting resources.
+ */
+enum efx_dl_falcon_resource_flags {
+	EFX_DL_FALCON_DUAL_FUNC = 0x1,
+	EFX_DL_FALCON_USE_MSI = 0x2,
+};
+
+/**
+ * struct efx_dl_falcon_resources - Falcon resource information.
+ *
+ * This structure describes Falcon hardware resources available for
+ * use by a driverlink driver.
+ *
+ * @hdr: Resource linked list header
+ * @biu_lock: Register access lock.
+ *	Some Falcon revisions require register access for configuration
+ *	registers to be serialised between ports and PCI functions.
+ *	The sfc driver will provide the appropriate lock semantics for
+ *	the underlying hardware.
+ * @buffer_table_min: First available buffer table entry
+ * @buffer_table_lim: Last available buffer table entry + 1
+ * @evq_timer_min: First available event queue with timer
+ * @evq_timer_lim: Last available event queue with timer + 1
+ * @evq_int_min: First available event queue with interrupt
+ * @evq_int_lim: Last available event queue with interrupt + 1
+ * @rxq_min: First available RX queue
+ * @rxq_lim: Last available RX queue + 1
+ * @txq_min: First available TX queue
+ * @txq_lim: Last available TX queue + 1
+ * @flags: Hardware variation flags
+ */
+struct efx_dl_falcon_resources {
+	struct efx_dl_device_info hdr;
+	spinlock_t *biu_lock;
+	unsigned buffer_table_min, buffer_table_lim;
+	unsigned evq_timer_min, evq_timer_lim;
+	unsigned evq_int_min, evq_int_lim;
+	unsigned rxq_min, rxq_lim;
+	unsigned txq_min, txq_lim;
+	enum efx_dl_falcon_resource_flags flags;
+};
+
+/**
+ * struct efx_dl_device - An Efx driverlink device.
+ *
+ * @pci_dev: Underlying PCI device.
+ *	This is the PCI device used by the sfc driver.  It will
+ *	already have been enabled for bus-mastering DMA etc.
+ * @priv: Driver private data
+ *	Driverlink clients can use this to store a pointer to their
+ *	internal per-device data structure. Each (driver, device)
+ *	tuple has a separate &struct efx_dl_device, so clients can use
+ *	this @priv field independently.
+ * @driver: Efx driverlink driver for this device
+ */
+struct efx_dl_device {
+	struct pci_dev *pci_dev;
+	void *priv;
+	struct efx_dl_driver *driver;
+};
+
+/**
+ * enum efx_veto - Packet veto request flag.
+ *
+ * This is the return type for the rx_packet() and tx_packet() methods
+ * in &struct efx_dl_callbacks.
+ *
+ * @EFX_ALLOW_PACKET: Packet may be transmitted/received
+ * @EFX_VETO_PACKET: Packet must not be transmitted/received
+ */
+enum efx_veto {
+	EFX_ALLOW_PACKET = 0,
+	EFX_VETO_PACKET = 1,
+};
+
+/**
+ * struct efx_dl_callbacks - Efx callbacks
+ *
+ * These methods can be hooked in to the sfc driver via
+ * efx_dl_register_callbacks().  They allow clients to intercept and/or
+ * modify the behaviour of the sfc driver at predetermined points.
+ *
+ * For efficiency, only one client can hook each callback.
+ *
+ * Since these callbacks are called on packet transmit and reception
+ * paths, clients should avoid acquiring locks or allocating memory.
+ *
+ * @tx_packet: Called when packet is about to be transmitted
+ * @rx_packet: Called when packet is received
+ * @link_change: Called when link status has changed
+ * @request_mtu: Called to request MTU change
+ * @mtu_changed: Called when MTU has been changed
+ * @event: Called when NIC event is not handled by the sfc driver
+ */
+struct efx_dl_callbacks {
+	/*
+	 * tx_packet - Packet about to be transmitted.
+	 * @efx_dev:		Efx driverlink device
+	 * @skb:		Socket buffer containing the packet to be sent
+	 *
+	 * This method is called for every packet about to be
+	 * transmitted.  It allows the client to snoop on traffic sent
+	 * via the kernel queues.
+	 *
+	 * The method may return %EFX_VETO_PACKET in order to prevent
+	 * the sfc driver from transmitting the packet.  The net
+	 * driver will then discard the packet.  If the client wishes
+	 * to retain a reference to the packet data after returning
+	 * %EFX_VETO_PACKET, it must obtain its own copy of the
+	 * packet (e.g. by calling skb_get(), or by copying out the
+	 * packet data to an external buffer).
+	 *
+	 * This method must return quickly, since it will have a
+	 * direct performance impact upon the sfc driver.  It will be
+	 * called with interrupts disabled (and may be called in
+	 * interrupt context), so may not sleep. Since the sfc driver
+	 * may have multiple TX queues, running in parallel, please avoid
+	 * the need for locking if it all possible.
+	 */
+#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL)
+	enum efx_veto fastcall (*tx_packet) (struct efx_dl_device *efx_dev,
+					     struct sk_buff *skb);
+#else
+	enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev,
+				    struct sk_buff *skb);
+#endif
+
+	/*
+	 * rx_packet - Packet received.
+	 * @efx_dev:		Efx driverlink device
+	 * @pkt_hdr:		Pointer to received packet
+	 * @pkt_len:		Length of received packet
+	 *
+	 * This method is called for every received packet.  It allows
+	 * the client to snoop on traffic received by the kernel
+	 * queues.
+	 *
+	 * The method may return %EFX_VETO_PACKET in order to prevent
+	 * the sfc driver from passing the packet to the kernel.  The net
+	 * driver will then discard the packet.
+	 *
+	 * This method must return quickly, since it will have a
+	 * direct performance impact upon the sfc driver.  It is
+	 * called in tasklet context, so may not sleep.  Note that
+	 * there are per-channel tasklets in the sfc driver, so
+	 * rx_packet() may be called simultaneously on different CPUs
+	 * and must lock appropriately.  The design of the sfc driver
+	 * allows for lockless operation between receive channels, so
+	 * please avoid the need for locking if at all possible.
+	 */
+#if defined(EFX_USE_KCOMPAT) && defined(EFX_USE_FASTCALL)
+	enum efx_veto fastcall (*rx_packet) (struct efx_dl_device *efx_dev,
+					     const char *pkt_hdr, int pkt_len);
+#else
+	enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev,
+				    const char *pkt_hdr, int pkt_len);
+#endif
+
+	/*
+	 * link_change - Link status change.
+	 * @efx_dev:		Efx driverlink device
+	 * @link_up:		Link up indicator
+	 *
+	 * This method is called to inform the driverlink client
+	 * whenever the PHY link status changes.  By the time this
+	 * function is called, the MAC has already been reconfigured
+	 * with the new autonegotiation settings from the PHY.
+	 *
+	 * This method is called from tasklet context and may not
+	 * sleep.
+	 */
+	void (*link_change) (struct efx_dl_device *efx_dev, int link_up);
+
+	/*
+	 * request_mtu: Request MTU change.
+	 * @efx_dev:		Efx driverlink device
+	 * @new_mtu:		Requested new MTU
+	 *
+	 * This method is called whenever the user requests an MTU
+	 * change on an interface.  The client may return an error, in
+	 * which case the MTU change request will be denied.  If the
+	 * client returns success, the MAC will be reconfigured with a
+	 * new maxmimum frame length equal to
+	 * EFX_MAX_FRAME_LEN(new_mtu).  The client will be notified
+	 * via the mtu_changed() method once the MAC has been
+	 * reconfigured.
+	 *
+	 * The current MTU for the port can be obtained via
+	 * efx_dl_get_netdev(efx_dl_device)->mtu.
+	 *
+	 * The sfc driver guarantees that no other callback functions
+	 * are in progress when this method is called.  This function
+	 * is called in process context and may sleep.
+	 *
+	 * Return a negative error code or 0 on success.
+	 */
+	int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu);
+
+	/*
+	 * mtu_changed - MTU has been changed.
+	 * @efx_dev:		Efx driverlink device
+	 * @mtu:		The new MTU
+	 *
+	 * This method is called once the MAC has been reconfigured
+	 * with a new MTU.  There will have been a preceding call to
+	 * request_mtu().
+	 *
+	 * The sfc driver guarantees that no other callback functions
+	 * are in progress when this method is called.  This function
+	 * is called in process context and may sleep.
+	 */
+	void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu);
+
+	/*
+	 * event - Event callback.
+	 * @efx_dev:		Efx driverlink device
+	 * @p_event:		Pointer to event
+	 *
+	 * This method is called for each event that is not handled by the
+	 * sfc driver.
+	 */
+	void (*event) (struct efx_dl_device *efx_dev, void *p_event);
+};
+
+/* Include API version number in symbol used for efx_dl_register_driver */
+#define efx_dl_stringify_1(x, y) x ## y
+#define efx_dl_stringify_2(x, y) efx_dl_stringify_1(x, y)
+#define efx_dl_register_driver					\
+	efx_dl_stringify_2(efx_dl_register_driver_api_ver_,	\
+			   EFX_DRIVERLINK_API_VERSION)
+
+extern int efx_dl_register_driver(struct efx_dl_driver *driver);
+
+extern void efx_dl_unregister_driver(struct efx_dl_driver *driver);
+
+extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev,
+				     struct efx_dl_callbacks *callbacks);
+
+extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev,
+					struct efx_dl_callbacks *callbacks);
+
+extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev);
+
+/**
+ * efx_dl_for_each_device_info_matching - iterate an efx_dl_device_info list
+ * @_dev_info: Pointer to first &struct efx_dl_device_info
+ * @_type: Type code to look for
+ * @_info_type: Structure type corresponding to type code
+ * @_field: Name of &struct efx_dl_device_info field in the type
+ * @_p: Iterator variable
+ *
+ * Example:
+ *
+ * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...)
+ * {
+ *        struct efx_dl_falcon_resources *res;
+ *
+ *        efx_dl_for_each_device_info_matching(dev_info,EFX_DL_FALCON_RESOURCES,
+ *                                             struct efx_dl_falcon_resources,
+ *                                             hdr, res) {
+ *                if (res->flags & EFX_DL_FALCON_DUAL_FUNC) {
+ *                          .....
+ *                }
+ *        }
+ * }
+ */
+#define efx_dl_for_each_device_info_matching(_dev_info, _type,		\
+					     _info_type, _field, _p)	\
+	for ((_p) = container_of((_dev_info), _info_type, _field);	\
+	     (_p) != NULL;						\
+	     (_p) = container_of((_p)->_field.next, _info_type, _field))\
+		if ((_p)->_field.type != _type)				\
+			continue;					\
+		else
+
+/**
+ * efx_dl_search_device_info - search an efx_dl_device_info list
+ * @_dev_info: Pointer to first &struct efx_dl_device_info
+ * @_type: Type code to look for
+ * @_info_type: Structure type corresponding to type code
+ * @_field: Name of &struct efx_dl_device_info member in this type
+ * @_p: Result variable
+ *
+ * Example:
+ *
+ * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...)
+ * {
+ *        struct efx_dl_falcon_resources *res;
+ *
+ *        efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES,
+ *                                  struct efx_dl_falcon_resources, hdr, res);
+ *        if (res != NULL) {
+ *                 ....
+ *        }
+ * }
+ */
+#define efx_dl_search_device_info(_dev_info, _type, _info_type,		\
+				  _field, _p)				\
+	efx_dl_for_each_device_info_matching((_dev_info), (_type),	\
+					     _info_type, _field, (_p))	\
+		break;
+
+#endif /* EFX_DRIVERLINK_API_H */
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 74265d8..0e153cb 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -544,6 +544,9 @@ static void efx_link_status_changed(struct efx_nic *efx)
 			netif_carrier_off(efx->net_dev);
 	}

+	/* Inform driverlink client */
+	EFX_DL_CALLBACK(efx, link_change, efx->link_up);
+
 	/* Status message for kernel log */
 	if (efx->link_up) {
 		struct mii_if_info *gmii = &efx->mii;
@@ -1410,6 +1413,8 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu)

 	efx_stop_all(efx);

+	/* Ask driverlink client if we can change MTU */
+	rc = EFX_DL_CALLBACK(efx, request_mtu, new_mtu);
 	EFX_LOG(efx, "changing MTU to %d\n", new_mtu);

 	efx_fini_channels(efx);
@@ -1418,6 +1423,9 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu)
 	if (rc)
 		goto fail;

+	/* Notify driverlink client of new MTU */
+	EFX_DL_CALLBACK(efx, mtu_changed, new_mtu);
+
 	efx_start_all(efx);
 	return rc;

@@ -1570,6 +1578,23 @@ static void efx_unregister_netdev(struct efx_nic *efx)
  * Device reset and suspend
  *
  **************************************************************************/
+/* Serialise access to the driverlink callbacks, by quiescing event processing
+ * (without flushing the descriptor queues), and acquiring the rtnl_lock */
+void efx_suspend(struct efx_nic *efx)
+{
+	EFX_LOG(efx, "suspending operations\n");
+
+	rtnl_lock();
+	efx_stop_all(efx);
+}
+
+void efx_resume(struct efx_nic *efx)
+{
+	EFX_LOG(efx, "resuming operations\n");
+
+	efx_start_all(efx);
+	rtnl_unlock();
+}

 /* The final hardware and software finalisation before reset. */
 static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
@@ -1632,6 +1657,10 @@ static int efx_reset(struct efx_nic *efx)
 	enum reset_type method = efx->reset_pending;
 	int rc;

+	/* Notify driverlink clients of imminent reset. */
+	efx_dl_reset_lock();
+	efx_dl_reset_suspend(efx);
+
 	/* Serialise with kernel interfaces */
 	rtnl_lock();

@@ -1701,8 +1730,9 @@ static int efx_reset(struct efx_nic *efx)

  unlock_rtnl:
 	rtnl_unlock();
+	efx_dl_reset_resume(efx, 1);
+	efx_dl_reset_unlock();
 	return 0;
-
  fail5:
  fail4:
  fail3:
@@ -1715,6 +1745,8 @@ static int efx_reset(struct efx_nic *efx)
 	rtnl_unlock();
 	efx_unregister_netdev(efx);
 	efx_fini_port(efx);
+	efx_dl_reset_resume(efx, 0);
+	efx_dl_reset_unlock();
 	return rc;
 }

@@ -1854,6 +1886,9 @@ static int efx_init_struct(struct efx_nic *efx, struct
efx_nic_type *type,
 	mutex_init(&efx->mac_lock);
 	efx->phy_op = &efx_dummy_phy_operations;
 	efx->mii.dev = net_dev;
+	INIT_LIST_HEAD(&efx->dl_node);
+	INIT_LIST_HEAD(&efx->dl_device_list);
+	efx->dl_cb = efx_default_callbacks;
 	INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work);
 	atomic_set(&efx->netif_stop_count, 1);

@@ -1960,6 +1995,9 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 	if (!efx)
 		return;

+	/* Unregister driver from driverlink layer */
+	efx_dl_unregister_nic(efx);
+
 	/* Mark the NIC as fini, then stop the interface */
 	rtnl_lock();
 	efx->state = STATE_FINI;
@@ -2126,8 +2164,15 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,

 	EFX_LOG(efx, "initialisation successful\n");

+	/* Register with driverlink layer */
+	rc = efx_dl_register_nic(efx);
+	if (rc)
+		goto fail6;
+
 	return 0;

+ fail6:
+	efx_unregister_netdev(efx);
  fail5:
 	efx_pci_remove_main(efx);
  fail4:
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 630406e..959e06a 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -36,14 +36,23 @@

 /**
  * struct falcon_nic_data - Falcon NIC state
+ * @tx_dc_entries: Number of entries in each TX queue descriptor cache
+ * @rx_dc_entries: Number of entries in each RX queue descriptor cache
+ * @tx_dc_base: Base address in SRAM of TX queue descriptor caches
+ * @rx_dc_base: Base address in SRAM of RX queue descriptor caches
  * @next_buffer_table: First available buffer table id
  * @pci_dev2: The secondary PCI device if present
  * @i2c_data: Operations and state for I2C bit-bashing algorithm
  */
 struct falcon_nic_data {
+	unsigned tx_dc_entries;
+	unsigned rx_dc_entries;
+	unsigned tx_dc_base;
+	unsigned rx_dc_base;
 	unsigned next_buffer_table;
 	struct pci_dev *pci_dev2;
 	struct i2c_algo_bit_data i2c_data;
+	struct efx_dl_falcon_resources resources;
 };

 /**************************************************************************
@@ -1120,10 +1129,12 @@ static void falcon_handle_driver_event(struct efx_channel
*channel,
 	case TX_DESCQ_FLS_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d TXQ %d flushed\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case RX_DESCQ_FLS_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RXQ %d flushed\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case EVQ_INIT_DONE_EV_DECODE:
 		EFX_LOG(efx, "channel %d EVQ %d initialised\n",
@@ -1132,14 +1143,17 @@ static void falcon_handle_driver_event(struct efx_channel
*channel,
 	case SRM_UPD_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d SRAM update done\n",
 			  channel->channel);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case WAKE_UP_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case TIMER_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RX queue %d timer expired\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case RX_RECOVERY_EV_DECODE:
 		EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. "
@@ -1164,6 +1178,7 @@ static void falcon_handle_driver_event(struct efx_channel *channel,
 		EFX_TRACE(efx, "channel %d unknown driver event code %d "
 			  "data %04x\n", channel->channel, ev_sub_code,
 			  ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	}
 }
@@ -2376,6 +2391,70 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
 	return rc;
 }

+/* Looks at available SRAM resources and silicon revision, and works out
+ * how many queues we can support, and where things like descriptor caches
+ * should live. */
+static int falcon_dimension_resources(struct efx_nic *efx)
+{
+	unsigned buffer_entry_bytes, internal_dcs_entries;
+	struct falcon_nic_data *nic_data = efx->nic_data;
+	struct efx_dl_falcon_resources *res = &nic_data->resources;
+
+	/* Fill out the driverlink resource list */
+	res->hdr.type = EFX_DL_FALCON_RESOURCES;
+	res->biu_lock = &efx->biu_lock;
+	efx->dl_info = &res->hdr;
+	nic_data->tx_dc_entries = 16;
+
+	/* Set the RX descriptor cache size.  Values 16, 32 and 64 are
+	 * supported (8 won't work).  Bigger is better, especially on B
+	 * silicon.
+	 */
+	nic_data->rx_dc_entries = 64;
+
+	/* NB. The minimum values get increased as this driver initialises
+	 * its resources, so this should prevent any overlap.
+	 */
+	switch (falcon_rev(efx)) {
+	case FALCON_REV_A1:
+		res->rxq_min = 16;
+		res->txq_min = 16;
+		res->evq_int_min = 4;
+		res->evq_int_lim = 5;
+		res->evq_timer_min = 5;
+		res->evq_timer_lim = 4096;
+		internal_dcs_entries = 8192;
+		break;
+	case FALCON_REV_B0:
+	default:
+		res->rxq_min = 0;
+		res->txq_min = 0;
+		res->evq_int_min = 0;
+		res->evq_int_lim = 64;
+		res->evq_timer_min = 64;
+		res->evq_timer_lim = 4096;
+		internal_dcs_entries = 4096;
+		break;
+	}
+
+	buffer_entry_bytes = 8;
+
+	res->rxq_lim = internal_dcs_entries / nic_data->rx_dc_entries;
+	res->txq_lim = internal_dcs_entries / nic_data->tx_dc_entries;
+	/* Internal SRAM only for now */
+	res->buffer_table_lim = 8192;
+	nic_data->tx_dc_base = 0x130000;
+	nic_data->rx_dc_base = 0x100000;
+
+	if (FALCON_IS_DUAL_FUNC(efx))
+		res->flags |= EFX_DL_FALCON_DUAL_FUNC;
+
+	if (EFX_INT_MODE_USE_MSI(efx))
+		res->flags |= EFX_DL_FALCON_USE_MSI;
+
+	return 0;
+}
+
 /* Probe the NIC variant (revision, ASIC vs FPGA, function count, port
  * count, port speed).  Set workaround and feature flags accordingly.
  */
@@ -2477,6 +2556,10 @@ int falcon_probe_nic(struct efx_nic *efx)
 	if (rc)
 		goto fail5;

+	rc = falcon_dimension_resources(efx);
+	if (rc)
+		goto fail6;
+
 	/* Initialise I2C adapter */
  	efx->i2c_adap.owner = THIS_MODULE;
  	efx->i2c_adap.class = I2C_CLASS_HWMON;
@@ -2487,10 +2570,12 @@ int falcon_probe_nic(struct efx_nic *efx)
 	strcpy(efx->i2c_adap.name, "SFC4000 GPIO");
 	rc = i2c_bit_add_bus(&efx->i2c_adap);
 	if (rc)
-		goto fail5;
+		goto fail6;

 	return 0;

+ fail6:
+	efx->dl_info = NULL;
  fail5:
 	falcon_free_buffer(efx, &efx->irq_status);
  fail4:
@@ -2681,6 +2766,7 @@ void falcon_remove_nic(struct efx_nic *efx)
 	/* Tear down the private nic state */
 	kfree(efx->nic_data);
 	efx->nic_data = NULL;
+	efx->dl_info = NULL;
 }

 void falcon_update_nic_stats(struct efx_nic *efx)
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index d803b86..2a84468 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -30,6 +30,8 @@

 #include "enum.h"
 #include "bitfield.h"
+#include "driverlink_api.h"
+#include "driverlink.h"

 #define EFX_MAX_LRO_DESCRIPTORS 8
 #define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS
@@ -674,6 +676,12 @@ union efx_multicast_hash {
  * @loopback_mode: Loopback status
  * @loopback_modes: Supported loopback mode bitmask
  * @loopback_selftest: Offline self-test private state
+ * @silicon_rev: Silicon revision description for driverlink
+ * @dl_info: Linked list of hardware parameters exposed through driverlink
+ * @dl_node: Driverlink port list
+ * @dl_device_list: Driverlink device list
+ * @dl_cb: Driverlink callbacks table
+ * @dl_cb_dev: Driverlink callback owner devices
  *
  * The @priv field of the corresponding &struct net_device points to
  * this.
@@ -749,6 +757,13 @@ struct efx_nic {
 	unsigned int loopback_modes;

 	void *loopback_selftest;
+
+	const char *silicon_rev;
+	struct efx_dl_device_info *dl_info;
+	struct list_head dl_node;
+	struct list_head dl_device_list;
+	struct efx_dl_callbacks dl_cb;
+	struct efx_dl_cb_devices dl_cb_dev;
 };

 static inline int efx_dev_registered(struct efx_nic *efx)
-- 
1.5.3.7


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists