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  linux-hardening  linux-cve-announce  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:	Mon, 14 Jul 2008 15:12:22 +0100
From:	Steve Hodgson <shodgson@...arflare.com>
To:	Jeff Garzik <jgarzik@...ox.com>
Cc:	netdev@...r.kernel.org, linux-net-drivers@...arflare.com,
	michael.meeks@...ell.com, Jan Beulich <jbeulich@...ell.com>
Subject: [PATCH] sfc: Driverlink API for exporting hardware features to client drivers

This patch adds support to the sfc driver for advertising and exporting
hardware resources (flash, eeprom, and VNIC interfaces) on the SFC4000
range of controllers.

The previous thread was:
  http://marc.info/?t=121338899200003&r=1&w=2

The following drivers make use of this API:

 - The sfc_mtd driver, which exports the on-board flash and eeprom as mtd
   devices, to support programming the PXE image. We plan on submitting
   this driver after this patch is merged.
 - The sfc_netback driver in the latest Xen release [1].
 - The sfc_resource and onload drivers in the open-source (gpl) OpenOnload
   user-level network stack [2].

[1] http://lists.xensource.com/archives/html/xen-devel/2008-02/msg00507.html
[2] http://www.openonload.org/

Signed-off-by: Steve Hodgson <shodgson@...arflare.com>
---
 drivers/net/sfc/Makefile         |    3 +-
 drivers/net/sfc/driverlink.c     |  362 ++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/driverlink.h     |   43 +++++
 drivers/net/sfc/driverlink_api.h |  303 +++++++++++++++++++++++++++++++
 drivers/net/sfc/efx.c            |   41 ++++-
 drivers/net/sfc/falcon.c         |   76 ++++++++-
 drivers/net/sfc/net_driver.h     |   15 ++
 drivers/net/sfc/rx.c             |   25 +++
 drivers/net/sfc/tx.c             |   16 ++-
 9 files changed, 876 insertions(+), 8 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..584bbf2 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,5 +1,4 @@
 sfc-y			+= efx.o falcon.o tx.o rx.o falcon_xmac.o \
-			   selftest.o ethtool.o xfp_phy.o \
+			   selftest.o ethtool.o driverlink.o xfp_phy.o \
 			   mdio_10g.o tenxpress.o boards.o sfe4001.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..468112a
