1134 lines
36 KiB
Plaintext
1134 lines
36 KiB
Plaintext
From: David Riddoch <driddoch@solarflare.com>
|
|
commit d96c061bfd1839e34e136de0555564520acc97af
|
|
Author: Steve Hodgson <shodgson@solarflare.com>
|
|
Date: Mon Jul 14 15:38:47 2008 +0100
|
|
|
|
Subject: sfc: Driverlink API for exporting hardware features to client drivers
|
|
References: FATE#303479
|
|
Patch-mainline: n/a
|
|
Acked-by: jbeulich@novell.com
|
|
|
|
--- head-2009-11-06.orig/drivers/net/sfc/Makefile 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/Makefile 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -1,6 +1,7 @@
|
|
sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.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
|
|
sfc-$(CONFIG_SFC_MTD) += mtd.o
|
|
|
|
obj-$(CONFIG_SFC) += sfc.o
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ head-2009-11-06/drivers/net/sfc/driverlink.c 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -0,0 +1,367 @@
|
|
+/****************************************************************************
|
|
+ * 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);
|
|
+
|
|
+void efx_dl_reset_unlock(void)
|
|
+{
|
|
+ mutex_unlock(&efx_driverlink_lock);
|
|
+}
|
|
+
|
|
+/* 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);
|
|
+}
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ head-2009-11-06/drivers/net/sfc/driverlink.h 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -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 */
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ head-2009-11-06/drivers/net/sfc/driverlink_api.h 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -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 */
|
|
--- head-2009-11-06.orig/drivers/net/sfc/efx.c 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/efx.c 2009-10-12 13:40:25.000000000 +0200
|
|
@@ -1487,12 +1487,21 @@ static int efx_change_mtu(struct net_dev
|
|
|
|
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);
|
|
net_dev->mtu = new_mtu;
|
|
efx_init_channels(efx);
|
|
|
|
+ /* Notify driverlink client of new MTU */
|
|
+ EFX_DL_CALLBACK(efx, mtu_changed, new_mtu);
|
|
+
|
|
+ out:
|
|
efx_start_all(efx);
|
|
return rc;
|
|
}
|
|
@@ -1680,6 +1689,23 @@ static void efx_unregister_netdev(struct
|
|
* 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();
|
|
+}
|
|
|
|
/* Tears down the entire software state and most of the hardware state
|
|
* before reset. */
|
|
@@ -1760,8 +1786,8 @@ static int efx_reset(struct efx_nic *efx
|
|
enum reset_type method = efx->reset_pending;
|
|
int rc = 0;
|
|
|
|
- /* 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 */
|
|
@@ -1807,6 +1833,7 @@ out_disable:
|
|
}
|
|
|
|
out_unlock:
|
|
+ efx_dl_reset_resume(efx, 1);
|
|
rtnl_unlock();
|
|
return rc;
|
|
}
|
|
@@ -1951,6 +1978,9 @@ static int efx_init_struct(struct efx_ni
|
|
efx->mac_op = &efx_dummy_mac_operations;
|
|
efx->phy_op = &efx_dummy_phy_operations;
|
|
efx->mdio.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->phy_work, efx_phy_work);
|
|
INIT_WORK(&efx->mac_work, efx_mac_work);
|
|
atomic_set(&efx->netif_stop_count, 1);
|
|
@@ -2054,6 +2084,7 @@ static void efx_pci_remove(struct pci_de
|
|
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();
|
|
@@ -2230,9 +2261,16 @@ static int __devinit efx_pci_probe(struc
|
|
if (rc)
|
|
goto fail5;
|
|
|
|
+ /* Register with driverlink layer */
|
|
+ rc = efx_dl_register_nic(efx);
|
|
+ if (rc)
|
|
+ goto fail6;
|
|
+
|
|
EFX_LOG(efx, "initialisation successful\n");
|
|
return 0;
|
|
|
|
+ fail6:
|
|
+ efx_unregister_netdev(efx);
|
|
fail5:
|
|
efx_pci_remove_main(efx);
|
|
fail4:
|
|
--- head-2009-11-06.orig/drivers/net/sfc/falcon.c 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/falcon.c 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -36,14 +36,14 @@
|
|
|
|
/**
|
|
* 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
|
|
* @int_error_count: Number of internal errors seen recently
|
|
* @int_error_expire: Time at which error count will be expired
|
|
*/
|
|
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;
|
|
|
|
@@ -336,8 +336,8 @@ static int falcon_alloc_special_buffer(s
|
|
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 %llx)\n", buffer->index,
|
|
@@ -960,10 +960,12 @@ static void falcon_handle_driver_event(s
|
|
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",
|
|
@@ -972,14 +974,17 @@ static void falcon_handle_driver_event(s
|
|
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. "
|
|
@@ -1004,6 +1009,7 @@ static void falcon_handle_driver_event(s
|
|
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;
|
|
}
|
|
}
|
|
@@ -2744,6 +2750,59 @@ static int falcon_probe_nvconfig(struct
|
|
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.
|
|
*/
|
|
@@ -2771,9 +2830,11 @@ static int falcon_probe_nic_variant(stru
|
|
EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
|
|
return -ENODEV;
|
|
}
|
|
+ efx->silicon_rev = "falcon/a1";
|
|
break;
|
|
|
|
case FALCON_REV_B0:
|
|
+ efx->silicon_rev = "falcon/b0";
|
|
break;
|
|
|
|
default:
|
|
@@ -2883,6 +2944,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;
|
|
nic_data->i2c_data = falcon_i2c_bit_operations;
|
|
@@ -2892,10 +2957,12 @@ int falcon_probe_nic(struct efx_nic *efx
|
|
strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name));
|
|
rc = i2c_bit_add_bus(&efx->i2c_adap);
|
|
if (rc)
|
|
- goto fail5;
|
|
+ goto fail6;
|
|
|
|
return 0;
|
|
|
|
+ fail6:
|
|
+ efx->dl_info = NULL;
|
|
fail5:
|
|
falcon_remove_spi_devices(efx);
|
|
falcon_free_buffer(efx, &efx->irq_status);
|
|
@@ -3083,6 +3150,7 @@ void falcon_remove_nic(struct efx_nic *e
|
|
/* 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)
|
|
--- head-2009-11-06.orig/drivers/net/sfc/net_driver.h 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/net_driver.h 2009-07-28 10:04:25.000000000 +0200
|
|
@@ -29,6 +29,8 @@
|
|
|
|
#include "enum.h"
|
|
#include "bitfield.h"
|
|
+#include "driverlink_api.h"
|
|
+#include "driverlink.h"
|
|
|
|
/**************************************************************************
|
|
*
|
|
@@ -754,6 +756,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.
|
|
@@ -844,6 +852,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)
|
|
--- head-2009-11-06.orig/drivers/net/sfc/rx.c 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/rx.c 2009-11-06 10:32:03.000000000 +0100
|
|
@@ -447,7 +447,21 @@ static void efx_rx_packet_lro(struct efx
|
|
struct efx_rx_buffer *rx_buf,
|
|
bool checksummed)
|
|
{
|
|
+ struct efx_nic *efx = channel->efx;
|
|
struct napi_struct *napi = &channel->napi_str;
|
|
+ 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) {
|
|
@@ -550,6 +564,7 @@ void __efx_rx_packet(struct efx_channel
|
|
struct efx_rx_buffer *rx_buf, bool checksummed)
|
|
{
|
|
struct efx_nic *efx = channel->efx;
|
|
+ enum efx_veto veto;
|
|
struct sk_buff *skb;
|
|
|
|
/* If we're in loopback test, then pass the packet directly to the
|
|
@@ -561,6 +576,16 @@ void __efx_rx_packet(struct efx_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;
|
|
+ }
|
|
+
|
|
if (rx_buf->skb) {
|
|
prefetch(skb_shinfo(rx_buf->skb));
|
|
|
|
--- head-2009-11-06.orig/drivers/net/sfc/tx.c 2009-11-06 10:29:51.000000000 +0100
|
|
+++ head-2009-11-06/drivers/net/sfc/tx.c 2009-10-12 13:40:32.000000000 +0200
|
|
@@ -374,6 +374,7 @@ netdev_tx_t efx_hard_start_xmit(struct s
|
|
{
|
|
struct efx_nic *efx = netdev_priv(net_dev);
|
|
struct efx_tx_queue *tx_queue;
|
|
+ enum efx_veto veto;
|
|
|
|
if (unlikely(efx->port_inhibited))
|
|
return NETDEV_TX_BUSY;
|
|
@@ -383,6 +384,17 @@ netdev_tx_t efx_hard_start_xmit(struct s
|
|
else
|
|
tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM];
|
|
|
|
+ /* 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);
|
|
}
|
|
|