From: David Riddoch commit d96c061bfd1839e34e136de0555564520acc97af Author: Steve Hodgson Date: Mon Jul 14 15:38:47 2008 +0100 Subject: sfc: Driverlink API for exporting hardware features to client drivers References: FATE#303479 Acked-by: jbeulich@novell.com Index: head-2008-08-18/drivers/net/sfc/Makefile =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/Makefile 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/Makefile 2008-08-18 10:16:46.000000000 +0200 @@ -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 Index: head-2008-08-18/drivers/net/sfc/driverlink.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-08-18/drivers/net/sfc/driverlink.c 2008-08-18 10:16:46.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 +#include +#include +#include +#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); +} Index: head-2008-08-18/drivers/net/sfc/driverlink.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-08-18/drivers/net/sfc/driverlink.h 2008-08-18 10:16:46.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 */ Index: head-2008-08-18/drivers/net/sfc/driverlink_api.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ head-2008-08-18/drivers/net/sfc/driverlink_api.h 2008-08-18 10:16:46.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 + +/* 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 */ Index: head-2008-08-18/drivers/net/sfc/efx.c =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/efx.c 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/efx.c 2008-08-18 10:16:46.000000000 +0200 @@ -1427,6 +1427,11 @@ 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); @@ -1435,6 +1440,10 @@ static int efx_change_mtu(struct net_dev 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; @@ -1587,6 +1596,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(); +} /* The final hardware and software finalisation before reset. */ static int efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) @@ -1649,8 +1675,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 */ @@ -1717,6 +1743,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; @@ -1729,6 +1756,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); @@ -1871,6 +1899,9 @@ static int efx_init_struct(struct efx_ni 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); @@ -1990,6 +2021,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(); @@ -2157,8 +2189,15 @@ static int __devinit efx_pci_probe(struc 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: Index: head-2008-08-18/drivers/net/sfc/falcon.c =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/falcon.c 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/falcon.c 2008-08-18 10:16:46.000000000 +0200 @@ -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(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 %lx)\n", buffer->index, @@ -1115,10 +1115,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", @@ -1127,14 +1129,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. " @@ -1159,6 +1164,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; } } @@ -2371,6 +2377,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. */ @@ -2403,10 +2462,12 @@ static int falcon_probe_nic_variant(stru 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: @@ -2472,6 +2533,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; @@ -2481,10 +2546,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_free_buffer(efx, &efx->irq_status); fail4: @@ -2675,6 +2742,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) Index: head-2008-08-18/drivers/net/sfc/net_driver.h =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/net_driver.h 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/net_driver.h 2008-08-18 10:16:46.000000000 +0200 @@ -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 @@ -676,6 +678,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. @@ -752,6 +760,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) Index: head-2008-08-18/drivers/net/sfc/rx.c =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/rx.c 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/rx.c 2008-08-18 10:16:46.000000000 +0200 @@ -549,8 +549,22 @@ static inline void efx_rx_packet__check_ 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 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 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); Index: head-2008-08-18/drivers/net/sfc/tx.c =================================================================== --- head-2008-08-18.orig/drivers/net/sfc/tx.c 2008-08-18 10:16:43.000000000 +0200 +++ head-2008-08-18/drivers/net/sfc/tx.c 2008-08-18 10:16:46.000000000 +0200 @@ -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)