--- /dev/null
+++ b/drivers/net/sfc/driverlink.c
@@ -0,0 +1,362 @@
+/****************************************************************************
+ * 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"
+
+/* Protects @efx_driverlink_lock and @efx_driver_list */
+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
+ * @efx_dev: driverlink device handle exported to consumers
+ * @efx: efx_nic backing the driverlink device
+ * @port_node: per-device list head
+ * @driver_node: per-driver list head
+ */
+struct efx_dl_handle {
+	struct efx_dl_device efx_dev;
+	struct efx_nic *efx;
+	struct list_head port_node;
+	struct list_head driver_node;
+};
+
+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, and call the driver's remove() callback if
+ * present. The caller must hold @efx_driverlink_lock. */
+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);
+
+	if (efx_dev->driver->remove)
+		efx_dev->driver->remove(efx_dev);
+
+	list_del(&efx_handle->driver_node);
+	list_del(&efx_handle->port_node);
+
+	kfree(efx_handle);
+}
+
+/* Attempt to probe the given device with the driver, creating a
+ * new &struct efx_dl_device. If the probe routine returns an error,
+ * then the &struct 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;
+
+	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);
+
+	rc = driver->probe(efx_dev, efx->net_dev,
+			   efx->dl_info, efx->silicon_rev);
+	if (rc)
+		goto fail;
+
+	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);
+}
+
+/* Unregister a driver from the driverlink layer, calling the
+ * driver's remove() callback for every attached device */
+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);
+
+	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);
+
+/* Register a new driver with the driverlink layer. The driver's
+ * probe routine will be called for every attached nic. */
+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);
+
+	INIT_LIST_HEAD(&driver->node);
+	INIT_LIST_HEAD(&driver->device_list);
+
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	list_add_tail(&driver->node, &efx_driver_list);
+	list_for_each_entry(efx, &efx_port_list, dl_node)
+		efx_dl_try_add_device(efx, driver);
+
+	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;
+
+	mutex_lock(&efx_driverlink_lock);
+
+	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);
+
+	list_del(&efx->dl_node);
+
+	mutex_unlock(&efx_driverlink_lock);
+}
+
+int efx_dl_register_nic(struct efx_nic *efx)
+{
+	struct efx_dl_driver *driver;
+	int rc;
+
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	list_add_tail(&efx->dl_node, &efx_port_list);
+	list_for_each_entry(driver, &efx_driver_list, node)
+		efx_dl_try_add_device(efx, driver);
+
+	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.
+ */
+static enum efx_veto efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev,
+						  struct sk_buff *skb)
+{
+	return EFX_ALLOW_PACKET;
+}
+
+static enum efx_veto efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev,
+						  const char *pkt_buf, int len)
+{
+	return EFX_ALLOW_PACKET;
+}
+
+static int efx_dummy_request_mtu_callback(struct efx_dl_device *efx_dev,
+					  int new_mtu)
+{
+	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,
+	.request_mtu	= efx_dummy_request_mtu_callback,
+	.mtu_changed	= efx_dummy_mtu_changed_callback,
+	.event		= efx_dummy_event_callback,
+};
+
+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;
+
+	efx_suspend(efx);
+
+	EFX_INFO(efx, "removing callback hooks into %s driver\n",
+		 efx_dev->driver->name);
+
+	if (callbacks->tx_packet) {
+		BUG_ON(efx->dl_cb_dev.tx_packet != efx_dev);
+		efx->dl_cb.tx_packet = efx_default_callbacks.tx_packet;
+		efx->dl_cb_dev.tx_packet = NULL;
+	}
+	if (callbacks->rx_packet) {
+		BUG_ON(efx->dl_cb_dev.rx_packet != efx_dev);
+		efx->dl_cb.rx_packet = efx_default_callbacks.rx_packet;
+		efx->dl_cb_dev.rx_packet = NULL;
+	}
+	if (callbacks->request_mtu) {
+		BUG_ON(efx->dl_cb_dev.request_mtu != efx_dev);
+		efx->dl_cb.request_mtu = efx_default_callbacks.request_mtu;
+		efx->dl_cb_dev.request_mtu = NULL;
+	}
+	if (callbacks->mtu_changed) {
+		BUG_ON(efx->dl_cb_dev.mtu_changed != efx_dev);
+		efx->dl_cb.mtu_changed = efx_default_callbacks.mtu_changed;
+		efx->dl_cb_dev.mtu_changed = NULL;
+	}
+	if (callbacks->event) {
+		BUG_ON(efx->dl_cb_dev.event != efx_dev);
+		efx->dl_cb.event = efx_default_callbacks.event;
+		efx->dl_cb_dev.event = NULL;
+	}
+
+	efx_resume(efx);
+}
+EXPORT_SYMBOL(efx_dl_unregister_callbacks);
+
+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;
+
+	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->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 the requested callbacks, leaving any NULL members
+	 * referencing the members of @efx_default_callbacks */
+	if (callbacks->tx_packet) {
+		efx->dl_cb.tx_packet = callbacks->tx_packet;
+		efx->dl_cb_dev.tx_packet = efx_dev;
+	}
+	if (callbacks->rx_packet) {
+		efx->dl_cb.rx_packet = callbacks->rx_packet;
+		efx->dl_cb_dev.rx_packet = efx_dev;
+	}
+	if (callbacks->request_mtu) {
+		efx->dl_cb.request_mtu = callbacks->request_mtu;
+		efx->dl_cb_dev.request_mtu = efx_dev;
+	}
+	if (callbacks->mtu_changed) {
+		efx->dl_cb.mtu_changed = callbacks->mtu_changed;
+		efx->dl_cb_dev.mtu_changed = efx_dev;
+	}
+	if (callbacks->event) {
+		efx->dl_cb.event = callbacks->event;
+		efx->dl_cb_dev.event = efx_dev;
+	}
+
+ out:
+	efx_resume(efx);
+
+	return rc;
+}
+EXPORT_SYMBOL(efx_dl_register_callbacks);
+
+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);
+
+/* Suspend ready for reset, serialising against all the driverlink interfacse
+ * and calling the suspend() callback of every registered driver */
+void efx_dl_reset_suspend(struct efx_nic *efx)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	mutex_lock(&efx_driverlink_lock);
+
+	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, calling the resume() callback of every registered
+ * driver, and releasing @Efx_driverlink_lock acquired in
+ * efx_dl_reset_resume() */
+void efx_dl_reset_resume(struct efx_nic *efx, int ok)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	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);
+	}
+
+	mutex_unlock(&efx_driverlink_lock);
+}
diff --git a/drivers/net/sfc/driverlink.h b/drivers/net/sfc/driverlink.h
new file mode 100644
index 0000000..ba464ca
--- /dev/null
+++ b/drivers/net/sfc/driverlink.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ * 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 callback devices
+ *
+ * A list of the devices that own each callback. The partner to
+ * struct efx_dl_callbacks.
+ */
+struct efx_dl_cb_devices {
+	struct efx_dl_device *tx_packet;
+	struct efx_dl_device *rx_packet;
+	struct efx_dl_device *request_mtu;
+	struct efx_dl_device *mtu_changed;
+	struct efx_dl_device *event;
+};
+
+extern struct efx_dl_callbacks efx_default_callbacks;
+
+#define EFX_DL_CALLBACK(_port, _name, ...)				\
+	(_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__)
+
+extern int efx_dl_register_nic(struct efx_nic *efx);
+extern void efx_dl_unregister_nic(struct efx_nic *efx);
+
+/* Suspend and resume client drivers over a hardware reset */
+extern void efx_dl_reset_suspend(struct efx_nic *efx);
+extern void efx_dl_reset_resume(struct efx_nic *efx, int ok);
+
+#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..4a0f65c
--- /dev/null
+++ b/drivers/net/sfc/driverlink_api.h
@@ -0,0 +1,303 @@
+/****************************************************************************
+ * 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>
+
+/* Forward declarations */
+struct pci_dev;
+struct net_device;
+struct sk_buff;
+struct efx_dl_device;
+struct efx_dl_device_info;
+
+/* An extra safeguard in addition to symbol versioning */
+#define EFX_DRIVERLINK_API_VERSION 2
+
+/**
+ * struct efx_dl_driver - An Efx driverlink device driver
+ *
+ * A driverlink client defines and initializes as many instances of
+ * efx_dl_driver as required, registering each one with
+ * efx_dl_register_driver().
+ *
+ * @name: Name of the driver
+ * @probe: Called when device added
+ *	The client should use the @def_info linked list and @silicon_rev
+ *	to determine if they wish to attach to this device.
+ *	Context: process, driverlink semaphore held
+ * @remove: Called when device removed
+ *	The client must ensure the finish all operations with this
+ *	device before returning from this method.
+ *	Context: process, driverlink semaphore held
+ * @reset_suspend: Called before device is reset
+ *	Called immediately before a hardware reset. The client must stop all
+ *	hardware processing before returning from this method. Callbacks will
+ *	be inactive when this method is called.
+ *	Context: process, driverlink semaphore held. rtnl_lock may be held
+ * @reset_resume: Called after device is reset
+ *	Called after a hardware reset. If @ok is true, the client should
+ *	state and resume normal operations. If @ok is false, the client should
+ *	abandon use of the hardware resources. remove() will still be called.
+ *	Context: process, driverlink semaphore held. rtnl_lock may be held
+ */
+struct efx_dl_driver {
+	const char *name;
+
+	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);
+	void (*remove) (struct efx_dl_device *efx_dev);
+	void (*reset_suspend) (struct efx_dl_device *efx_dev);
+	void (*reset_resume) (struct efx_dl_device *efx_dev, int ok);
+
+/* private: */
+	struct list_head node;
+	struct list_head device_list;
+};
+
+/**
+ * enum efx_dl_device_info_type - Device information identifier.
+ *
+ * Used to identify each item in the &struct efx_dl_device_info linked list
+ * provided to each driverlink client in the probe() @dev_info member.
+ *
+ * @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
+ */
+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 current Falcon device.
+ *
+ * @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
+ * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts.
+ *	Falcon supports traditional legacy interrupts and MSI/MSI-X
+ *	interrupts. The choice is made at run time by the sfc driver, and
+ *	notified to the clients by this enumeration
+ */
+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;
+	unsigned buffer_table_lim;
+	unsigned evq_timer_min;
+	unsigned evq_timer_lim;
+	unsigned evq_int_min;
+	unsigned evq_int_lim;
+	unsigned rxq_min;
+	unsigned rxq_lim;
+	unsigned txq_min;
+	unsigned txq_lim;
+	enum efx_dl_falcon_resource_flags flags;
+};
+
+/**
+ * struct efx_dl_device - An Efx driverlink device.
+ *
+ * @pci_dev: PCI device used by the sfc driver.
+ * @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
+ *
+ * This is a tighly controlled set of simple callbacks, that are attached
+ * to the sfc driver via efx_dl_register_callbacks().  They export just enough
+ * state to allow clients to make use of the available hardware resources.
+ *
+ * For efficiency, only one client can hook each callback. Since these
+ * callbacks are called on packet transmit and reception paths, and the
+ * sfc driver may have multiple tx and rx queues per port, clients should
+ * avoid acquiring locks or allocating memory.
+ *
+ * @tx_packet: Called when packet is about to be transmitted
+ *	Called for every packet about to be transmitted, providing means
+ *	for the client to snoop traffic, and veto transmission by returning
+ *	%EFX_VETO_PACKET (the sfc driver will subsequently free the skb).
+ *	Context: tasklet, netif_tx_lock held
+ * @rx_packet: Called when packet is received
+ *	Called for every received packet (after LRO), allowing the client
+ *	to snoop every received packet (on every rx queue), and veto
+ *	reception by returning %EFX_VETO_PACKET.
+ *	Context: tasklet
+ * @request_mtu: Called to request MTU change.
+ *	Called whenever the user requests the net_dev mtu to be changed.
+ *	If the client returns an error, the mtu change is aborted. The sfc
+ *	driver guarantees that no other callbacks are running.
+ *	Context: process, rtnl_lock held.
+ * @mtu_changed: Called when MTU has been changed.
+ *	Called after the mtu has been successfully changed, always after
+ *	a previous call to request_mtu(). The sfc driver guarantees that no
+ *	other callbacks are running.
+ *	Context: process, rtnl_lock held.
+ * @event: Called when a hardware NIC event is not understood by the sfc driver.
+ *	Context: tasklet.
+ */
+struct efx_dl_callbacks {
+	enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev,
+				    struct sk_buff *skb);
+	enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev,
+				    const char *pkt_hdr, int pkt_len);
+	int (*request_mtu) (struct efx_dl_device *efx_dev, int new_mtu);
+	void (*mtu_changed) (struct efx_dl_device *efx_dev, int mtu);
+	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)
+
+/* Exported driverlink api used to register and unregister the client driver
+ * and any callbacks [only one per port allowed], and to allow a client driver
+ * to request reset to recover from an error condition.
+ *
+ * All of these functions acquire the driverlink semaphore, so must not be
+ * called from an efx_dl_driver or efx_dl_callbacks member, and must be called
+ * from process context.
+ */
+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);
+
+/* Schedule a reset without grabbing any locks */
+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:
+ *	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:
+ *	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)
+ *		....
+ */
+#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..bcd08f2 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1410,6 +1410,11 @@ 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);
+	if (rc)
+		goto out;
+
 	EFX_LOG(efx, "changing MTU to %d\n", new_mtu);
 
 	efx_fini_channels(efx);
@@ -1418,6 +1423,10 @@ 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);
+
+ out:
 	efx_start_all(efx);
 	return rc;
 
@@ -1570,6 +1579,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,8 +1658,8 @@ static int efx_reset(struct efx_nic *efx)
 	enum reset_type method = efx->reset_pending;
 	int rc;
 
-	/* Serialise with kernel interfaces */
 	rtnl_lock();
+	efx_dl_reset_suspend(efx);
 
 	/* If we're not RUNNING then don't reset. Leave the reset_pending
 	 * flag set so that efx_pci_probe_main will be retried */
@@ -1700,6 +1726,7 @@ static int efx_reset(struct efx_nic *efx)
 	efx_start_all(efx);
 
  unlock_rtnl:
+	efx_dl_reset_resume(efx, 1);
 	rtnl_unlock();
 	return 0;
 
@@ -1712,6 +1739,7 @@ static int efx_reset(struct efx_nic *efx)
 	efx->state = STATE_DISABLED;
 
 	mutex_unlock(&efx->mac_lock);
+	efx_dl_reset_resume(efx, 0);
 	rtnl_unlock();
 	efx_unregister_netdev(efx);
 	efx_fini_port(efx);
@@ -1854,6 +1882,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);
 
@@ -1959,6 +1990,7 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 	efx = pci_get_drvdata(pci_dev);
 	if (!efx)
 		return;
+	efx_dl_unregister_nic(efx);
 
 	/* Mark the NIC as fini, then stop the interface */
 	rtnl_lock();
@@ -2126,8 +2158,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 9138ee5..b982f43 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -36,12 +36,12 @@
 
 /**
  * struct falcon_nic_data - Falcon NIC state
- * @next_buffer_table: First available buffer table id
+ * @resources: Resource information for driverlink client
  * @pci_dev2: The secondary PCI device if present
  * @i2c_data: Operations and state for I2C bit-bashing algorithm
  */
 struct falcon_nic_data {
-	unsigned next_buffer_table;
+	struct efx_dl_falcon_resources resources;
 	struct pci_dev *pci_dev2;
 	struct i2c_algo_bit_data i2c_data;
 };
@@ -322,8 +322,8 @@ static int falcon_alloc_special_buffer(struct efx_nic *efx,
 	memset(buffer->addr, 0xff, len);
 
 	/* Select new buffer ID */
-	buffer->index = nic_data->next_buffer_table;
-	nic_data->next_buffer_table += buffer->entries;
+	buffer->index = nic_data->resources.buffer_table_min;
+	nic_data->resources.buffer_table_min += buffer->entries;
 
 	EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x "
 		"(virt %p phys %lx)\n", buffer->index,
@@ -1115,10 +1115,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",
@@ -1127,14 +1129,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. "
@@ -1159,6 +1164,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;
 	}
 }
@@ -2371,6 +2377,59 @@ 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 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;
+
+	/* 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;
+	}
+
+	/* Internal SRAM only for now */
+	res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES;
+	res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES;
+	res->buffer_table_lim = 8192;
+
+	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.
  */
@@ -2403,10 +2462,12 @@ static int falcon_probe_nic_variant(struct efx_nic *efx)
 			EFX_ERR(efx, "1G mode not supported\n");
 			return -ENODEV;
 		}
+		efx->silicon_rev = "falcon/a1";
 		break;
 	}
 
 	case FALCON_REV_B0:
+		efx->silicon_rev = "falcon/b0";
 		break;
 
 	default:
@@ -2483,8 +2544,14 @@ int falcon_probe_nic(struct efx_nic *efx)
 	if (rc)
 		goto fail5;
 
+	rc = falcon_dimension_resources(efx);
+	if (rc)
+		goto fail6;
+
 	return 0;
 
+ fail6:
+	efx->dl_info = NULL;
  fail5:
 	falcon_free_buffer(efx, &efx->irq_status);
  fail4:
@@ -2675,6 +2742,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)
diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c
index 601b001..eb31826 100644
--- a/drivers/net/sfc/rx.c
+++ b/drivers/net/sfc/rx.c
@@ -549,8 +549,22 @@ static inline void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue,
 static inline void efx_rx_packet_lro(struct efx_channel *channel,
 				     struct efx_rx_buffer *rx_buf)
 {
+	struct efx_nic *efx = channel->efx;
 	struct net_lro_mgr *lro_mgr = &channel->lro_mgr;
 	void *priv = channel;
+	enum efx_veto veto;
+
+	/* It would be faster if we had access to packets at the
+	 * other side of generic LRO. Unfortunately, there isn't
+	 * an obvious interface to this, so veto packets before LRO */
+	veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len);
+	if (unlikely(veto)) {
+		EFX_TRACE(efx, "LRO RX vetoed by driverlink %s driver\n",
+			  efx->dl_cb_dev.rx_packet->driver->name);
+		/* Free the buffer now */
+		efx_free_rx_buffer(efx, rx_buf);
+		return;
+	}
 
 	/* Pass the skb/page into the LRO engine */
 	if (rx_buf->page) {
@@ -686,6 +700,7 @@ void __efx_rx_packet(struct efx_channel *channel,
 		     struct efx_rx_buffer *rx_buf, int checksummed)
 {
 	struct efx_nic *efx = channel->efx;
+	enum efx_veto veto;
 	struct sk_buff *skb;
 	int lro = efx->net_dev->features & NETIF_F_LRO;
 
@@ -723,6 +738,16 @@ void __efx_rx_packet(struct efx_channel *channel,
 		goto done;
 	}
 
+	/* Allow callback to veto the packet */
+	veto = EFX_DL_CALLBACK(efx, rx_packet, rx_buf->data, rx_buf->len);
+	if (unlikely(veto)) {
+		EFX_LOG(efx, "RX vetoed by driverlink %s driver\n",
+			efx->dl_cb_dev.rx_packet->driver->name);
+		/* Free the buffer now */
+		efx_free_rx_buffer(efx, rx_buf);
+		goto done;
+	}
+
 	/* Form an skb if required */
 	if (rx_buf->page) {
 		int hdr_len = min(rx_buf->len, EFX_SKB_HEADERS);
diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c
index 5cdd082..e32e29b 100644
--- a/drivers/net/sfc/tx.c
+++ b/drivers/net/sfc/tx.c
@@ -368,7 +368,21 @@ inline int efx_xmit(struct efx_nic *efx,
 int efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
 {
 	struct efx_nic *efx = net_dev->priv;
-	return efx_xmit(efx, &efx->tx_queue[0], skb);
+	struct efx_tx_queue *tx_queue = &efx->tx_queue[0];
+	enum efx_veto veto;
+
+	/* See if driverlink wants to veto the packet. */
+	veto = EFX_DL_CALLBACK(efx, tx_packet, skb);
+	if (unlikely(veto)) {
+		EFX_TRACE(efx, "TX queue %d packet vetoed by "
+			  "driverlink %s driver\n", tx_queue->queue,
+			  efx->dl_cb_dev.tx_packet->driver->name);
+		/* Free the skb; nothing else will do it */
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
+
+	return efx_xmit(efx, tx_queue, skb);
 }
 
 void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
-- 
1.5.5

--
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

Powered by Openwall GNU/*/Linux Powered by OpenVZ