SFC backend network accelerator source diff -r 6dcdd8348e5b drivers/xen/Kconfig --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -82,6 +82,12 @@ config XEN_NETDEV_ACCEL_SFC_UTIL config XEN_NETDEV_ACCEL_SFC_UTIL tristate default n + +config XEN_NETDEV_ACCEL_SFC_BACKEND + tristate "Network-device backend driver acceleration for Solarflare NICs" + depends on XEN_NETDEV_BACKEND + select XEN_NETDEV_ACCEL_SFC_UTIL + default m config XEN_NETDEV_LOOPBACK tristate "Network-device loopback driver" diff -r 6dcdd8348e5b drivers/xen/Makefile --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_XEN_GRANT_DEV) += gntdev/ obj-$(CONFIG_XEN_GRANT_DEV) += gntdev/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL) += sfc_netutil/ obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND) += sfc_netfront/ +obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_BACKEND) += sfc_netback/ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/Makefile --- /dev/null +++ b/drivers/xen/sfc_netback/Makefile @@ -0,0 +1,12 @@ +EXTRA_CFLAGS += -Idrivers/xen/sfc_netutil -Idrivers/xen/netback -Idrivers/net/sfc +EXTRA_CFLAGS += -D__ci_driver__ +EXTRA_CFLAGS += -DEFX_USE_KCOMPAT +EXTRA_CFLAGS += -Werror + +ifdef GCOV +EXTRA_CFLAGS += -fprofile-arcs -ftest-coverage -DEFX_GCOV +endif + +obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_BACKEND) := sfc_netback.o + +sfc_netback-objs := accel.o accel_fwd.o accel_msg.o accel_solarflare.o accel_xenbus.o accel_debugfs.o diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel.c @@ -0,0 +1,129 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include "accel.h" +#include "accel_msg_iface.h" +#include "accel_solarflare.h" + +#include + +#ifdef EFX_GCOV +#include "gcov.h" +#endif + +static int netback_accel_netdev_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = (struct net_device *)ptr; + struct netback_accel *bend; + + if ((event == NETDEV_UP) || (event == NETDEV_DOWN)) { + mutex_lock(&bend_list_mutex); + bend = bend_list; + while (bend != NULL) { + mutex_lock(&bend->bend_mutex); + /* + * This happens when the shared pages have + * been unmapped, but the bend not yet removed + * from list + */ + if (bend->shared_page == NULL) + goto next; + + if (bend->net_dev->ifindex == net_dev->ifindex) + netback_accel_set_interface_state + (bend, event == NETDEV_UP); + + next: + mutex_unlock(&bend->bend_mutex); + bend = bend->next_bend; + } + mutex_unlock(&bend_list_mutex); + } + + return NOTIFY_DONE; +} + + +static struct notifier_block netback_accel_netdev_notifier = { + .notifier_call = netback_accel_netdev_event, +}; + + +unsigned max_pages = NETBACK_ACCEL_DEFAULT_MAX_BUF_PAGES; +module_param(max_pages, int, 0666); +MODULE_PARM_DESC(max_pages, + "The number of buffer pages to enforce on each guest"); + +/* Initialise subsystems need for the accelerated fast path */ +static int __init netback_accel_init(void) +{ + int rc = 0; + +#ifdef EFX_GCOV + gcov_provider_init(THIS_MODULE); +#endif + + rc = netback_accel_init_fwd(); + + if (rc == 0) + netback_accel_debugfs_init(); + + if (rc == 0) + rc = netback_accel_sf_init(); + + if (rc == 0) + rc = register_netdevice_notifier + (&netback_accel_netdev_notifier); + + /* + * What if no device was found, shouldn't we clean up stuff + * we've allocated for acceleration subsystem? + */ + + return rc; +} + +module_init(netback_accel_init); + +static void __exit netback_accel_exit(void) +{ + unregister_netdevice_notifier(&netback_accel_netdev_notifier); + + netback_accel_sf_shutdown(); + + netback_accel_shutdown_bends(); + + netback_accel_debugfs_fini(); + + netback_accel_shutdown_fwd(); + +#ifdef EFX_GCOV + gcov_provider_fini(THIS_MODULE); +#endif +} + +module_exit(netback_accel_exit); + +MODULE_LICENSE("GPL"); diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel.h --- /dev/null +++ b/drivers/xen/sfc_netback/accel.h @@ -0,0 +1,393 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef NETBACK_ACCEL_H +#define NETBACK_ACCEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "accel_shared_fifo.h" +#include "accel_msg_iface.h" +#include "accel_util.h" + +/************************************************************************** + * Datatypes + **************************************************************************/ + +#define NETBACK_ACCEL_DEFAULT_MAX_FILTERS (8) +#define NETBACK_ACCEL_DEFAULT_MAX_MCASTS (8) +#define NETBACK_ACCEL_DEFAULT_MAX_BUF_PAGES (384) +/* Variable to store module parameter for max_buf_pages */ +extern unsigned max_pages; + +#define NETBACK_ACCEL_STATS 1 + +#if NETBACK_ACCEL_STATS +#define NETBACK_ACCEL_STATS_OP(x) x +#else +#define NETBACK_ACCEL_STATS_OP(x) +#endif + +/*! Statistics for a given backend */ +struct netback_accel_stats { + /*! Number of eventq wakeup events */ + u64 evq_wakeups; + /*! Number of eventq timeout events */ + u64 evq_timeouts; + /*! Number of filters used */ + u32 num_filters; + /*! Number of buffer pages registered */ + u32 num_buffer_pages; +}; + + +/* Debug fs nodes for each of the above stats */ +struct netback_accel_dbfs { + struct dentry *evq_wakeups; + struct dentry *evq_timeouts; + struct dentry *num_filters; + struct dentry *num_buffer_pages; +}; + + +/*! Resource limits for a given NIC */ +struct netback_accel_limits { + int max_filters; /*!< Max. number of filters to use. */ + int max_mcasts; /*!< Max. number of mcast subscriptions */ + int max_buf_pages; /*!< Max. number of pages of NIC buffers */ +}; + + +/*! The state for an instance of the back end driver. */ +struct netback_accel { + /*! mutex to protect this state */ + struct mutex bend_mutex; + + /*! Watches on xenstore */ + struct xenbus_watch domu_accel_watch; + struct xenbus_watch config_accel_watch; + + /*! Pointer to whatever device cookie ties us in to the hypervisor */ + void *hdev_data; + + /*! FIFO indices. Next page is msg FIFOs */ + struct net_accel_shared_page *shared_page; + + /*! Defer control message processing */ + struct work_struct handle_msg; + + /*! Identifies other end VM and interface.*/ + int far_end; + int vif_num; + + /*!< To unmap the shared pages */ + void *sh_pages_unmap; + + /* Resource tracking */ + /*! Limits on H/W & Dom0 resources */ + struct netback_accel_limits quotas; + + /* Hardware resources */ + /*! The H/W type of associated NIC */ + enum net_accel_hw_type hw_type; + /*! State of allocation */ + int hw_state; + /*! Index into ci_driver.nics[] for this interface */ + int nic_index; + /*! How to set up the acceleration for this hardware */ + int (*accel_setup)(struct netback_accel *); + /*! And how to stop it. */ + void (*accel_shutdown)(struct netback_accel *); + + /*! The physical/real net_dev for this interface */ + struct net_device *net_dev; + + /*! Magic pointer to locate state in fowarding table */ + void *fwd_priv; + + /*! Message FIFO */ + sh_msg_fifo2 to_domU; + /*! Message FIFO */ + sh_msg_fifo2 from_domU; + + /*! General notification channel id */ + int msg_channel; + /*! General notification channel irq */ + int msg_channel_irq; + + /*! Event channel id dedicated to network packet interrupts. */ + int net_channel; + /*! Event channel irq dedicated to network packets interrupts */ + int net_channel_irq; + + /*! The MAC address the frontend goes by. */ + u8 mac[ETH_ALEN]; + /*! Driver name of associated NIC */ + char *nicname; + + /*! Array of pointers to buffer pages mapped */ + grant_handle_t *buffer_maps; + u64 *buffer_addrs; + /*! Index into buffer_maps */ + int buffer_maps_index; + /*! Max number of pages that domU is allowed/will request to map */ + int max_pages; + + /*! Pointer to hardware specific private area */ + void *accel_hw_priv; + + /*! Wait queue for changes in accelstate. */ + wait_queue_head_t state_wait_queue; + + /*! Current state of the frontend according to the xenbus + * watch. */ + XenbusState frontend_state; + + /*! Current state of this backend. */ + XenbusState backend_state; + + /*! Non-zero if the backend is being removed. */ + int removing; + + /*! Non-zero if the setup_vnic has been called. */ + int vnic_is_setup; + +#if NETBACK_ACCEL_STATS + struct netback_accel_stats stats; +#endif +#if defined(CONFIG_DEBUG_FS) + char *dbfs_dir_name; + struct dentry *dbfs_dir; + struct netback_accel_dbfs dbfs; +#endif + + /*! List */ + struct netback_accel *next_bend; +}; + + +/* + * Values for netback_accel.hw_state. States of resource allocation + * we can go through + */ +/*! No hardware has yet been allocated. */ +#define NETBACK_ACCEL_RES_NONE (0) +/*! Hardware has been allocated. */ +#define NETBACK_ACCEL_RES_ALLOC (1) +#define NETBACK_ACCEL_RES_FILTER (2) +#define NETBACK_ACCEL_RES_HWINFO (3) + +/*! Filtering specification. This assumes that for VNIC support we + * will always want wildcard entries, so only specifies the + * destination IP/port + */ +struct netback_accel_filter_spec { + /*! Internal, used to access efx_vi API */ + void *filter_handle; + + /*! Destination IP in network order */ + u32 destip_be; + /*! Destination port in network order */ + u16 destport_be; + /*! Mac address */ + u8 mac[ETH_ALEN]; + /*! TCP or UDP */ + u8 proto; +}; + + +/************************************************************************** + * From accel.c + **************************************************************************/ + +/*! \brief Start up all the acceleration plugins + * + * \return 0 on success, an errno on failure + */ +extern int netback_accel_init_accel(void); + +/*! \brief Shut down all the acceleration plugins + */ +extern void netback_accel_shutdown_accel(void); + + +/************************************************************************** + * From accel_fwd.c + **************************************************************************/ + +/*! \brief Init the forwarding infrastructure + * \return 0 on success, or -ENOMEM if it couldn't get memory for the + * forward table + */ +extern int netback_accel_init_fwd(void); + +/*! \brief Shut down the forwarding and free memory. */ +extern void netback_accel_shutdown_fwd(void); + +/*! Initialise each nic port's fowarding table */ +extern void *netback_accel_init_fwd_port(void); +extern void netback_accel_shutdown_fwd_port(void *fwd_priv); + +/*! \brief Add an entry to the forwarding table. + * \param mac : MAC address, used as hash key + * \param ctxt : value to associate with key (can be NULL, see + * netback_accel_fwd_set_context) + * \return 0 on success, -ENOMEM if table was full and could no grow it + */ +extern int netback_accel_fwd_add(const __u8 *mac, void *context, + void *fwd_priv); + +/*! \brief Remove an entry from the forwarding table. + * \param mac : the MAC address to remove + * \return nothing: it is not an error if the mac was not in the table + */ +extern void netback_accel_fwd_remove(const __u8 *mac, void *fwd_priv); + +/*! \brief Set the context pointer for an existing fwd table entry. + * \param mac : key that is already present in the table + * \param context : new value to associate with key + * \return 0 on success, -ENOENT if mac not present in table. + */ +extern int netback_accel_fwd_set_context(const __u8 *mac, void *context, + void *fwd_priv); + +/************************************************************************** + * From accel_msg.c + **************************************************************************/ + + +/*! \brief Send the start-of-day message that handshakes with the VNIC + * and tells it its MAC address. + * + * \param bend The back end driver data structure + * \param version The version of communication to use, e.g. NET_ACCEL_MSG_VERSION + */ +extern void netback_accel_msg_tx_hello(struct netback_accel *bend, + unsigned version); + +/*! \brief Send a "there's a new local mac address" message + * + * \param bend The back end driver data structure for the vnic to send + * the message to + * \param mac Pointer to the new mac address + */ +extern void netback_accel_msg_tx_new_localmac(struct netback_accel *bend, + const void *mac); + +/*! \brief Send a "a mac address that was local has gone away" message + * + * \param bend The back end driver data structure for the vnic to send + * the message to + * \param mac Pointer to the old mac address + */ +extern void netback_accel_msg_tx_old_localmac(struct netback_accel *bend, + const void *mac); + +extern void netback_accel_set_interface_state(struct netback_accel *bend, + int up); + +/*! \brief Process the message queue for a bend that has just + * interrupted. + * + * Demultiplexs an interrupt from the front end driver, taking + * messages from the fifo and taking appropriate action. + * + * \param bend The back end driver data structure + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +extern void netback_accel_msg_rx_handler(struct work_struct *arg); +#else +extern void netback_accel_msg_rx_handler(void *bend_void); +#endif + +/************************************************************************** + * From accel_xenbus.c + **************************************************************************/ +/*! List of all the bends currently in existence. */ +extern struct netback_accel *bend_list; +extern struct mutex bend_list_mutex; + +/*! \brief Probe a new network interface. */ +extern int netback_accel_probe(struct xenbus_device *dev); + +/*! \brief Remove a network interface. */ +extern int netback_accel_remove(struct xenbus_device *dev); + +/*! \brief Shutdown all accelerator backends */ +extern void netback_accel_shutdown_bends(void); + +/*! \brief Initiate the xenbus state teardown handshake */ +extern void netback_accel_set_closing(struct netback_accel *bend); + +/************************************************************************** + * From accel_debugfs.c + **************************************************************************/ +/*! Global statistics */ +struct netback_accel_global_stats { + /*! Number of TX packets seen through driverlink */ + u64 dl_tx_packets; + /*! Number of TX packets seen through driverlink we didn't like */ + u64 dl_tx_bad_packets; + /*! Number of RX packets seen through driverlink */ + u64 dl_rx_packets; + /*! Number of mac addresses we are forwarding to */ + u32 num_fwds; +}; + +/*! Debug fs entries for each of the above stats */ +struct netback_accel_global_dbfs { + struct dentry *dl_tx_packets; + struct dentry *dl_tx_bad_packets; + struct dentry *dl_rx_packets; + struct dentry *num_fwds; +}; + +#if NETBACK_ACCEL_STATS +extern struct netback_accel_global_stats global_stats; +#endif + +/*! \brief Initialise the debugfs root and populate with global stats */ +extern void netback_accel_debugfs_init(void); + +/*! \brief Remove our debugfs root directory */ +extern void netback_accel_debugfs_fini(void); + +/*! \brief Add per-bend statistics to debug fs */ +extern int netback_accel_debugfs_create(struct netback_accel *bend); +/*! \brief Remove per-bend statistics from debug fs */ +extern int netback_accel_debugfs_remove(struct netback_accel *bend); + +#endif /* NETBACK_ACCEL_H */ + + diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_debugfs.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel_debugfs.c @@ -0,0 +1,170 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include +#include + +#include "accel.h" + +#if defined(CONFIG_DEBUG_FS) +static struct dentry *sfc_debugfs_root = NULL; +#endif + +#if NETBACK_ACCEL_STATS +struct netback_accel_global_stats global_stats; +#if defined(CONFIG_DEBUG_FS) +static struct netback_accel_global_dbfs global_dbfs; +#endif +#endif + +/* + * Extend debugfs helper functions to have a u64 version + */ +static void debugfs_u64_set(void *data, u64 val) +{ + *(u64 *)data = val; +} + +static u64 debugfs_u64_get(void *data) +{ + return *(u64 *)data; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n"); + +struct dentry *debugfs_create_u64(const char *name, mode_t mode, + struct dentry *parent, u64 *value) +{ + return debugfs_create_file(name, mode, parent, value, &fops_u64); +} + + +void netback_accel_debugfs_init(void) +{ +#if defined(CONFIG_DEBUG_FS) + sfc_debugfs_root = debugfs_create_dir("sfc_netback", NULL); + if (sfc_debugfs_root == NULL) + return; + + global_dbfs.num_fwds = debugfs_create_u32 + ("num_fwds", S_IRUSR | S_IRGRP | S_IROTH, + sfc_debugfs_root, &global_stats.num_fwds); + global_dbfs.dl_tx_packets = debugfs_create_u64 + ("dl_tx_packets", S_IRUSR | S_IRGRP | S_IROTH, + sfc_debugfs_root, &global_stats.dl_tx_packets); + global_dbfs.dl_rx_packets = debugfs_create_u64 + ("dl_rx_packets", S_IRUSR | S_IRGRP | S_IROTH, + sfc_debugfs_root, &global_stats.dl_rx_packets); + global_dbfs.dl_tx_bad_packets = debugfs_create_u64 + ("dl_tx_bad_packets", S_IRUSR | S_IRGRP | S_IROTH, + sfc_debugfs_root, &global_stats.dl_tx_bad_packets); +#endif +} + + +void netback_accel_debugfs_fini(void) +{ +#if defined(CONFIG_DEBUG_FS) + debugfs_remove(global_dbfs.num_fwds); + debugfs_remove(global_dbfs.dl_tx_packets); + debugfs_remove(global_dbfs.dl_rx_packets); + debugfs_remove(global_dbfs.dl_tx_bad_packets); + + debugfs_remove(sfc_debugfs_root); +#endif +} + + +int netback_accel_debugfs_create(struct netback_accel *bend) +{ +#if defined(CONFIG_DEBUG_FS) + /* Smallest length is 7 (vif0.0\n) */ + int length = 7, temp; + + if (sfc_debugfs_root == NULL) + return -ENOENT; + + /* Work out length of string representation of far_end and vif_num */ + temp = bend->far_end; + while (temp > 9) { + length++; + temp = temp / 10; + } + temp = bend->vif_num; + while (temp > 9) { + length++; + temp = temp / 10; + } + + bend->dbfs_dir_name = kmalloc(length, GFP_KERNEL); + if (bend->dbfs_dir_name == NULL) + return -ENOMEM; + sprintf(bend->dbfs_dir_name, "vif%d.%d", bend->far_end, bend->vif_num); + + bend->dbfs_dir = debugfs_create_dir(bend->dbfs_dir_name, + sfc_debugfs_root); + if (bend->dbfs_dir == NULL) { + kfree(bend->dbfs_dir_name); + return -ENOMEM; + } + +#if NETBACK_ACCEL_STATS + bend->dbfs.evq_wakeups = debugfs_create_u64 + ("evq_wakeups", S_IRUSR | S_IRGRP | S_IROTH, + bend->dbfs_dir, &bend->stats.evq_wakeups); + bend->dbfs.evq_timeouts = debugfs_create_u64 + ("evq_timeouts", S_IRUSR | S_IRGRP | S_IROTH, + bend->dbfs_dir, &bend->stats.evq_timeouts); + bend->dbfs.num_filters = debugfs_create_u32 + ("num_filters", S_IRUSR | S_IRGRP | S_IROTH, + bend->dbfs_dir, &bend->stats.num_filters); + bend->dbfs.num_buffer_pages = debugfs_create_u32 + ("num_buffer_pages", S_IRUSR | S_IRGRP | S_IROTH, + bend->dbfs_dir, &bend->stats.num_buffer_pages); +#endif +#endif + return 0; +} + + +int netback_accel_debugfs_remove(struct netback_accel *bend) +{ +#if defined(CONFIG_DEBUG_FS) + if (bend->dbfs_dir != NULL) { +#if NETBACK_ACCEL_STATS + debugfs_remove(bend->dbfs.evq_wakeups); + debugfs_remove(bend->dbfs.evq_timeouts); + debugfs_remove(bend->dbfs.num_filters); + debugfs_remove(bend->dbfs.num_buffer_pages); +#endif + debugfs_remove(bend->dbfs_dir); + } + + if (bend->dbfs_dir_name) + kfree(bend->dbfs_dir_name); +#endif + return 0; +} + + diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_fwd.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel_fwd.c @@ -0,0 +1,415 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include "accel.h" +#include "accel_cuckoo_hash.h" +#include "accel_util.h" +#include "accel_solarflare.h" + +#include "driverlink_api.h" + +#include +#include +#include + +/* State stored in the forward table */ +struct fwd_struct { + struct list_head link; /* Forms list */ + void * context; + __u8 valid; + __u8 mac[ETH_ALEN]; +}; + +/* Max value we support */ +#define NUM_FWDS_BITS 8 +#define NUM_FWDS (1 << NUM_FWDS_BITS) +#define FWD_MASK (NUM_FWDS - 1) + +struct port_fwd { + /* Make a list */ + struct list_head link; + /* Hash table to store the fwd_structs */ + cuckoo_hash_table fwd_hash_table; + /* The array of fwd_structs */ + struct fwd_struct *fwd_array; + /* Linked list of entries in use. */ + struct list_head fwd_list; + /* Could do something clever with a reader/writer lock. */ + spinlock_t fwd_lock; + /* Make find_free_entry() a bit faster by caching this */ + int last_free_index; +}; + +/* + * This is unlocked as it's only called from dl probe and remove, + * which are themselves synchronised. Could get rid of it entirely as + * it's never iterated, but useful for debug + */ +static struct list_head port_fwds; + + +/* Search the fwd_array for an unused entry */ +static int fwd_find_free_entry(struct port_fwd *fwd_set) +{ + int index = fwd_set->last_free_index; + + do { + if (!fwd_set->fwd_array[index].valid) { + fwd_set->last_free_index = index; + return index; + } + index++; + if (index >= NUM_FWDS) + index = 0; + } while (index != fwd_set->last_free_index); + + return -ENOMEM; +} + + +/* Look up a MAC in the hash table. Caller should hold table lock. */ +static inline struct fwd_struct *fwd_find_entry(const __u8 *mac, + struct port_fwd *fwd_set) +{ + cuckoo_hash_value value; + cuckoo_hash_mac_key key = cuckoo_mac_to_key(mac); + + if (cuckoo_hash_lookup(&fwd_set->fwd_hash_table, + (cuckoo_hash_key *)(&key), + &value)) { + struct fwd_struct *fwd = &fwd_set->fwd_array[value]; + DPRINTK_ON(memcmp(fwd->mac, mac, ETH_ALEN) != 0); + return fwd; + } + + return NULL; +} + + +/* Initialise each nic port's fowarding table */ +void *netback_accel_init_fwd_port(void) +{ + struct port_fwd *fwd_set; + + fwd_set = kzalloc(sizeof(struct port_fwd), GFP_KERNEL); + if (fwd_set == NULL) { + return NULL; + } + + spin_lock_init(&fwd_set->fwd_lock); + + fwd_set->fwd_array = kzalloc(sizeof (struct fwd_struct) * NUM_FWDS, + GFP_KERNEL); + if (fwd_set->fwd_array == NULL) { + kfree(fwd_set); + return NULL; + } + + if (cuckoo_hash_init(&fwd_set->fwd_hash_table, NUM_FWDS_BITS, 8) != 0) { + kfree(fwd_set->fwd_array); + kfree(fwd_set); + return NULL; + } + + INIT_LIST_HEAD(&fwd_set->fwd_list); + + list_add(&fwd_set->link, &port_fwds); + + return fwd_set; +} + + +void netback_accel_shutdown_fwd_port(void *fwd_priv) +{ + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + + BUG_ON(fwd_priv == NULL); + + BUG_ON(list_empty(&port_fwds)); + list_del(&fwd_set->link); + + BUG_ON(!list_empty(&fwd_set->fwd_list)); + + cuckoo_hash_destroy(&fwd_set->fwd_hash_table); + kfree(fwd_set->fwd_array); + kfree(fwd_set); +} + + +int netback_accel_init_fwd() +{ + INIT_LIST_HEAD(&port_fwds); + return 0; +} + + +void netback_accel_shutdown_fwd() +{ + BUG_ON(!list_empty(&port_fwds)); +} + + +/* + * Add an entry to the forwarding table. Returns -ENOMEM if no + * space. + */ +int netback_accel_fwd_add(const __u8 *mac, void *context, void *fwd_priv) +{ + struct fwd_struct *fwd; + int rc = 0, index; + unsigned long flags; + cuckoo_hash_mac_key key = cuckoo_mac_to_key(mac); + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + + BUG_ON(fwd_priv == NULL); + + DPRINTK("Adding mac " MAC_FMT "\n", MAC_ARG(mac)); + + spin_lock_irqsave(&fwd_set->fwd_lock, flags); + + if ((rc = fwd_find_free_entry(fwd_set)) < 0 ) { + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + return rc; + } + + index = rc; + + /* Shouldn't already be in the table */ + BUG_ON(cuckoo_hash_lookup(&fwd_set->fwd_hash_table, + (cuckoo_hash_key *)(&key), &rc) != 0); + + if ((rc = cuckoo_hash_add(&fwd_set->fwd_hash_table, + (cuckoo_hash_key *)(&key), index, 1)) == 0) { + fwd = &fwd_set->fwd_array[index]; + fwd->valid = 1; + fwd->context = context; + memcpy(fwd->mac, mac, ETH_ALEN); + list_add(&fwd->link, &fwd_set->fwd_list); + NETBACK_ACCEL_STATS_OP(global_stats.num_fwds++); + } + + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + + /* + * No need to tell frontend that this mac address is local - + * it should auto-discover through packets on fastpath what is + * local and what is not, and just being on same server + * doesn't make it local (it could be on a different + * bridge) + */ + + return rc; +} + + +/* remove an entry from the forwarding tables. */ +void netback_accel_fwd_remove(const __u8 *mac, void *fwd_priv) +{ + struct fwd_struct *fwd; + unsigned long flags; + cuckoo_hash_mac_key key = cuckoo_mac_to_key(mac); + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + + DPRINTK("Removing mac " MAC_FMT "\n", MAC_ARG(mac)); + + BUG_ON(fwd_priv == NULL); + + spin_lock_irqsave(&fwd_set->fwd_lock, flags); + + fwd = fwd_find_entry(mac, fwd_set); + if (fwd != NULL) { + BUG_ON(list_empty(&fwd_set->fwd_list)); + list_del(&fwd->link); + + fwd->valid = 0; + cuckoo_hash_remove(&fwd_set->fwd_hash_table, + (cuckoo_hash_key *)(&key)); + NETBACK_ACCEL_STATS_OP(global_stats.num_fwds--); + } + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + + /* + * No need to tell frontend that this is no longer present - + * the frontend is currently only interested in remote + * addresses and it works these out (mostly) by itself + */ +} + + +/* Set the context pointer for a hash table entry. */ +int netback_accel_fwd_set_context(const __u8 *mac, void *context, + void *fwd_priv) +{ + struct fwd_struct *fwd; + unsigned long flags; + int rc = -ENOENT; + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + + BUG_ON(fwd_priv == NULL); + + spin_lock_irqsave(&fwd_set->fwd_lock, flags); + fwd = fwd_find_entry(mac, fwd_set); + if (fwd != NULL) { + fwd->context = context; + rc = 0; + } + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + return rc; +} + + +/************************************************************************** + * Process a received packet + **************************************************************************/ + +/* + * Returns whether or not we have a match in our forward table for the + * this skb. Must be called with appropriate fwd_lock already held + */ +static struct netback_accel *for_a_vnic(struct netback_pkt_buf *skb, + struct port_fwd *fwd_set) +{ + struct fwd_struct *fwd; + struct netback_accel *retval = NULL; + + fwd = fwd_find_entry(skb->mac.raw, fwd_set); + if (fwd != NULL) + retval = fwd->context; + return retval; +} + + +static inline int packet_is_arp_reply(struct sk_buff *skb) +{ + return skb->protocol == ntohs(ETH_P_ARP) + && skb->nh.arph->ar_op == ntohs(ARPOP_REPLY); +} + + +static inline void hdr_to_filt(struct ethhdr *ethhdr, struct iphdr *ip, + struct netback_accel_filter_spec *spec) +{ + spec->proto = ip->protocol; + spec->destip_be = ip->daddr; + memcpy(spec->mac, ethhdr->h_source, ETH_ALEN); + + if (ip->protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (struct tcphdr *)((char *)ip + 4 * ip->ihl); + spec->destport_be = tcp->dest; + } else { + struct udphdr *udp = (struct udphdr *)((char *)ip + 4 * ip->ihl); + EPRINTK_ON(ip->protocol != IPPROTO_UDP); + spec->destport_be = udp->dest; + } +} + + +static inline int netback_accel_can_filter(struct netback_pkt_buf *skb) +{ + return (skb->protocol == htons(ETH_P_IP) && + ((skb->nh.iph->protocol == IPPROTO_TCP) || + (skb->nh.iph->protocol == IPPROTO_UDP))); +} + + +static inline void netback_accel_filter_packet(struct netback_accel *bend, + struct netback_pkt_buf *skb) +{ + struct netback_accel_filter_spec fs; + struct ethhdr *eh = (struct ethhdr *)(skb->mac.raw); + + hdr_to_filt(eh, skb->nh.iph, &fs); + + netback_accel_filter_check_add(bend, &fs); +} + + +/* + * Receive a packet and do something appropriate with it. Return true + * to take exclusive ownership of the packet. This is verging on + * solarflare specific + */ +void netback_accel_rx_packet(struct netback_pkt_buf *skb, void *fwd_priv) +{ + struct netback_accel *bend; + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + unsigned long flags; + + BUG_ON(fwd_priv == NULL); + + /* Checking for bcast is cheaper so do that first */ + if (is_broadcast_ether_addr(skb->mac.raw)) { + /* pass through the slow path by not claiming ownership */ + return; + } else if (is_multicast_ether_addr(skb->mac.raw)) { + /* pass through the slow path by not claiming ownership */ + return; + } else { + /* It is unicast */ + spin_lock_irqsave(&fwd_set->fwd_lock, flags); + /* We insert filter to pass it off to a VNIC */ + if ((bend = for_a_vnic(skb, fwd_set)) != NULL) + if (netback_accel_can_filter(skb)) + netback_accel_filter_packet(bend, skb); + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + } + return; +} + + +void netback_accel_tx_packet(struct sk_buff *skb, void *fwd_priv) +{ + __u8 *mac; + unsigned long flags; + struct port_fwd *fwd_set = (struct port_fwd *)fwd_priv; + struct fwd_struct *fwd; + + BUG_ON(fwd_priv == NULL); + + if (is_broadcast_ether_addr(skb->mac.raw) && packet_is_arp_reply(skb)) { + /* + * update our fast path forwarding to reflect this + * gratuitous ARP + */ + mac = skb->mac.raw+ETH_ALEN; + + DPRINTK("%s: found gratuitous ARP for " MAC_FMT "\n", + __FUNCTION__, MAC_ARG(mac)); + + spin_lock_irqsave(&fwd_set->fwd_lock, flags); + /* + * Might not be local, but let's tell them all it is, + * and they can restore the fastpath if they continue + * to get packets that way + */ + list_for_each_entry(fwd, &fwd_set->fwd_list, link) { + struct netback_accel *bend = fwd->context; + if (bend != NULL) + netback_accel_msg_tx_new_localmac(bend, mac); + } + + spin_unlock_irqrestore(&fwd_set->fwd_lock, flags); + } + return; +} diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_msg.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel_msg.c @@ -0,0 +1,392 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include + +#include "accel.h" +#include "accel_msg_iface.h" +#include "accel_util.h" +#include "accel_solarflare.h" + +/* Send a HELLO to front end to start things off */ +void netback_accel_msg_tx_hello(struct netback_accel *bend, unsigned version) +{ + unsigned long lock_state; + struct net_accel_msg *msg = + net_accel_msg_start_send(bend->shared_page, + &bend->to_domU, &lock_state); + /* The queue _cannot_ be full, we're the first users. */ + EPRINTK_ON(msg == NULL); + + if (msg != NULL) { + net_accel_msg_init(msg, NET_ACCEL_MSG_HELLO); + msg->u.hello.version = version; + msg->u.hello.max_pages = bend->quotas.max_buf_pages; + VPRINTK("Sending hello to channel %d\n", bend->msg_channel); + net_accel_msg_complete_send_notify(bend->shared_page, + &bend->to_domU, + &lock_state, + bend->msg_channel_irq); + } +} + +/* Send a local mac message to vnic */ +static void netback_accel_msg_tx_localmac(struct netback_accel *bend, + int type, const void *mac) +{ + unsigned long lock_state; + struct net_accel_msg *msg; + + BUG_ON(bend == NULL || mac == NULL); + + VPRINTK("Sending local mac message: " MAC_FMT "\n", + MAC_ARG((const char *)mac)); + + msg = net_accel_msg_start_send(bend->shared_page, &bend->to_domU, + &lock_state); + + if (msg != NULL) { + net_accel_msg_init(msg, NET_ACCEL_MSG_LOCALMAC); + msg->u.localmac.flags = type; + memcpy(msg->u.localmac.mac, mac, ETH_ALEN); + net_accel_msg_complete_send_notify(bend->shared_page, + &bend->to_domU, + &lock_state, + bend->msg_channel_irq); + } else { + /* + * TODO if this happens we may leave a domU + * fastpathing packets when they should be delivered + * locally. Solution is get domU to timeout entries + * in its fastpath lookup table when it receives no RX + * traffic + */ + EPRINTK("%s: saw full queue, may need ARP timer to recover\n", + __FUNCTION__); + } +} + +/* Send an add local mac message to vnic */ +void netback_accel_msg_tx_new_localmac(struct netback_accel *bend, + const void *mac) +{ + netback_accel_msg_tx_localmac(bend, NET_ACCEL_MSG_ADD, mac); +} + + +static int netback_accel_msg_rx_buffer_map(struct netback_accel *bend, + struct net_accel_msg *msg) +{ + int log2_pages, rc; + + /* Can only allocate in power of two */ + log2_pages = log2_ge(msg->u.mapbufs.pages, 0); + if (msg->u.mapbufs.pages != pow2(log2_pages)) { + EPRINTK("%s: Can only alloc bufs in power of 2 sizes (%d)\n", + __FUNCTION__, msg->u.mapbufs.pages); + rc = -EINVAL; + goto err_out; + } + + /* + * Sanity. Assumes NET_ACCEL_MSG_MAX_PAGE_REQ is same for + * both directions/domains + */ + if (msg->u.mapbufs.pages > NET_ACCEL_MSG_MAX_PAGE_REQ) { + EPRINTK("%s: too many pages in a single message: %d %d\n", + __FUNCTION__, msg->u.mapbufs.pages, + NET_ACCEL_MSG_MAX_PAGE_REQ); + rc = -EINVAL; + goto err_out; + } + + if ((rc = netback_accel_add_buffers(bend, msg->u.mapbufs.pages, + log2_pages, msg->u.mapbufs.grants, + &msg->u.mapbufs.buf)) < 0) { + goto err_out; + } + + msg->id |= NET_ACCEL_MSG_REPLY; + + return 0; + + err_out: + EPRINTK("%s: err_out\n", __FUNCTION__); + msg->id |= NET_ACCEL_MSG_ERROR | NET_ACCEL_MSG_REPLY; + return rc; +} + + +/* Hint from frontend that one of our filters is out of date */ +static int netback_accel_process_fastpath(struct netback_accel *bend, + struct net_accel_msg *msg) +{ + struct netback_accel_filter_spec spec; + + if (msg->u.fastpath.flags & NET_ACCEL_MSG_REMOVE) { + /* + * Would be nice to BUG() this but would leave us + * vulnerable to naughty frontend + */ + EPRINTK_ON(msg->u.fastpath.flags & NET_ACCEL_MSG_ADD); + + memcpy(spec.mac, msg->u.fastpath.mac, ETH_ALEN); + spec.destport_be = msg->u.fastpath.port; + spec.destip_be = msg->u.fastpath.ip; + spec.proto = msg->u.fastpath.proto; + + netback_accel_filter_remove_spec(bend, &spec); + } + + return 0; +} + + +/* Flow control for message queues */ +inline void set_queue_not_full(struct netback_accel *bend) +{ + if (!test_and_set_bit(NET_ACCEL_MSG_AFLAGS_QUEUEUNOTFULL_B, + (unsigned long *)&bend->shared_page->aflags)) + notify_remote_via_irq(bend->msg_channel_irq); + else + VPRINTK("queue not full bit already set, not signalling\n"); +} + + +/* Flow control for message queues */ +inline void set_queue_full(struct netback_accel *bend) +{ + if (!test_and_set_bit(NET_ACCEL_MSG_AFLAGS_QUEUE0FULL_B, + (unsigned long *)&bend->shared_page->aflags)) + notify_remote_via_irq(bend->msg_channel_irq); + else + VPRINTK("queue full bit already set, not signalling\n"); +} + + +void netback_accel_set_interface_state(struct netback_accel *bend, int up) +{ + bend->shared_page->net_dev_up = up; + if (!test_and_set_bit(NET_ACCEL_MSG_AFLAGS_NETUPDOWN_B, + (unsigned long *)&bend->shared_page->aflags)) + notify_remote_via_irq(bend->msg_channel_irq); + else + VPRINTK("interface up/down bit already set, not signalling\n"); +} + + +static int check_rx_hello_version(unsigned version) +{ + /* Should only happen if there's been a version mismatch */ + BUG_ON(version == NET_ACCEL_MSG_VERSION); + + if (version > NET_ACCEL_MSG_VERSION) { + /* Newer protocol, we must refuse */ + return -EPROTO; + } + + if (version < NET_ACCEL_MSG_VERSION) { + /* + * We are newer, so have discretion to accept if we + * wish. For now however, just reject + */ + return -EPROTO; + } + + return -EINVAL; +} + + +static int process_rx_msg(struct netback_accel *bend, + struct net_accel_msg *msg) +{ + int err = 0; + + switch (msg->id) { + case NET_ACCEL_MSG_REPLY | NET_ACCEL_MSG_HELLO: + /* Reply to a HELLO; mark ourselves as connected */ + DPRINTK("got Hello reply, version %.8x\n", + msg->u.hello.version); + + /* + * Check that we've not successfully done this + * already. NB no check at the moment that this reply + * comes after we've actually sent a HELLO as that's + * not possible with the current code structure + */ + if (bend->hw_state != NETBACK_ACCEL_RES_NONE) + return -EPROTO; + + /* Store max_pages for accel_setup */ + if (msg->u.hello.max_pages > bend->quotas.max_buf_pages) { + EPRINTK("More pages than quota allows (%d > %d)\n", + msg->u.hello.max_pages, + bend->quotas.max_buf_pages); + /* Force it down to the quota */ + msg->u.hello.max_pages = bend->quotas.max_buf_pages; + } + bend->max_pages = msg->u.hello.max_pages; + + /* Set up the hardware visible to the other end */ + err = bend->accel_setup(bend); + if (err) { + /* This is fatal */ + DPRINTK("Hello gave accel_setup error %d\n", err); + netback_accel_set_closing(bend); + } else { + /* + * Now add the context so that packet + * forwarding will commence + */ + netback_accel_fwd_set_context(bend->mac, bend, + bend->fwd_priv); + } + break; + case NET_ACCEL_MSG_REPLY | NET_ACCEL_MSG_HELLO | NET_ACCEL_MSG_ERROR: + EPRINTK("got Hello error, versions us:%.8x them:%.8x\n", + NET_ACCEL_MSG_VERSION, msg->u.hello.version); + + if (bend->hw_state != NETBACK_ACCEL_RES_NONE) + return -EPROTO; + + if (msg->u.hello.version != NET_ACCEL_MSG_VERSION) { + /* Error is due to version mismatch */ + err = check_rx_hello_version(msg->u.hello.version); + if (err == 0) { + /* + * It's OK to be compatible, send + * another hello with compatible version + */ + netback_accel_msg_tx_hello + (bend, msg->u.hello.version); + } else { + /* + * Tell frontend that we're not going to + * send another HELLO by going to Closing. + */ + netback_accel_set_closing(bend); + } + } + break; + case NET_ACCEL_MSG_MAPBUF: + VPRINTK("Got mapped buffers request %d\n", + msg->u.mapbufs.reqid); + + if (bend->hw_state == NETBACK_ACCEL_RES_NONE) + return -EPROTO; + + /* + * Frontend wants a buffer table entry for the + * supplied pages + */ + err = netback_accel_msg_rx_buffer_map(bend, msg); + if (net_accel_msg_reply_notify(bend->shared_page, + bend->msg_channel_irq, + &bend->to_domU, msg)) { + /* + * This is fatal as we can't tell the frontend + * about the problem through the message + * queue, and so would otherwise stalemate + */ + netback_accel_set_closing(bend); + } + break; + case NET_ACCEL_MSG_FASTPATH: + DPRINTK("Got fastpath request\n"); + + if (bend->hw_state == NETBACK_ACCEL_RES_NONE) + return -EPROTO; + + err = netback_accel_process_fastpath(bend, msg); + break; + default: + EPRINTK("Huh? Message code is %x\n", msg->id); + err = -EPROTO; + break; + } + return err; +} + + +/* Demultiplex an IRQ from the frontend driver. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +void netback_accel_msg_rx_handler(struct work_struct *arg) +#else +void netback_accel_msg_rx_handler(void *bend_void) +#endif +{ + struct net_accel_msg msg; + int err, queue_was_full = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + struct netback_accel *bend = + container_of(arg, struct netback_accel, handle_msg); +#else + struct netback_accel *bend = (struct netback_accel *)bend_void; +#endif + + mutex_lock(&bend->bend_mutex); + + /* + * This happens when the shared pages have been unmapped, but + * the workqueue not flushed yet + */ + if (bend->shared_page == NULL) + goto done; + + if ((bend->shared_page->aflags & + NET_ACCEL_MSG_AFLAGS_TO_DOM0_MASK) != 0) { + if (bend->shared_page->aflags & + NET_ACCEL_MSG_AFLAGS_QUEUE0NOTFULL) { + /* We've been told there may now be space. */ + clear_bit(NET_ACCEL_MSG_AFLAGS_QUEUE0NOTFULL_B, + (unsigned long *)&bend->shared_page->aflags); + } + + if (bend->shared_page->aflags & + NET_ACCEL_MSG_AFLAGS_QUEUEUFULL) { + clear_bit(NET_ACCEL_MSG_AFLAGS_QUEUEUFULL_B, + (unsigned long *)&bend->shared_page->aflags); + queue_was_full = 1; + } + } + + while ((err = net_accel_msg_recv(bend->shared_page, &bend->from_domU, + &msg)) == 0) { + err = process_rx_msg(bend, &msg); + + if (err != 0) { + EPRINTK("%s: Error %d\n", __FUNCTION__, err); + goto err; + } + } + + err: + /* There will be space now if we can make any. */ + if (queue_was_full) + set_queue_not_full(bend); + done: + mutex_unlock(&bend->bend_mutex); + + return; +} diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_solarflare.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel_solarflare.c @@ -0,0 +1,1253 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include "common.h" + +#include "accel.h" +#include "accel_solarflare.h" +#include "accel_msg_iface.h" +#include "accel_util.h" + +#include "accel_cuckoo_hash.h" + +#include "ci/driver/resource/efx_vi.h" + +#include "ci/efrm/nic_table.h" +#include "ci/efhw/public.h" + +#include +#include +#include +#include + +#include "driverlink_api.h" + +#define SF_XEN_RX_USR_BUF_SIZE 2048 + +struct falcon_bend_accel_priv { + struct efx_vi_state *efx_vih; + + /*! Array of pointers to dma_map state, used so VNIC can + * request their removal in a single message + */ + struct efx_vi_dma_map_state **dma_maps; + /*! Index into dma_maps */ + int dma_maps_index; + + /*! Serialises access to filters */ + spinlock_t filter_lock; + /*! Bitmap of which filters are free */ + unsigned long free_filters; + /*! Used for index normalisation */ + u32 filter_idx_mask; + struct netback_accel_filter_spec *fspecs; + cuckoo_hash_table filter_hash_table; + + u32 txdmaq_gnt; + u32 rxdmaq_gnt; + u32 doorbell_gnt; + u32 evq_rptr_gnt; + u32 evq_mem_gnts[EF_HW_FALCON_EVQ_PAGES]; + u32 evq_npages; +}; + +/* Forward declaration */ +static int netback_accel_filter_init(struct netback_accel *); +static void netback_accel_filter_shutdown(struct netback_accel *); + +/************************************************************************** + * + * Driverlink stuff + * + **************************************************************************/ + +struct driverlink_port { + struct list_head link; + enum net_accel_hw_type type; + struct net_device *net_dev; + struct efx_dl_device *efx_dl_dev; + int nic_index; + void *fwd_priv; +}; + +static struct list_head dl_ports; + +/* This mutex protects global state, such as the dl_ports list */ +DEFINE_MUTEX(accel_mutex); + +static int init_done = 0; + +/* The DL callbacks */ + + +#if defined(EFX_USE_FASTCALL) +static enum efx_veto fastcall +#else +static enum efx_veto +#endif +bend_dl_tx_packet(struct efx_dl_device *efx_dl_dev, + struct sk_buff *skb) +{ + struct driverlink_port *port = efx_dl_dev->priv; + + BUG_ON(port == NULL); + + NETBACK_ACCEL_STATS_OP(global_stats.dl_tx_packets++); + if (skb->mac.raw != NULL) + netback_accel_tx_packet(skb, port->fwd_priv); + else { + DPRINTK("Ignoring packet with missing mac address\n"); + NETBACK_ACCEL_STATS_OP(global_stats.dl_tx_bad_packets++); + } + return EFX_ALLOW_PACKET; +} + +/* EFX_USE_FASTCALL */ +#if defined(EFX_USE_FASTCALL) +static enum efx_veto fastcall +#else +static enum efx_veto +#endif +bend_dl_rx_packet(struct efx_dl_device *efx_dl_dev, + const char *pkt_buf, int pkt_len) +{ + struct driverlink_port *port = efx_dl_dev->priv; + struct netback_pkt_buf pkt; + struct ethhdr *eh; + + BUG_ON(port == NULL); + + pkt.mac.raw = (char *)pkt_buf; + pkt.nh.raw = (char *)pkt_buf + ETH_HLEN; + eh = (struct ethhdr *)pkt_buf; + pkt.protocol = eh->h_proto; + + NETBACK_ACCEL_STATS_OP(global_stats.dl_rx_packets++); + netback_accel_rx_packet(&pkt, port->fwd_priv); + return EFX_ALLOW_PACKET; +} + + +/* Callbacks we'd like to get from the netdriver through driverlink */ +struct efx_dl_callbacks bend_dl_callbacks = + { + .tx_packet = bend_dl_tx_packet, + .rx_packet = bend_dl_rx_packet, + }; + + +static struct netback_accel_hooks accel_hooks = { + THIS_MODULE, + &netback_accel_probe, + &netback_accel_remove +}; + + +/* + * Handy helper which given an efx_dl_device works out which + * efab_nic_t index into efrm_nic_table.nics[] it corresponds to + */ +static int efx_device_to_efab_nic_index(struct efx_dl_device *efx_dl_dev) +{ + int i; + + for (i = 0; i < EFHW_MAX_NR_DEVS; i++) { + struct efhw_nic *nic = efrm_nic_table.nic[i]; + + /* + * It's possible for the nic structure to have not + * been initialised if the resource driver failed its + * driverlink probe + */ + if (nic == NULL || nic->net_driver_dev == NULL) + continue; + + /* Work out if these are talking about the same NIC */ + if (nic->net_driver_dev->pci_dev == efx_dl_dev->pci_dev) + return i; + } + + return -1; +} + + +/* Driver link probe - register our callbacks */ +static int bend_dl_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) +{ + int rc; + enum net_accel_hw_type type; + struct driverlink_port *port; + + DPRINTK("%s: %s\n", __FUNCTION__, silicon_rev); + + if (strcmp(silicon_rev, "falcon/a1") == 0) + type = NET_ACCEL_MSG_HWTYPE_FALCON_A; + else if (strcmp(silicon_rev, "falcon/b0") == 0) + type = NET_ACCEL_MSG_HWTYPE_FALCON_B; + else { + EPRINTK("%s: unsupported silicon %s\n", __FUNCTION__, + silicon_rev); + rc = -EINVAL; + goto fail1; + } + + port = kmalloc(sizeof(struct driverlink_port), GFP_KERNEL); + if (port == NULL) { + EPRINTK("%s: no memory for dl probe\n", __FUNCTION__); + rc = -ENOMEM; + goto fail1; + } + + port->efx_dl_dev = efx_dl_dev; + efx_dl_dev->priv = port; + + port->nic_index = efx_device_to_efab_nic_index(efx_dl_dev); + if (port->nic_index < 0) { + /* + * This can happen in theory if the resource driver + * failed to initialise properly + */ + EPRINTK("%s: nic structure not found\n", __FUNCTION__); + rc = -EINVAL; + goto fail2; + } + + port->fwd_priv = netback_accel_init_fwd_port(); + if (port->fwd_priv == NULL) { + EPRINTK("%s: failed to set up forwarding for port\n", + __FUNCTION__); + rc = -ENOMEM; + goto fail2; + } + + rc = efx_dl_register_callbacks(efx_dl_dev, &bend_dl_callbacks); + if (rc != 0) { + EPRINTK("%s: register_callbacks failed\n", __FUNCTION__); + goto fail3; + } + + port->type = type; + port->net_dev = (struct net_device *)net_dev; + + mutex_lock(&accel_mutex); + list_add(&port->link, &dl_ports); + mutex_unlock(&accel_mutex); + + rc = netback_connect_accelerator(NETBACK_ACCEL_VERSION, 0, + port->net_dev->name, &accel_hooks); + + if (rc < 0) { + EPRINTK("Xen netback accelerator version mismatch\n"); + goto fail4; + } else if (rc > 0) { + /* + * In future may want to add backwards compatibility + * and accept certain subsets of previous versions + */ + EPRINTK("Xen netback accelerator version mismatch\n"); + goto fail4; + } + + return 0; + + fail4: + mutex_lock(&accel_mutex); + list_del(&port->link); + mutex_unlock(&accel_mutex); + + efx_dl_unregister_callbacks(efx_dl_dev, &bend_dl_callbacks); + fail3: + netback_accel_shutdown_fwd_port(port->fwd_priv); + fail2: + efx_dl_dev->priv = NULL; + kfree(port); + fail1: + return rc; +} + + +static void bend_dl_remove(struct efx_dl_device *efx_dl_dev) +{ + struct driverlink_port *port; + + DPRINTK("Unregistering driverlink callbacks.\n"); + + mutex_lock(&accel_mutex); + + port = (struct driverlink_port *)efx_dl_dev->priv; + + BUG_ON(list_empty(&dl_ports)); + BUG_ON(port == NULL); + BUG_ON(port->efx_dl_dev != efx_dl_dev); + + netback_disconnect_accelerator(0, port->net_dev->name); + + list_del(&port->link); + + mutex_unlock(&accel_mutex); + + efx_dl_unregister_callbacks(efx_dl_dev, &bend_dl_callbacks); + netback_accel_shutdown_fwd_port(port->fwd_priv); + + efx_dl_dev->priv = NULL; + kfree(port); + + return; +} + + +static struct efx_dl_driver bend_dl_driver = + { + .name = "SFC Xen backend", + .probe = bend_dl_probe, + .remove = bend_dl_remove, + }; + + +int netback_accel_sf_init(void) +{ + int rc, nic_i; + struct efhw_nic *nic; + + INIT_LIST_HEAD(&dl_ports); + + rc = efx_dl_register_driver(&bend_dl_driver); + /* If we couldn't find the NET driver, give up */ + if (rc == -ENOENT) + return rc; + + if (rc == 0) { + EFRM_FOR_EACH_NIC(nic_i, nic) + falcon_nic_set_rx_usr_buf_size(nic, + SF_XEN_RX_USR_BUF_SIZE); + } + + init_done = (rc == 0); + return rc; +} + + +void netback_accel_sf_shutdown(void) +{ + if (!init_done) + return; + DPRINTK("Unregistering driverlink driver\n"); + + /* + * This will trigger removal callbacks for all the devices, which + * will unregister their callbacks, disconnect from netfront, etc. + */ + efx_dl_unregister_driver(&bend_dl_driver); +} + + +int netback_accel_sf_hwtype(struct netback_accel *bend) +{ + struct driverlink_port *port; + + mutex_lock(&accel_mutex); + + list_for_each_entry(port, &dl_ports, link) { + if (strcmp(bend->nicname, port->net_dev->name) == 0) { + bend->hw_type = port->type; + bend->accel_setup = netback_accel_setup_vnic_hw; + bend->accel_shutdown = netback_accel_shutdown_vnic_hw; + bend->fwd_priv = port->fwd_priv; + /* This is just needed to pass to efx_vi_alloc */ + bend->nic_index = port->nic_index; + bend->net_dev = port->net_dev; + mutex_unlock(&accel_mutex); + return 0; + } + } + + mutex_unlock(&accel_mutex); + + EPRINTK("Failed to identify backend device '%s' with a NIC\n", + bend->nicname); + + return -ENOENT; +} + + +/**************************************************************************** + * Resource management code + ***************************************************************************/ + +static int alloc_page_state(struct netback_accel *bend, int max_pages) +{ + struct falcon_bend_accel_priv *accel_hw_priv; + + if (max_pages < 0 || max_pages > bend->quotas.max_buf_pages) { + EPRINTK("%s: invalid max_pages: %d\n", __FUNCTION__, max_pages); + return -EINVAL; + } + + accel_hw_priv = kzalloc(sizeof(struct falcon_bend_accel_priv), + GFP_KERNEL); + if (accel_hw_priv == NULL) { + EPRINTK("%s: no memory for accel_hw_priv\n", __FUNCTION__); + return -ENOMEM; + } + + accel_hw_priv->dma_maps = kzalloc + (sizeof(struct efx_vi_dma_map_state **) * + (max_pages / NET_ACCEL_MSG_MAX_PAGE_REQ), GFP_KERNEL); + if (accel_hw_priv->dma_maps == NULL) { + EPRINTK("%s: no memory for dma_maps\n", __FUNCTION__); + kfree(accel_hw_priv); + return -ENOMEM; + } + + bend->buffer_maps = kzalloc(sizeof(struct vm_struct *) * max_pages, + GFP_KERNEL); + if (bend->buffer_maps == NULL) { + EPRINTK("%s: no memory for buffer_maps\n", __FUNCTION__); + kfree(accel_hw_priv->dma_maps); + kfree(accel_hw_priv); + return -ENOMEM; + } + + bend->buffer_addrs = kzalloc(sizeof(u64) * max_pages, GFP_KERNEL); + if (bend->buffer_addrs == NULL) { + kfree(bend->buffer_maps); + kfree(accel_hw_priv->dma_maps); + kfree(accel_hw_priv); + return -ENOMEM; + } + + bend->accel_hw_priv = accel_hw_priv; + + return 0; +} + + +static int free_page_state(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv; + + DPRINTK("%s: %p\n", __FUNCTION__, bend); + + accel_hw_priv = bend->accel_hw_priv; + + if (accel_hw_priv) { + kfree(accel_hw_priv->dma_maps); + kfree(bend->buffer_maps); + kfree(bend->buffer_addrs); + kfree(accel_hw_priv); + bend->accel_hw_priv = NULL; + bend->max_pages = 0; + } + + return 0; +} + + +/* The timeout event callback for the event q */ +static void bend_evq_timeout(void *context, int is_timeout) +{ + struct netback_accel *bend = (struct netback_accel *)context; + if (is_timeout) { + /* Pass event to vnic front end driver */ + VPRINTK("timeout event to %d\n", bend->net_channel); + NETBACK_ACCEL_STATS_OP(bend->stats.evq_timeouts++); + notify_remote_via_irq(bend->net_channel_irq); + } else { + /* It's a wakeup event, used by Falcon */ + VPRINTK("wakeup to %d\n", bend->net_channel); + NETBACK_ACCEL_STATS_OP(bend->stats.evq_wakeups++); + notify_remote_via_irq(bend->net_channel_irq); + } +} + + +/* + * Create the eventq and associated gubbins for communication with the + * front end vnic driver + */ +static int ef_get_vnic(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv; + int rc = 0; + + BUG_ON(bend->hw_state != NETBACK_ACCEL_RES_NONE); + + /* Allocate page related state and accel_hw_priv */ + rc = alloc_page_state(bend, bend->max_pages); + if (rc != 0) { + EPRINTK("Failed to allocate page state: %d\n", rc); + return rc; + } + + accel_hw_priv = bend->accel_hw_priv; + + rc = efx_vi_alloc(&accel_hw_priv->efx_vih, bend->nic_index); + if (rc != 0) { + EPRINTK("%s: efx_vi_alloc failed %d\n", __FUNCTION__, rc); + free_page_state(bend); + return rc; + } + + rc = efx_vi_eventq_register_callback(accel_hw_priv->efx_vih, + bend_evq_timeout, + bend); + if (rc != 0) { + EPRINTK("%s: register_callback failed %d\n", __FUNCTION__, rc); + efx_vi_free(accel_hw_priv->efx_vih); + free_page_state(bend); + return rc; + } + + bend->hw_state = NETBACK_ACCEL_RES_ALLOC; + + return 0; +} + + +static void ef_free_vnic(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + + BUG_ON(bend->hw_state != NETBACK_ACCEL_RES_ALLOC); + + efx_vi_eventq_kill_callback(accel_hw_priv->efx_vih); + + DPRINTK("Hardware is freeable. Will proceed.\n"); + + efx_vi_free(accel_hw_priv->efx_vih); + accel_hw_priv->efx_vih = NULL; + + VPRINTK("Free page state...\n"); + free_page_state(bend); + + bend->hw_state = NETBACK_ACCEL_RES_NONE; +} + + +static inline void ungrant_or_crash(grant_ref_t gntref, int domain) { + if (net_accel_ungrant_page(gntref) == -EBUSY) + net_accel_shutdown_remote(domain); +} + + +static void netback_accel_release_hwinfo(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + int i; + + DPRINTK("Remove dma q grants %d %d\n", accel_hw_priv->txdmaq_gnt, + accel_hw_priv->rxdmaq_gnt); + ungrant_or_crash(accel_hw_priv->txdmaq_gnt, bend->far_end); + ungrant_or_crash(accel_hw_priv->rxdmaq_gnt, bend->far_end); + + DPRINTK("Remove doorbell grant %d\n", accel_hw_priv->doorbell_gnt); + ungrant_or_crash(accel_hw_priv->doorbell_gnt, bend->far_end); + + if (bend->hw_type == NET_ACCEL_MSG_HWTYPE_FALCON_A) { + DPRINTK("Remove rptr grant %d\n", accel_hw_priv->evq_rptr_gnt); + ungrant_or_crash(accel_hw_priv->evq_rptr_gnt, bend->far_end); + } + + for (i = 0; i < accel_hw_priv->evq_npages; i++) { + DPRINTK("Remove evq grant %d\n", accel_hw_priv->evq_mem_gnts[i]); + ungrant_or_crash(accel_hw_priv->evq_mem_gnts[i], bend->far_end); + } + + bend->hw_state = NETBACK_ACCEL_RES_FILTER; + + return; +} + + +static int ef_bend_hwinfo_falcon_common(struct netback_accel *bend, + struct net_accel_hw_falcon_b *hwinfo) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + struct efx_vi_hw_resource_metadata res_mdata; + struct efx_vi_hw_resource res_array[EFX_VI_HW_RESOURCE_MAXSIZE]; + int rc, len = EFX_VI_HW_RESOURCE_MAXSIZE, i, pfn = 0; + unsigned long txdmaq_pfn = 0, rxdmaq_pfn = 0; + + rc = efx_vi_hw_resource_get_phys(accel_hw_priv->efx_vih, &res_mdata, + res_array, &len); + if (rc != 0) { + DPRINTK("%s: resource_get_phys returned %d\n", + __FUNCTION__, rc); + return rc; + } + + if (res_mdata.version != 0) + return -EPROTO; + + hwinfo->nic_arch = res_mdata.nic_arch; + hwinfo->nic_variant = res_mdata.nic_variant; + hwinfo->nic_revision = res_mdata.nic_revision; + + hwinfo->evq_order = res_mdata.evq_order; + hwinfo->evq_offs = res_mdata.evq_offs; + hwinfo->evq_capacity = res_mdata.evq_capacity; + hwinfo->instance = res_mdata.instance; + hwinfo->rx_capacity = res_mdata.rx_capacity; + hwinfo->tx_capacity = res_mdata.tx_capacity; + + VPRINTK("evq_order %d evq_offs %d evq_cap %d inst %d rx_cap %d tx_cap %d\n", + hwinfo->evq_order, hwinfo->evq_offs, hwinfo->evq_capacity, + hwinfo->instance, hwinfo->rx_capacity, hwinfo->tx_capacity); + + for (i = 0; i < len; i++) { + struct efx_vi_hw_resource *res = &(res_array[i]); + switch (res->type) { + case EFX_VI_HW_RESOURCE_TXDMAQ: + txdmaq_pfn = page_to_pfn(virt_to_page(res->address)); + break; + case EFX_VI_HW_RESOURCE_RXDMAQ: + rxdmaq_pfn = page_to_pfn(virt_to_page(res->address)); + break; + case EFX_VI_HW_RESOURCE_EVQTIMER: + break; + case EFX_VI_HW_RESOURCE_EVQRPTR: + case EFX_VI_HW_RESOURCE_EVQRPTR_OFFSET: + hwinfo->evq_rptr = res->address; + break; + case EFX_VI_HW_RESOURCE_EVQMEMKVA: + accel_hw_priv->evq_npages = 1 << res_mdata.evq_order; + pfn = page_to_pfn(virt_to_page(res->address)); + break; + case EFX_VI_HW_RESOURCE_BELLPAGE: + hwinfo->doorbell_mfn = res->address; + break; + default: + EPRINTK("%s: Unknown hardware resource type %d\n", + __FUNCTION__, res->type); + break; + } + } + + VPRINTK("Passing txdmaq page pfn %lx\n", txdmaq_pfn); + accel_hw_priv->txdmaq_gnt = hwinfo->txdmaq_gnt = + net_accel_grant_page(bend->hdev_data, pfn_to_mfn(txdmaq_pfn), + 0); + + VPRINTK("Passing rxdmaq page pfn %lx\n", rxdmaq_pfn); + accel_hw_priv->rxdmaq_gnt = hwinfo->rxdmaq_gnt = + net_accel_grant_page(bend->hdev_data, pfn_to_mfn(rxdmaq_pfn), + 0); + + VPRINTK("Passing doorbell page mfn %x\n", hwinfo->doorbell_mfn); + /* Make the relevant H/W pages mappable by the far end */ + accel_hw_priv->doorbell_gnt = hwinfo->doorbell_gnt = + net_accel_grant_page(bend->hdev_data, hwinfo->doorbell_mfn, 1); + + /* Now do the same for the memory pages */ + /* Convert the page + length we got back for the evq to grants. */ + for (i = 0; i < accel_hw_priv->evq_npages; i++) { + accel_hw_priv->evq_mem_gnts[i] = hwinfo->evq_mem_gnts[i] = + net_accel_grant_page(bend->hdev_data, pfn_to_mfn(pfn), 0); + VPRINTK("Got grant %u for evq pfn %x\n", hwinfo->evq_mem_gnts[i], + pfn); + pfn++; + } + + return 0; +} + + +static int ef_bend_hwinfo_falcon_a(struct netback_accel *bend, + struct net_accel_hw_falcon_a *hwinfo) +{ + int rc; + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + + if ((rc = ef_bend_hwinfo_falcon_common(bend, &hwinfo->common)) != 0) + return rc; + + /* + * Note that unlike the above, where the message field is the + * page number, here evq_rptr is the entire address because + * it is currently a pointer into the densely mapped timer page. + */ + VPRINTK("Passing evq_rptr pfn %x for rptr %x\n", + hwinfo->common.evq_rptr >> PAGE_SHIFT, + hwinfo->common.evq_rptr); + rc = net_accel_grant_page(bend->hdev_data, + hwinfo->common.evq_rptr >> PAGE_SHIFT, 0); + if (rc < 0) + return rc; + + accel_hw_priv->evq_rptr_gnt = hwinfo->evq_rptr_gnt = rc; + VPRINTK("evq_rptr_gnt got %d\n", hwinfo->evq_rptr_gnt); + + return 0; +} + + +static int ef_bend_hwinfo_falcon_b(struct netback_accel *bend, + struct net_accel_hw_falcon_b *hwinfo) +{ + return ef_bend_hwinfo_falcon_common(bend, hwinfo); +} + + +/* + * Fill in the message with a description of the hardware resources, based on + * the H/W type + */ +static int netback_accel_hwinfo(struct netback_accel *bend, + struct net_accel_msg_hw *msgvi) +{ + int rc = 0; + + BUG_ON(bend->hw_state != NETBACK_ACCEL_RES_FILTER); + + msgvi->type = bend->hw_type; + switch (bend->hw_type) { + case NET_ACCEL_MSG_HWTYPE_FALCON_A: + rc = ef_bend_hwinfo_falcon_a(bend, &msgvi->resources.falcon_a); + break; + case NET_ACCEL_MSG_HWTYPE_FALCON_B: + rc = ef_bend_hwinfo_falcon_b(bend, &msgvi->resources.falcon_b); + break; + case NET_ACCEL_MSG_HWTYPE_NONE: + /* Nothing to do. The slow path should just work. */ + break; + } + + if (rc == 0) + bend->hw_state = NETBACK_ACCEL_RES_HWINFO; + + return rc; +} + + +/* Allocate hardware resources and make them available to the client domain */ +int netback_accel_setup_vnic_hw(struct netback_accel *bend) +{ + struct net_accel_msg msg; + int err; + + /* Allocate the event queue, VI and so on. */ + err = ef_get_vnic(bend); + if (err) { + EPRINTK("Failed to allocate hardware resource for bend:" + "error %d\n", err); + return err; + } + + /* Set up the filter management */ + err = netback_accel_filter_init(bend); + if (err) { + EPRINTK("Filter setup failed, error %d", err); + ef_free_vnic(bend); + return err; + } + + net_accel_msg_init(&msg, NET_ACCEL_MSG_SETHW); + + /* + * Extract the low-level hardware info we will actually pass to the + * other end, and set up the grants/ioremap permissions needed + */ + err = netback_accel_hwinfo(bend, &msg.u.hw); + + if (err != 0) { + netback_accel_filter_shutdown(bend); + ef_free_vnic(bend); + return err; + } + + /* Send the message, this is a reply to a hello-reply */ + err = net_accel_msg_reply_notify(bend->shared_page, + bend->msg_channel_irq, + &bend->to_domU, &msg); + + /* + * The message should succeed as it's logically a reply and we + * guarantee space for replies, but a misbehaving frontend + * could result in that behaviour, so be tolerant + */ + if (err != 0) { + netback_accel_release_hwinfo(bend); + netback_accel_filter_shutdown(bend); + ef_free_vnic(bend); + } + + return err; +} + + +/* Free hardware resources */ +void netback_accel_shutdown_vnic_hw(struct netback_accel *bend) +{ + /* + * Only try and release resources if accel_hw_priv was setup, + * otherwise there is nothing to do as we're on "null-op" + * acceleration + */ + switch (bend->hw_state) { + case NETBACK_ACCEL_RES_HWINFO: + VPRINTK("Release hardware resources\n"); + netback_accel_release_hwinfo(bend); + /* deliberate drop through */ + case NETBACK_ACCEL_RES_FILTER: + VPRINTK("Free filters...\n"); + netback_accel_filter_shutdown(bend); + /* deliberate drop through */ + case NETBACK_ACCEL_RES_ALLOC: + VPRINTK("Free vnic...\n"); + ef_free_vnic(bend); + /* deliberate drop through */ + case NETBACK_ACCEL_RES_NONE: + break; + default: + BUG(); + } +} + +/************************************************************************** + * + * Buffer table stuff + * + **************************************************************************/ + +/* + * Undo any allocation that netback_accel_msg_rx_buffer_map() has made + * if it fails half way through + */ +static inline void buffer_map_cleanup(struct netback_accel *bend, int i) +{ + while (i > 0) { + i--; + bend->buffer_maps_index--; + net_accel_unmap_device_page(bend->hdev_data, + bend->buffer_maps[bend->buffer_maps_index], + bend->buffer_addrs[bend->buffer_maps_index]); + } +} + + +int netback_accel_add_buffers(struct netback_accel *bend, int pages, int log2_pages, + u32 *grants, u32 *buf_addr_out) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + unsigned long long addr_array[NET_ACCEL_MSG_MAX_PAGE_REQ]; + int rc, i, index; + u64 dev_bus_addr; + + /* Make sure we can't overflow the dma_maps array */ + if (accel_hw_priv->dma_maps_index >= + bend->max_pages / NET_ACCEL_MSG_MAX_PAGE_REQ) { + EPRINTK("%s: too many buffer table allocations: %d %d\n", + __FUNCTION__, accel_hw_priv->dma_maps_index, + bend->max_pages / NET_ACCEL_MSG_MAX_PAGE_REQ); + return -EINVAL; + } + + /* Make sure we can't overflow the buffer_maps array */ + if (bend->buffer_maps_index + pages > bend->max_pages) { + EPRINTK("%s: too many pages mapped: %d + %d > %d\n", + __FUNCTION__, bend->buffer_maps_index, + pages, bend->max_pages); + return -EINVAL; + } + + for (i = 0; i < pages; i++) { + VPRINTK("%s: mapping page %d\n", __FUNCTION__, i); + rc = net_accel_map_device_page + (bend->hdev_data, grants[i], + &bend->buffer_maps[bend->buffer_maps_index], + &dev_bus_addr); + + if (rc != 0) { + EPRINTK("error in net_accel_map_device_page\n"); + buffer_map_cleanup(bend, i); + return rc; + } + + bend->buffer_addrs[bend->buffer_maps_index] = dev_bus_addr; + + bend->buffer_maps_index++; + + addr_array[i] = dev_bus_addr; + } + + VPRINTK("%s: mapping dma addresses to vih %p\n", __FUNCTION__, + accel_hw_priv->efx_vih); + + index = accel_hw_priv->dma_maps_index; + if ((rc = efx_vi_dma_map_addrs(accel_hw_priv->efx_vih, addr_array, pages, + &(accel_hw_priv->dma_maps[index]))) < 0) { + EPRINTK("error in dma_map_pages\n"); + buffer_map_cleanup(bend, i); + return rc; + } + + accel_hw_priv->dma_maps_index++; + NETBACK_ACCEL_STATS_OP(bend->stats.num_buffer_pages += pages); + + //DPRINTK("%s: getting map address\n", __FUNCTION__); + + *buf_addr_out = efx_vi_dma_get_map_addr(accel_hw_priv->efx_vih, + accel_hw_priv->dma_maps[index]); + + //DPRINTK("%s: done\n", __FUNCTION__); + + return 0; +} + + +int netback_accel_remove_buffers(struct netback_accel *bend) +{ + /* Only try to free buffers if accel_hw_priv was setup */ + if (bend->hw_state != NETBACK_ACCEL_RES_NONE) { + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + int i; + + efx_vi_reset(accel_hw_priv->efx_vih); + + while (accel_hw_priv->dma_maps_index > 0) { + accel_hw_priv->dma_maps_index--; + i = accel_hw_priv->dma_maps_index; + efx_vi_dma_unmap_addrs(accel_hw_priv->efx_vih, + accel_hw_priv->dma_maps[i]); + } + + while (bend->buffer_maps_index > 0) { + VPRINTK("Unmapping granted buffer %d\n", + bend->buffer_maps_index); + bend->buffer_maps_index--; + i = bend->buffer_maps_index; + net_accel_unmap_device_page(bend->hdev_data, + bend->buffer_maps[i], + bend->buffer_addrs[i]); + } + + NETBACK_ACCEL_STATS_OP(bend->stats.num_buffer_pages = 0); + } + + return 0; +} + +/************************************************************************** + * + * Filter stuff + * + **************************************************************************/ + +static int netback_accel_filter_init(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + int i, rc; + + BUG_ON(bend->hw_state != NETBACK_ACCEL_RES_ALLOC); + + spin_lock_init(&accel_hw_priv->filter_lock); + + if ((rc = cuckoo_hash_init(&accel_hw_priv->filter_hash_table, + 5 /* space for 32 filters */, 8)) != 0) { + EPRINTK("Failed to initialise filter hash table\n"); + return rc; + } + + accel_hw_priv->fspecs = kzalloc(sizeof(struct netback_accel_filter_spec) * + bend->quotas.max_filters, + GFP_KERNEL); + + if (accel_hw_priv->fspecs == NULL) { + EPRINTK("No memory for filter specs.\n"); + cuckoo_hash_destroy(&accel_hw_priv->filter_hash_table); + return -ENOMEM; + } + + for (i = 0; i < bend->quotas.max_filters; i++) { + accel_hw_priv->free_filters |= (1 << i); + } + + /* Base mask on highest set bit in max_filters */ + accel_hw_priv->filter_idx_mask = (1 << fls(bend->quotas.max_filters)) - 1; + VPRINTK("filter setup: max is %x mask is %x\n", + bend->quotas.max_filters, accel_hw_priv->filter_idx_mask); + + bend->hw_state = NETBACK_ACCEL_RES_FILTER; + + return 0; +} + + +static inline void make_filter_key(cuckoo_hash_ip_key *key, + struct netback_accel_filter_spec *filt) + +{ + key->local_ip = filt->destip_be; + key->local_port = filt->destport_be; + key->proto = filt->proto; +} + + +static inline +void netback_accel_free_filter(struct falcon_bend_accel_priv *accel_hw_priv, + int filter) +{ + cuckoo_hash_ip_key filter_key; + + if (!(accel_hw_priv->free_filters & (1 << filter))) { + efx_vi_filter_stop(accel_hw_priv->efx_vih, + accel_hw_priv->fspecs[filter].filter_handle); + make_filter_key(&filter_key, &(accel_hw_priv->fspecs[filter])); + if (cuckoo_hash_remove(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)&filter_key)) { + EPRINTK("%s: Couldn't find filter to remove from table\n", + __FUNCTION__); + BUG(); + } + } +} + + +static void netback_accel_filter_shutdown(struct netback_accel *bend) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + int i; + unsigned long flags; + + BUG_ON(bend->hw_state != NETBACK_ACCEL_RES_FILTER); + + spin_lock_irqsave(&accel_hw_priv->filter_lock, flags); + + BUG_ON(accel_hw_priv->fspecs == NULL); + + for (i = 0; i < bend->quotas.max_filters; i++) { + netback_accel_free_filter(accel_hw_priv, i); + } + + kfree(accel_hw_priv->fspecs); + accel_hw_priv->fspecs = NULL; + accel_hw_priv->free_filters = 0; + + cuckoo_hash_destroy(&accel_hw_priv->filter_hash_table); + + spin_unlock_irqrestore(&accel_hw_priv->filter_lock, flags); + + bend->hw_state = NETBACK_ACCEL_RES_ALLOC; +} + + +/*! Suggest a filter to replace when we want to insert a new one and have + * none free. + */ +static unsigned get_victim_filter(struct netback_accel *bend) +{ + /* + * We could attempt to get really clever, and may do at some + * point, but random replacement is v. cheap and low on + * pathological worst cases. + */ + unsigned index, cycles; + + rdtscl(cycles); + + /* + * Some doubt about the quality of the bottom few bits, so + * throw 'em * away + */ + index = (cycles >> 4) & ((struct falcon_bend_accel_priv *) + bend->accel_hw_priv)->filter_idx_mask; + /* + * We don't enforce that the number of filters is a power of + * two, but the masking gets us to within one subtraction of a + * valid index + */ + if (index >= bend->quotas.max_filters) + index -= bend->quotas.max_filters; + DPRINTK("backend %s->%d has no free filters. Filter %d will be evicted\n", + bend->nicname, bend->far_end, index); + return index; +} + + +/* Add a filter for the specified IP/port to the backend */ +int +netback_accel_filter_check_add(struct netback_accel *bend, + struct netback_accel_filter_spec *filt) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + struct netback_accel_filter_spec *fs; + unsigned filter_index; + unsigned long flags; + int rc, recycling = 0; + cuckoo_hash_ip_key filter_key, evict_key; + + BUG_ON(filt->proto != IPPROTO_TCP && filt->proto != IPPROTO_UDP); + + DPRINTK("Will add %s filter for dst ip %08x and dst port %d\n", + (filt->proto == IPPROTO_TCP) ? "TCP" : "UDP", + be32_to_cpu(filt->destip_be), be16_to_cpu(filt->destport_be)); + + spin_lock_irqsave(&accel_hw_priv->filter_lock, flags); + /* + * Check to see if we're already filtering this IP address and + * port. Happens if you insert a filter mid-stream as there + * are many packets backed up to be delivered to dom0 already + */ + make_filter_key(&filter_key, filt); + if (cuckoo_hash_lookup(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)(&filter_key), + &filter_index)) { + DPRINTK("Found matching filter %d already in table\n", + filter_index); + rc = -1; + goto out; + } + + if (accel_hw_priv->free_filters == 0) { + filter_index = get_victim_filter(bend); + recycling = 1; + } else { + filter_index = __ffs(accel_hw_priv->free_filters); + clear_bit(filter_index, &accel_hw_priv->free_filters); + } + + fs = &accel_hw_priv->fspecs[filter_index]; + + if (recycling) { + DPRINTK("Removing filter index %d handle %p\n", filter_index, + fs->filter_handle); + + if ((rc = efx_vi_filter_stop(accel_hw_priv->efx_vih, + fs->filter_handle)) != 0) { + EPRINTK("Couldn't clear NIC filter table entry %d\n", rc); + } + + make_filter_key(&evict_key, fs); + if (cuckoo_hash_remove(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)&evict_key)) { + EPRINTK("Couldn't find filter to remove from table\n"); + BUG(); + } + NETBACK_ACCEL_STATS_OP(bend->stats.num_filters--); + } + + /* Update the filter spec with new details */ + *fs = *filt; + + if ((rc = cuckoo_hash_add(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)&filter_key, filter_index, + 1)) != 0) { + EPRINTK("Error (%d) adding filter to table\n", rc); + accel_hw_priv->free_filters |= (1 << filter_index); + goto out; + } + + rc = efx_vi_filter(accel_hw_priv->efx_vih, filt->proto, filt->destip_be, + filt->destport_be, + (struct filter_resource_t **)&fs->filter_handle); + + if (rc != 0) { + EPRINTK("Hardware filter insertion failed. Error %d\n", rc); + accel_hw_priv->free_filters |= (1 << filter_index); + cuckoo_hash_remove(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)&filter_key); + rc = -1; + goto out; + } + + NETBACK_ACCEL_STATS_OP(bend->stats.num_filters++); + + VPRINTK("%s: success index %d handle %p\n", __FUNCTION__, filter_index, + fs->filter_handle); + + rc = filter_index; + out: + spin_unlock_irqrestore(&accel_hw_priv->filter_lock, flags); + return rc; +} + + +/* Remove a filter entry for the specific device and IP/port */ +static void netback_accel_filter_remove(struct netback_accel *bend, + int filter_index) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + + BUG_ON(accel_hw_priv->free_filters & (1 << filter_index)); + netback_accel_free_filter(accel_hw_priv, filter_index); + accel_hw_priv->free_filters |= (1 << filter_index); +} + + +/* Remove a filter entry for the specific device and IP/port */ +void netback_accel_filter_remove_spec(struct netback_accel *bend, + struct netback_accel_filter_spec *filt) +{ + struct falcon_bend_accel_priv *accel_hw_priv = bend->accel_hw_priv; + unsigned filter_found; + unsigned long flags; + cuckoo_hash_ip_key filter_key; + struct netback_accel_filter_spec *fs; + + if (filt->proto == IPPROTO_TCP) { + DPRINTK("Remove TCP filter for dst ip %08x and dst port %d\n", + be32_to_cpu(filt->destip_be), + be16_to_cpu(filt->destport_be)); + } else if (filt->proto == IPPROTO_UDP) { + DPRINTK("Remove UDP filter for dst ip %08x and dst port %d\n", + be32_to_cpu(filt->destip_be), + be16_to_cpu(filt->destport_be)); + } else { + /* + * This could be provoked by an evil frontend, so can't + * BUG(), but harmless as it should fail tests below + */ + DPRINTK("Non-TCP/UDP filter dst ip %08x and dst port %d\n", + be32_to_cpu(filt->destip_be), + be16_to_cpu(filt->destport_be)); + } + + spin_lock_irqsave(&accel_hw_priv->filter_lock, flags); + + make_filter_key(&filter_key, filt); + if (!cuckoo_hash_lookup(&accel_hw_priv->filter_hash_table, + (cuckoo_hash_key *)(&filter_key), + &filter_found)) { + EPRINTK("Couldn't find matching filter already in table\n"); + goto out; + } + + /* Do a full check to make sure we've not had a hash collision */ + fs = &accel_hw_priv->fspecs[filter_found]; + if (fs->destip_be == filt->destip_be && + fs->destport_be == filt->destport_be && + fs->proto == filt->proto && + !memcmp(fs->mac, filt->mac, ETH_ALEN)) { + netback_accel_filter_remove(bend, filter_found); + } else { + EPRINTK("Entry in hash table does not match filter spec\n"); + goto out; + } + + out: + spin_unlock_irqrestore(&accel_hw_priv->filter_lock, flags); +} diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_solarflare.h --- /dev/null +++ b/drivers/xen/sfc_netback/accel_solarflare.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef NETBACK_ACCEL_SOLARFLARE_H +#define NETBACK_ACCEL_SOLARFLARE_H + +#include "accel.h" +#include "accel_msg_iface.h" + +#include "driverlink_api.h" + +#define MAX_NICS 5 +#define MAX_PORTS 2 + + +extern int netback_accel_sf_init(void); +extern void netback_accel_sf_shutdown(void); +extern int netback_accel_sf_hwtype(struct netback_accel *bend); + +extern int netback_accel_sf_char_init(void); +extern void netback_accel_sf_char_shutdown(void); + +extern int netback_accel_setup_vnic_hw(struct netback_accel *bend); +extern void netback_accel_shutdown_vnic_hw(struct netback_accel *bend); + +extern int netback_accel_add_buffers(struct netback_accel *bend, int pages, + int log2_pages, u32 *grants, + u32 *buf_addr_out); +extern int netback_accel_remove_buffers(struct netback_accel *bend); + + +/* Add a filter for the specified IP/port to the backend */ +extern int +netback_accel_filter_check_add(struct netback_accel *bend, + struct netback_accel_filter_spec *filt); +/* Remove a filter entry for the specific device and IP/port */ +extern +void netback_accel_filter_remove_index(struct netback_accel *bend, + int filter_index); +extern +void netback_accel_filter_remove_spec(struct netback_accel *bend, + struct netback_accel_filter_spec *filt); + +/* This is designed to look a bit like a skb */ +struct netback_pkt_buf { + union { + unsigned char *raw; + } mac; + union { + struct iphdr *iph; + struct arphdr *arph; + unsigned char *raw; + } nh; + int protocol; +}; + +/*! \brief Handle a received packet: insert fast path filters as necessary + * \param skb The packet buffer + */ +extern void netback_accel_rx_packet(struct netback_pkt_buf *skb, void *fwd_priv); + +/*! \brief Handle a transmitted packet: update fast path filters as necessary + * \param skb The packet buffer + */ +extern void netback_accel_tx_packet(struct sk_buff *skb, void *fwd_priv); + +#endif /* NETBACK_ACCEL_SOLARFLARE_H */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/accel_xenbus.c --- /dev/null +++ b/drivers/xen/sfc_netback/accel_xenbus.c @@ -0,0 +1,831 @@ +/**************************************************************************** + * Solarflare driver for Xen network acceleration + * + * Copyright 2006-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#include +#include + +/* drivers/xen/netback/common.h */ +#include "common.h" + +#include "accel.h" +#include "accel_solarflare.h" +#include "accel_util.h" + +#define NODENAME_PATH_FMT "backend/vif/%d/%d" + +#define NETBACK_ACCEL_FROM_XENBUS_DEVICE(_dev) (struct netback_accel *) \ + ((struct backend_info *)(_dev)->dev.driver_data)->netback_accel_priv + +/* List of all the bends currently in existence. */ +struct netback_accel *bend_list = NULL; +DEFINE_MUTEX(bend_list_mutex); + +/* Put in bend_list. Must hold bend_list_mutex */ +static void link_bend(struct netback_accel *bend) +{ + bend->next_bend = bend_list; + bend_list = bend; +} + +/* Remove from bend_list, Must hold bend_list_mutex */ +static void unlink_bend(struct netback_accel *bend) +{ + struct netback_accel *tmp = bend_list; + struct netback_accel *prev = NULL; + while (tmp != NULL) { + if (tmp == bend) { + if (prev != NULL) + prev->next_bend = bend->next_bend; + else + bend_list = bend->next_bend; + return; + } + prev = tmp; + tmp = tmp->next_bend; + } +} + + +/* Demultiplex a message IRQ from the frontend driver. */ +static irqreturn_t msgirq_from_frontend(int irq, void *context, + struct pt_regs *unused) +{ + struct xenbus_device *dev = context; + struct netback_accel *bend = NETBACK_ACCEL_FROM_XENBUS_DEVICE(dev); + VPRINTK("irq %d from device %s\n", irq, dev->nodename); + schedule_work(&bend->handle_msg); + return IRQ_HANDLED; +} + + +/* + * Demultiplex an IRQ from the frontend driver. This is never used + * functionally, but we need it to pass to the bind function, and may + * get called spuriously + */ +static irqreturn_t netirq_from_frontend(int irq, void *context, + struct pt_regs *unused) +{ + VPRINTK("netirq %d from device %s\n", irq, + ((struct xenbus_device *)context)->nodename); + + return IRQ_HANDLED; +} + + +/* Read the limits values of the xenbus structure. */ +static +void cfg_hw_quotas(struct xenbus_device *dev, struct netback_accel *bend) +{ + int err = xenbus_gather + (XBT_NIL, dev->nodename, + "limits/max-filters", "%d", &bend->quotas.max_filters, + "limits/max-buf-pages", "%d", &bend->quotas.max_buf_pages, + "limits/max-mcasts", "%d", &bend->quotas.max_mcasts, + NULL); + if (err) { + /* + * TODO what if they have previously been set by the + * user? This will overwrite with defaults. Maybe + * not what we want to do, but useful in startup + * case + */ + DPRINTK("Failed to read quotas from xenbus, using defaults\n"); + bend->quotas.max_filters = NETBACK_ACCEL_DEFAULT_MAX_FILTERS; + bend->quotas.max_buf_pages = max_pages; + bend->quotas.max_mcasts = NETBACK_ACCEL_DEFAULT_MAX_MCASTS; + } + + return; +} + + +static void bend_config_accel_change(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + struct netback_accel *bend; + + bend = container_of(watch, struct netback_accel, config_accel_watch); + + mutex_lock(&bend->bend_mutex); + if (bend->config_accel_watch.node != NULL) { + struct xenbus_device *dev = + (struct xenbus_device *)bend->hdev_data; + DPRINTK("Watch matched, got dev %p otherend %p\n", + dev, dev->otherend); + if(!xenbus_exists(XBT_NIL, watch->node, "")) { + DPRINTK("Ignoring watch as otherend seems invalid\n"); + goto out; + } + + cfg_hw_quotas(dev, bend); + } + out: + mutex_unlock(&bend->bend_mutex); + return; +} + + +/* + * Setup watch on "limits" in the backend vif info to know when + * configuration has been set + */ +static int setup_config_accel_watch(struct xenbus_device *dev, + struct netback_accel *bend) +{ + int err; + + VPRINTK("Setting watch on %s/%s\n", dev->nodename, "limits"); + + err = xenbus_watch_path2(dev, dev->nodename, "limits", + &bend->config_accel_watch, + bend_config_accel_change); + + if (err) { + EPRINTK("%s: Failed to register xenbus watch: %d\n", + __FUNCTION__, err); + bend->config_accel_watch.node = NULL; + return err; + } + return 0; +} + + +static int +cfg_frontend_info(struct xenbus_device *dev, struct netback_accel *bend, + int *grants) +{ + /* Get some info from xenbus on the event channel and shmem grant */ + int err = xenbus_gather(XBT_NIL, dev->otherend, + "accel-msg-channel", "%u", &bend->msg_channel, + "accel-ctrl-page", "%d", &(grants[0]), + "accel-msg-page", "%d", &(grants[1]), + "accel-net-channel", "%u", &bend->net_channel, + NULL); + if (err) + EPRINTK("failed to read event channels or shmem grant: %d\n", + err); + else + DPRINTK("got event chan %d and net chan %d from frontend\n", + bend->msg_channel, bend->net_channel); + return err; +} + + +/* Setup all the comms needed to chat with the front end driver */ +static int setup_vnic(struct xenbus_device *dev) +{ + struct netback_accel *bend; + int grants[2], err, msgs_per_queue; + + bend = NETBACK_ACCEL_FROM_XENBUS_DEVICE(dev); + + err = cfg_frontend_info(dev, bend, grants); + if (err) + goto fail1; + + /* + * If we get here, both frontend Connected and configuration + * options available. All is well. + */ + + /* Get the hardware quotas for the VNIC in question. */ + cfg_hw_quotas(dev, bend); + + /* Set up the deferred work handlers */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + INIT_WORK(&bend->handle_msg, + netback_accel_msg_rx_handler); +#else + INIT_WORK(&bend->handle_msg, + netback_accel_msg_rx_handler, + (void*)bend); +#endif + + /* Request the frontend mac */ + err = net_accel_xen_net_read_mac(dev, bend->mac); + if (err) + goto fail2; + + /* Set up the shared page. */ + bend->shared_page = net_accel_map_grants_contig(dev, grants, 2, + &bend->sh_pages_unmap); + + if (bend->shared_page == NULL) { + EPRINTK("failed to map shared page for %s\n", dev->otherend); + err = -ENOMEM; + goto fail2; + } + + /* Initialise the shared page(s) used for comms */ + net_accel_msg_init_page(bend->shared_page, PAGE_SIZE, + bend->net_dev->flags & IFF_UP); + + msgs_per_queue = (PAGE_SIZE/2) / sizeof(struct net_accel_msg); + + net_accel_msg_init_queue + (&bend->to_domU, &bend->shared_page->queue0, + (struct net_accel_msg *)((__u8*)bend->shared_page + PAGE_SIZE), + msgs_per_queue); + + net_accel_msg_init_queue + (&bend->from_domU, &bend->shared_page->queue1, + (struct net_accel_msg *)((__u8*)bend->shared_page + + (3 * PAGE_SIZE / 2)), + msgs_per_queue); + + /* Bind the message event channel to a handler + * + * Note that we will probably get a spurious interrupt when we + * do this, so it must not be done until we have set up + * everything we need to handle it. + */ + err = bind_interdomain_evtchn_to_irqhandler(dev->otherend_id, + bend->msg_channel, + msgirq_from_frontend, + 0, + "netback_accel", + dev); + if (err < 0) { + EPRINTK("failed to bind event channel: %d\n", err); + goto fail3; + } + else + bend->msg_channel_irq = err; + + /* TODO: No need to bind this evtchn to an irq. */ + err = bind_interdomain_evtchn_to_irqhandler(dev->otherend_id, + bend->net_channel, + netirq_from_frontend, + 0, + "netback_accel", + dev); + if (err < 0) { + EPRINTK("failed to bind net channel: %d\n", err); + goto fail4; + } + else + bend->net_channel_irq = err; + + /* + * Grab ourselves an entry in the forwarding hash table. We do + * this now so we don't have the embarassmesnt of sorting out + * an allocation failure while at IRQ. Because we pass NULL as + * the context, the actual hash lookup will succeed for this + * NIC, but the check for somewhere to forward to will + * fail. This is necessary to prevent forwarding before + * hardware resources are set up + */ + err = netback_accel_fwd_add(bend->mac, NULL, bend->fwd_priv); + if (err) { + EPRINTK("failed to add to fwd hash table\n"); + goto fail5; + } + + /* + * Say hello to frontend. Important to do this straight after + * obtaining the message queue as otherwise we are vulnerable + * to an evil frontend sending a HELLO-REPLY before we've sent + * the HELLO and confusing us + */ + netback_accel_msg_tx_hello(bend, NET_ACCEL_MSG_VERSION); + return 0; + + fail5: + unbind_from_irqhandler(bend->net_channel_irq, dev); + fail4: + unbind_from_irqhandler(bend->msg_channel_irq, dev); + fail3: + net_accel_unmap_grants_contig(dev, bend->sh_pages_unmap); + bend->shared_page = NULL; + bend->sh_pages_unmap = NULL; + fail2: + fail1: + return err; +} + + +static int read_nicname(struct xenbus_device *dev, struct netback_accel *bend) +{ + int len; + + /* nic name used to select interface used for acceleration */ + bend->nicname = xenbus_read(XBT_NIL, dev->nodename, "accel", &len); + if (IS_ERR(bend->nicname)) + return PTR_ERR(bend->nicname); + + return 0; +} + +static const char *frontend_name = "sfc_netfront"; + +static int publish_frontend_name(struct xenbus_device *dev) +{ + struct xenbus_transaction tr; + int err; + + /* Publish the name of the frontend driver */ + do { + err = xenbus_transaction_start(&tr); + if (err != 0) { + EPRINTK("%s: transaction start failed\n", __FUNCTION__); + return err; + } + err = xenbus_printf(tr, dev->nodename, "accel-frontend", + "%s", frontend_name); + if (err != 0) { + EPRINTK("%s: xenbus_printf failed\n", __FUNCTION__); + xenbus_transaction_end(tr, 1); + return err; + } + err = xenbus_transaction_end(tr, 0); + } while (err == -EAGAIN); + + if (err != 0) { + EPRINTK("failed to end frontend name transaction\n"); + return err; + } + return 0; +} + + +static int unpublish_frontend_name(struct xenbus_device *dev) +{ + struct xenbus_transaction tr; + int err; + + do { + err = xenbus_transaction_start(&tr); + if (err != 0) + break; + err = xenbus_rm(tr, dev->nodename, "accel-frontend"); + if (err != 0) { + xenbus_transaction_end(tr, 1); + break; + } + err = xenbus_transaction_end(tr, 0); + } while (err == -EAGAIN); + + return err; +} + + +static void cleanup_vnic(struct netback_accel *bend) +{ + struct xenbus_device *dev; + + dev = (struct xenbus_device *)bend->hdev_data; + + DPRINTK("%s: bend %p dev %p\n", __FUNCTION__, bend, dev); + + DPRINTK("%s: Remove %p's mac from fwd table...\n", + __FUNCTION__, bend); + netback_accel_fwd_remove(bend->mac, bend->fwd_priv); + + /* Free buffer table allocations */ + netback_accel_remove_buffers(bend); + + DPRINTK("%s: Release hardware resources...\n", __FUNCTION__); + if (bend->accel_shutdown) + bend->accel_shutdown(bend); + + if (bend->net_channel_irq) { + unbind_from_irqhandler(bend->net_channel_irq, dev); + bend->net_channel_irq = 0; + } + + if (bend->msg_channel_irq) { + unbind_from_irqhandler(bend->msg_channel_irq, dev); + bend->msg_channel_irq = 0; + } + + if (bend->sh_pages_unmap) { + DPRINTK("%s: Unmap grants %p\n", __FUNCTION__, + bend->sh_pages_unmap); + net_accel_unmap_grants_contig(dev, bend->sh_pages_unmap); + bend->sh_pages_unmap = NULL; + bend->shared_page = NULL; + } +} + + +/*************************************************************************/ + +/* + * The following code handles accelstate changes between the frontend + * and the backend. It calls setup_vnic and cleanup_vnic in matching + * pairs in response to transitions. + * + * Valid state transitions for Dom0 are as follows: + * + * Closed->Init on probe or in response to Init from domU + * Closed->Closing on error/remove + * + * Init->Connected in response to Connected from domU + * Init->Closing on error/remove or in response to Closing from domU + * + * Connected->Closing on error/remove or in response to Closing from domU + * + * Closing->Closed in response to Closed from domU + * + */ + + +static void netback_accel_frontend_changed(struct xenbus_device *dev, + XenbusState frontend_state) +{ + struct netback_accel *bend = NETBACK_ACCEL_FROM_XENBUS_DEVICE(dev); + XenbusState backend_state; + + DPRINTK("%s: changing from %s to %s. nodename %s, otherend %s\n", + __FUNCTION__, xenbus_strstate(bend->frontend_state), + xenbus_strstate(frontend_state),dev->nodename, dev->otherend); + + /* + * Ignore duplicate state changes. This can happen if the + * frontend changes state twice in quick succession and the + * first watch fires in the backend after the second + * transition has completed. + */ + if (bend->frontend_state == frontend_state) + return; + + bend->frontend_state = frontend_state; + backend_state = bend->backend_state; + + switch (frontend_state) { + case XenbusStateInitialising: + if (backend_state == XenbusStateClosed && + !bend->removing) + backend_state = XenbusStateInitialising; + break; + + case XenbusStateConnected: + if (backend_state == XenbusStateInitialising) { + if (!bend->vnic_is_setup && + setup_vnic(dev) == 0) { + bend->vnic_is_setup = 1; + backend_state = XenbusStateConnected; + } else { + backend_state = XenbusStateClosing; + } + } + break; + + case XenbusStateInitWait: + case XenbusStateInitialised: + default: + DPRINTK("Unknown state %s (%d) from frontend.\n", + xenbus_strstate(frontend_state), frontend_state); + /* Unknown state. Fall through. */ + case XenbusStateClosing: + if (backend_state != XenbusStateClosed) + backend_state = XenbusStateClosing; + + /* + * The bend will now persist (with watches active) in + * case the frontend comes back again, eg. after + * frontend module reload or suspend/resume + */ + + break; + + case XenbusStateUnknown: + case XenbusStateClosed: + if (bend->vnic_is_setup) { + bend->vnic_is_setup = 0; + cleanup_vnic(bend); + } + + if (backend_state == XenbusStateClosing) + backend_state = XenbusStateClosed; + break; + } + + if (backend_state != bend->backend_state) { + DPRINTK("Switching from state %s (%d) to %s (%d)\n", + xenbus_strstate(bend->backend_state), + bend->backend_state, + xenbus_strstate(backend_state), backend_state); + bend->backend_state = backend_state; + net_accel_update_state(dev, backend_state); + } + + wake_up(&bend->state_wait_queue); +} + + +/* accelstate on the frontend's xenbus node has changed */ +static void bend_domu_accel_change(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + int state; + struct netback_accel *bend; + + bend = container_of(watch, struct netback_accel, domu_accel_watch); + if (bend->domu_accel_watch.node != NULL) { + struct xenbus_device *dev = + (struct xenbus_device *)bend->hdev_data; + VPRINTK("Watch matched, got dev %p otherend %p\n", + dev, dev->otherend); + /* + * dev->otherend != NULL check to protect against + * watch firing when domain goes away and we haven't + * yet cleaned up + */ + if (!dev->otherend || + !xenbus_exists(XBT_NIL, watch->node, "") || + strncmp(dev->otherend, vec[XS_WATCH_PATH], + strlen(dev->otherend))) { + DPRINTK("Ignoring watch as otherend seems invalid\n"); + return; + } + + mutex_lock(&bend->bend_mutex); + + xenbus_scanf(XBT_NIL, dev->otherend, "accelstate", "%d", + &state); + netback_accel_frontend_changed(dev, state); + + mutex_unlock(&bend->bend_mutex); + } +} + +/* Setup watch on frontend's accelstate */ +static int setup_domu_accel_watch(struct xenbus_device *dev, + struct netback_accel *bend) +{ + int err; + + VPRINTK("Setting watch on %s/%s\n", dev->otherend, "accelstate"); + + err = xenbus_watch_path2(dev, dev->otherend, "accelstate", + &bend->domu_accel_watch, + bend_domu_accel_change); + if (err) { + EPRINTK("%s: Failed to register xenbus watch: %d\n", + __FUNCTION__, err); + goto fail; + } + return 0; + fail: + bend->domu_accel_watch.node = NULL; + return err; +} + + +int netback_accel_probe(struct xenbus_device *dev) +{ + struct netback_accel *bend; + struct backend_info *binfo; + int err; + + DPRINTK("%s: passed device %s\n", __FUNCTION__, dev->nodename); + + /* Allocate structure to store all our state... */ + bend = kzalloc(sizeof(struct netback_accel), GFP_KERNEL); + if (bend == NULL) { + DPRINTK("%s: no memory for bend\n", __FUNCTION__); + return -ENOMEM; + } + + mutex_init(&bend->bend_mutex); + + mutex_lock(&bend->bend_mutex); + + /* ...and store it where we can get at it */ + binfo = (struct backend_info *) dev->dev.driver_data; + binfo->netback_accel_priv = bend; + /* And vice-versa */ + bend->hdev_data = dev; + + DPRINTK("%s: Adding bend %p to list\n", __FUNCTION__, bend); + + init_waitqueue_head(&bend->state_wait_queue); + bend->vnic_is_setup = 0; + bend->frontend_state = XenbusStateUnknown; + bend->backend_state = XenbusStateClosed; + bend->removing = 0; + + sscanf(dev->nodename, NODENAME_PATH_FMT, &bend->far_end, + &bend->vif_num); + + err = read_nicname(dev, bend); + if (err) { + /* + * Technically not an error, just means we're not + * supposed to accelerate this + */ + DPRINTK("failed to get device name\n"); + goto fail_nicname; + } + + /* + * Look up the device name in the list of NICs provided by + * driverlink to get the hardware type. + */ + err = netback_accel_sf_hwtype(bend); + if (err) { + /* + * Technically not an error, just means we're not + * supposed to accelerate this, probably belongs to + * some other backend + */ + DPRINTK("failed to match device name\n"); + goto fail_init_type; + } + + err = publish_frontend_name(dev); + if (err) + goto fail_publish; + + err = netback_accel_debugfs_create(bend); + if (err) + goto fail_debugfs; + + mutex_unlock(&bend->bend_mutex); + + err = setup_config_accel_watch(dev, bend); + if (err) + goto fail_config_watch; + + err = setup_domu_accel_watch(dev, bend); + if (err) + goto fail_domu_watch; + + /* + * Indicate to the other end that we're ready to start unless + * the watch has already fired. + */ + mutex_lock(&bend->bend_mutex); + if (bend->backend_state == XenbusStateClosed) { + bend->backend_state = XenbusStateInitialising; + net_accel_update_state(dev, XenbusStateInitialising); + } + mutex_unlock(&bend->bend_mutex); + + mutex_lock(&bend_list_mutex); + link_bend(bend); + mutex_unlock(&bend_list_mutex); + + return 0; + +fail_domu_watch: + + unregister_xenbus_watch(&bend->config_accel_watch); + kfree(bend->config_accel_watch.node); +fail_config_watch: + + /* + * Flush the scheduled work queue before freeing bend to get + * rid of any pending netback_accel_msg_rx_handler() + */ + flush_scheduled_work(); + + mutex_lock(&bend->bend_mutex); + net_accel_update_state(dev, XenbusStateUnknown); + netback_accel_debugfs_remove(bend); +fail_debugfs: + + unpublish_frontend_name(dev); +fail_publish: + + /* No need to reverse netback_accel_sf_hwtype. */ +fail_init_type: + + kfree(bend->nicname); +fail_nicname: + binfo->netback_accel_priv = NULL; + mutex_unlock(&bend->bend_mutex); + kfree(bend); + return err; +} + + +int netback_accel_remove(struct xenbus_device *dev) +{ + struct backend_info *binfo; + struct netback_accel *bend; + int frontend_state; + + binfo = (struct backend_info *) dev->dev.driver_data; + bend = (struct netback_accel *) binfo->netback_accel_priv; + + DPRINTK("%s: dev %p bend %p\n", __FUNCTION__, dev, bend); + + BUG_ON(bend == NULL); + + mutex_lock(&bend_list_mutex); + unlink_bend(bend); + mutex_unlock(&bend_list_mutex); + + mutex_lock(&bend->bend_mutex); + + /* Reject any requests to connect. */ + bend->removing = 1; + + /* + * Switch to closing to tell the other end that we're going + * away. + */ + if (bend->backend_state != XenbusStateClosing) { + bend->backend_state = XenbusStateClosing; + net_accel_update_state(dev, XenbusStateClosing); + } + + frontend_state = (int)XenbusStateUnknown; + xenbus_scanf(XBT_NIL, dev->otherend, "accelstate", "%d", + &frontend_state); + + mutex_unlock(&bend->bend_mutex); + + /* + * Wait until this end goes to the closed state. This happens + * in response to the other end going to the closed state. + * Don't bother doing this if the other end is already closed + * because if it is then there is nothing to do. + */ + if (frontend_state != (int)XenbusStateClosed && + frontend_state != (int)XenbusStateUnknown) + wait_event(bend->state_wait_queue, + bend->backend_state == XenbusStateClosed); + + unregister_xenbus_watch(&bend->domu_accel_watch); + kfree(bend->domu_accel_watch.node); + + unregister_xenbus_watch(&bend->config_accel_watch); + kfree(bend->config_accel_watch.node); + + /* + * Flush the scheduled work queue before freeing bend to get + * rid of any pending netback_accel_msg_rx_handler() + */ + flush_scheduled_work(); + + mutex_lock(&bend->bend_mutex); + + /* Tear down the vnic if it was set up. */ + if (bend->vnic_is_setup) { + bend->vnic_is_setup = 0; + cleanup_vnic(bend); + } + + bend->backend_state = XenbusStateUnknown; + net_accel_update_state(dev, XenbusStateUnknown); + + netback_accel_debugfs_remove(bend); + + unpublish_frontend_name(dev); + + kfree(bend->nicname); + + binfo->netback_accel_priv = NULL; + + mutex_unlock(&bend->bend_mutex); + + kfree(bend); + + return 0; +} + + +void netback_accel_shutdown_bends(void) +{ + mutex_lock(&bend_list_mutex); + /* + * I think we should have had a remove callback for all + * interfaces before being allowed to unload the module + */ + BUG_ON(bend_list != NULL); + mutex_unlock(&bend_list_mutex); +} + + +void netback_accel_set_closing(struct netback_accel *bend) +{ + + bend->backend_state = XenbusStateClosing; + net_accel_update_state((struct xenbus_device *)bend->hdev_data, + XenbusStateClosing); +} diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/* + * \author djr + * \brief Compatability layer. Provides definitions of fundamental + * types and definitions that are used throughout CI source + * code. It does not introduce any link time dependencies, + * or include any unnecessary system headers. + */ +/*! \cidoxg_include_ci */ + +#ifndef __CI_COMPAT_H__ +#define __CI_COMPAT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +#ifdef __cplusplus +} +#endif + +#endif /* __CI_COMPAT_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/gcc.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/gcc.h @@ -0,0 +1,158 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_GCC_H__ +#define __CI_COMPAT_GCC_H__ + + +#define CI_HAVE_INT64 + + +#if defined(__linux__) && defined(__KERNEL__) + +# include + +typedef __u64 ci_uint64; +typedef __s64 ci_int64; +# if BITS_PER_LONG == 32 +typedef __s32 ci_ptr_arith_t; +typedef __u32 ci_uintptr_t; +# else +typedef __s64 ci_ptr_arith_t; +typedef __u64 ci_uintptr_t; +# endif + + +/* it's not obvious to me why the below is wrong for x64_64, but + * gcc seems to complain on this platform + */ +# if defined(__ia64__) +# define CI_PRId64 "ld" +# define CI_PRIi64 "li" +# define CI_PRIo64 "lo" +# define CI_PRIu64 "lu" +# define CI_PRIx64 "lx" +# define CI_PRIX64 "lX" +# else +# define CI_PRId64 "lld" +# define CI_PRIi64 "lli" +# define CI_PRIo64 "llo" +# define CI_PRIu64 "llu" +# define CI_PRIx64 "llx" +# define CI_PRIX64 "llX" +# endif + +# define CI_PRId32 "d" +# define CI_PRIi32 "i" +# define CI_PRIo32 "o" +# define CI_PRIu32 "u" +# define CI_PRIx32 "x" +# define CI_PRIX32 "X" + +#else + +# include +# include + +typedef uint64_t ci_uint64; +typedef int64_t ci_int64; +typedef intptr_t ci_ptr_arith_t; +typedef uintptr_t ci_uintptr_t; + +# define CI_PRId64 PRId64 +# define CI_PRIi64 PRIi64 +# define CI_PRIo64 PRIo64 +# define CI_PRIu64 PRIu64 +# define CI_PRIx64 PRIx64 +# define CI_PRIX64 PRIX64 + +# define CI_PRId32 PRId32 +# define CI_PRIi32 PRIi32 +# define CI_PRIo32 PRIo32 +# define CI_PRIu32 PRIu32 +# define CI_PRIx32 PRIx32 +# define CI_PRIX32 PRIX32 + +#endif + + +typedef ci_uint64 ci_fixed_descriptor_t; + +#define from_fixed_descriptor(desc) ((ci_uintptr_t)(desc)) +#define to_fixed_descriptor(desc) ((ci_fixed_descriptor_t)(ci_uintptr_t)(desc)) + + +#if __GNUC__ >= 3 && !defined(__cplusplus) +/* +** Checks that [p_mbr] has the same type as [&c_type::mbr_name]. +*/ +# define CI_CONTAINER(c_type, mbr_name, p_mbr) \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(__typeof__(&((c_type*)0)->mbr_name), \ + __typeof__(p_mbr)), \ + __CI_CONTAINER(c_type, mbr_name, p_mbr), (void)0) + +# define ci_restrict __restrict__ +#endif + + +#if !defined(__KERNEL__) || defined(__unix__) +#define CI_HAVE_NPRINTF 1 +#endif + + +/* At what version was this introduced? */ +#if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ > 91) +# define CI_LIKELY(t) __builtin_expect((t), 1) +# define CI_UNLIKELY(t) __builtin_expect((t), 0) +#endif + +/********************************************************************** + * Attributes + */ +#if __GNUC__ >= 3 && defined(NDEBUG) +# define CI_HF __attribute__((visibility("hidden"))) +# define CI_HV __attribute__((visibility("hidden"))) +#else +# define CI_HF +# define CI_HV +#endif + +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define ci_noinline static __attribute__((__noinline__)) +/* (Linux 2.6 defines its own "noinline", so we use the "__noinline__" form) */ +#else +# define ci_noinline static +#endif + +#define CI_ALIGN(x) __attribute__ ((aligned (x))) + +#define CI_PRINTF_LIKE(a,b) __attribute__((format(printf,a,b))) + +#endif /* __CI_COMPAT_GCC_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/gcc_x86.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/gcc_x86.h @@ -0,0 +1,115 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_GCC_X86_H__ +#define __CI_COMPAT_GCC_X86_H__ + +/* +** The facts: +** +** SSE sfence +** SSE2 lfence, mfence, pause +*/ + +/* + Barriers to enforce ordering with respect to: + + normal memory use: ci_wmb, ci_rmb, ci_wmb + IO bus access use: ci_wiob, ci_riob, ci_iob +*/ +#if defined(__x86_64__) +# define ci_x86_mb() __asm__ __volatile__ ("lock; addl $0,0(%%rsp)":::"memory") +#else +# define ci_x86_mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)":::"memory") +#endif + +/* ?? measure the impact of latency of sfence on a modern processor before we + take a decision on how to integrate with respect to writecombining */ + +/* DJR: I don't think we need to add "memory" here. It means the asm does +** something to memory that GCC doesn't understand. But all this does is +** commit changes that GCC thinks have already happened. NB. GCC will not +** reorder across a __volatile__ __asm__ anyway. +*/ +#define ci_gcc_fence() __asm__ __volatile__ ("") + +#if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) +# define ci_x86_sfence() __asm__ __volatile__ ("sfence") +# define ci_x86_lfence() __asm__ __volatile__ ("lfence") +# define ci_x86_mfence() __asm__ __volatile__ ("mfence") +#else +# define ci_x86_sfence() __asm__ __volatile__ (".byte 0x0F, 0xAE, 0xF8") +# define ci_x86_lfence() __asm__ __volatile__ (".byte 0x0F, 0xAE, 0xE8") +# define ci_x86_mfence() __asm__ __volatile__ (".byte 0x0F, 0xAE, 0xF0") +#endif + + +/* x86 processors to P4 Xeon store in-order unless executing streaming + extensions or when using writecombining + + Hence we do not define ci_wmb to use sfence by default. Requirement is that + we do not use writecombining to memory and any code which uses SSE + extensions must call sfence directly + + We need to track non intel clones which may support out of order store. + +*/ + +#if CI_CPU_OOS +# if CI_CPU_HAS_SSE +# define ci_wmb() ci_x86_sfence() +# else +# define ci_wmb() ci_x86_mb() +# endif +#else +# define ci_wmb() ci_gcc_fence() +#endif + +#if CI_CPU_HAS_SSE2 +# define ci_rmb() ci_x86_lfence() +# define ci_mb() ci_x86_mfence() +# define ci_riob() ci_x86_lfence() +# define ci_wiob() ci_x86_sfence() +# define ci_iob() ci_x86_mfence() +#else +# if CI_CPU_HAS_SSE +# define ci_wiob() ci_x86_sfence() +# else +# define ci_wiob() ci_x86_mb() +# endif +# define ci_rmb() ci_x86_mb() +# define ci_mb() ci_x86_mb() +# define ci_riob() ci_x86_mb() +# define ci_iob() ci_x86_mb() +#endif + +typedef unsigned long ci_phys_addr_t; +#define ci_phys_addr_fmt "%lx" + +#endif /* __CI_COMPAT_GCC_X86_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/primitive.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/primitive.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_PRIMITIVE_H__ +#define __CI_COMPAT_PRIMITIVE_H__ + + +/********************************************************************** + * Primitive types. + */ + +typedef unsigned char ci_uint8; +typedef char ci_int8; + +typedef unsigned short ci_uint16; +typedef short ci_int16; + +typedef unsigned int ci_uint32; +typedef int ci_int32; + +/* 64-bit support is platform dependent. */ + + +/********************************************************************** + * Other fancy types. + */ + +typedef ci_uint8 ci_octet; + +typedef enum { + CI_FALSE = 0, + CI_TRUE +} ci_boolean_t; + + +/********************************************************************** + * Some nice types you'd always assumed were standards. + * (Really, they are SYSV "standards".) + */ + +#ifdef _WIN32 +typedef unsigned long ulong; +typedef unsigned int uint; +typedef char* caddr_t; +#elif defined(__linux__) && defined(__KERNEL__) +#include +#elif defined(__linux__) +#include +#endif + + +#endif /* __CI_COMPAT_PRIMITIVE_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/sysdep.h @@ -0,0 +1,166 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_SYSDEP_H__ +#define __CI_COMPAT_SYSDEP_H__ + + +/********************************************************************** + * Platform definition fixups. + */ + +#if defined(__ci_ul_driver__) && !defined(__ci_driver__) +# define __ci_driver__ +#endif + +#if defined(__ci_driver__) && !defined(__ci_ul_driver__) && \ + !defined(__KERNEL__) +# define __KERNEL__ +#endif + + +/********************************************************************** + * Sanity checks (no cheating!) + */ + +#if defined(__KERNEL__) && !defined(__ci_driver__) +# error Insane. +#endif + +#if defined(__KERNEL__) && defined(__ci_ul_driver__) +# error Madness. +#endif + +#if defined(__unix__) && defined(_WIN32) +# error Strange. +#endif + +#if defined(__GNUC__) && defined(_MSC_VER) +# error Crazy. +#endif + + +/********************************************************************** + * Compiler and processor dependencies. + */ + +#if defined(__GNUC__) + +# include + +# if defined(__i386__) +# include +# include +# elif defined(__x86_64__) +# include +# include +# elif defined(__PPC__) +# include +# include +# elif defined(__ia64__) +# include +# include +# else +# error Unknown processor - GNU C +# endif + +#elif defined(_MSC_VER) + +# include + +# if defined(__i386__) +# include +# include +# elif defined(__x86_64__) +# include +# include +# else +# error Unknown processor MSC +# endif + +#elif defined(__PGI) + +# include +# include + +#elif defined(__INTEL_COMPILER) + +/* Intel compilers v7 claim to be very gcc compatible. */ +# if __INTEL_COMPILER >= 700 +# include +# include +# include +# else +# error Old Intel compiler not supported. Yet. +# endif + +#else +# error Unknown compiler. +#endif + + +/********************************************************************** + * Misc stuff (that probably shouldn't be here). + */ + +#ifdef __sun +# ifdef __KERNEL__ +# define _KERNEL +# define _SYSCALL32 +# ifdef _LP64 +# define _SYSCALL32_IMPL +# endif +# else +# define _REENTRANT +# endif +#endif + + +/********************************************************************** + * Defaults for anything left undefined. + */ + +#ifndef CI_LIKELY +# define CI_LIKELY(t) (t) +# define CI_UNLIKELY(t) (t) +#endif + +#ifndef ci_restrict +# define ci_restrict +#endif + +#ifndef ci_inline +# define ci_inline static inline +#endif + +#ifndef ci_noinline +# define ci_noinline static +#endif + +#endif /* __CI_COMPAT_SYSDEP_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/utils.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/utils.h @@ -0,0 +1,269 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/* + * \author djr + * \brief Handy utility macros. + * \date 2003/01/17 + */ + +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_UTILS_H__ +#define __CI_COMPAT_UTILS_H__ + + +/********************************************************************** + * Alignment -- [align] must be a power of 2. + **********************************************************************/ + + /*! Align forward onto next boundary. */ + +#define CI_ALIGN_FWD(p, align) (((p)+(align)-1u) & ~((align)-1u)) + + + /*! Align back onto prev boundary. */ + +#define CI_ALIGN_BACK(p, align) ((p) & ~((align)-1u)) + + + /*! How far to next boundary? */ + +#define CI_ALIGN_NEEDED(p, align, signed_t) (-(signed_t)(p) & ((align)-1u)) + + + /*! How far beyond prev boundary? */ + +#define CI_OFFSET(p, align) ((p) & ((align)-1u)) + + + /*! Does object fit in gap before next boundary? */ + +#define CI_FITS(p, size, align, signed_t) \ + (CI_ALIGN_NEEDED((p) + 1, (align), signed_t) + 1 >= (size)) + + + /*! Align forward onto next boundary. */ + +#define CI_PTR_ALIGN_FWD(p, align) \ + ((char*) CI_ALIGN_FWD(((ci_ptr_arith_t)(p)), ((ci_ptr_arith_t)(align)))) + + /*! Align back onto prev boundary. */ + +#define CI_PTR_ALIGN_BACK(p, align) \ + ((char*) CI_ALIGN_BACK(((ci_ptr_arith_t)(p)), ((ci_ptr_arith_t)(align)))) + + /*! How far to next boundary? */ + +#define CI_PTR_ALIGN_NEEDED(p, align) \ + CI_ALIGN_NEEDED(((ci_ptr_arith_t)(p)), ((ci_ptr_arith_t)(align)), \ + ci_ptr_arith_t) + + /*! How far to next boundary? NZ = not zero i.e. give align if on boundary */ + +#define CI_PTR_ALIGN_NEEDED_NZ(p, align) \ + ((align) - (((char*)p) - \ + ((char*) CI_ALIGN_BACK(((ci_ptr_arith_t)(p)), ((ci_ptr_arith_t)(align)))))) + + /*! How far beyond prev boundary? */ + +#define CI_PTR_OFFSET(p, align) \ + CI_OFFSET(((ci_ptr_arith_t)(p)), ((ci_ptr_arith_t)(align))) + + + /* Same as CI_ALIGN_FWD and CI_ALIGN_BACK. */ + +#define CI_ROUND_UP(i, align) (((i)+(align)-1u) & ~((align)-1u)) + +#define CI_ROUND_DOWN(i, align) ((i) & ~((align)-1u)) + + +/********************************************************************** + * Byte-order + **********************************************************************/ + +/* These are not flags. They are enumeration values for use with + * CI_MY_BYTE_ORDER. */ +#define CI_BIG_ENDIAN 1 +#define CI_LITTLE_ENDIAN 0 + +/* +** Note that these byte-swapping primitives may leave junk in bits above +** the range they operate on. +** +** The CI_BSWAP_nn() routines require that bits above [nn] are zero. Use +** CI_BSWAPM_nn(x) if this cannot be guaranteed. +*/ + +/* ?? May be able to improve on some of these with inline assembler on some +** platforms. +*/ + +#define CI_BSWAP_16(v) ((((v) & 0xff) << 8) | ((v) >> 8)) +#define CI_BSWAPM_16(v) ((((v) & 0xff) << 8) | (((v) & 0xff00) >> 8)) + +#define CI_BSWAP_32(v) (((v) >> 24) | \ + (((v) & 0x00ff0000) >> 8) | \ + (((v) & 0x0000ff00) << 8) | \ + ((v) << 24)) +#define CI_BSWAPM_32(v) ((((v) & 0xff000000) >> 24) | \ + (((v) & 0x00ff0000) >> 8) | \ + (((v) & 0x0000ff00) << 8) | \ + ((v) << 24)) + +#define CI_BSWAP_64(v) (((v) >> 56) | \ + (((v) & 0x00ff000000000000) >> 40) | \ + (((v) & 0x0000ff0000000000) >> 24) | \ + (((v) & 0x000000ff00000000) >> 8) | \ + (((v) & 0x00000000ff000000) << 8) | \ + (((v) & 0x0000000000ff0000) << 24) | \ + (((v) & 0x000000000000ff00) << 40) | \ + ((v) << 56)) + +# define CI_BSWAPPED_16_IF(c,v) ((c) ? CI_BSWAP_16(v) : (v)) +# define CI_BSWAPPED_32_IF(c,v) ((c) ? CI_BSWAP_32(v) : (v)) +# define CI_BSWAPPED_64_IF(c,v) ((c) ? CI_BSWAP_64(v) : (v)) +# define CI_BSWAP_16_IF(c,v) do{ if((c)) (v) = CI_BSWAP_16(v); }while(0) +# define CI_BSWAP_32_IF(c,v) do{ if((c)) (v) = CI_BSWAP_32(v); }while(0) +# define CI_BSWAP_64_IF(c,v) do{ if((c)) (v) = CI_BSWAP_64(v); }while(0) + +#if (CI_MY_BYTE_ORDER == CI_LITTLE_ENDIAN) +# define CI_BSWAP_LE16(v) (v) +# define CI_BSWAP_LE32(v) (v) +# define CI_BSWAP_LE64(v) (v) +# define CI_BSWAP_BE16(v) CI_BSWAP_16(v) +# define CI_BSWAP_BE32(v) CI_BSWAP_32(v) +# define CI_BSWAP_BE64(v) CI_BSWAP_64(v) +# define CI_BSWAPM_LE16(v) (v) +# define CI_BSWAPM_LE32(v) (v) +# define CI_BSWAPM_LE64(v) (v) +# define CI_BSWAPM_BE16(v) CI_BSWAPM_16(v) +# define CI_BSWAPM_BE32(v) CI_BSWAPM_32(v) +#elif (CI_MY_BYTE_ORDER == CI_BIG_ENDIAN) +# define CI_BSWAP_BE16(v) (v) +# define CI_BSWAP_BE32(v) (v) +# define CI_BSWAP_BE64(v) (v) +# define CI_BSWAP_LE16(v) CI_BSWAP_16(v) +# define CI_BSWAP_LE32(v) CI_BSWAP_32(v) +# define CI_BSWAP_LE64(v) CI_BSWAP_64(v) +# define CI_BSWAPM_BE16(v) (v) +# define CI_BSWAPM_BE32(v) (v) +# define CI_BSWAPM_BE64(v) (v) +# define CI_BSWAPM_LE16(v) CI_BSWAPM_16(v) +# define CI_BSWAPM_LE32(v) CI_BSWAPM_32(v) +#else +# error Bad endian. +#endif + + +/********************************************************************** + * Get pointer to struct from pointer to member + **********************************************************************/ + +#define CI_MEMBER_OFFSET(c_type, mbr_name) \ + ((ci_uint32) (ci_uintptr_t)(&((c_type*)0)->mbr_name)) + +#define CI_MEMBER_SIZE(c_type, mbr_name) \ + sizeof(((c_type*)0)->mbr_name) + +#define __CI_CONTAINER(c_type, mbr_name, p_mbr) \ + ( (c_type*) ((char*)(p_mbr) - CI_MEMBER_OFFSET(c_type, mbr_name)) ) + +#ifndef CI_CONTAINER +# define CI_CONTAINER(t,m,p) __CI_CONTAINER(t,m,p) +#endif + + +/********************************************************************** + * Structure member initialiser. + **********************************************************************/ + +#ifndef CI_STRUCT_MBR +# define CI_STRUCT_MBR(name, val) .name = val +#endif + + +/********************************************************************** + * min / max + **********************************************************************/ + +#define CI_MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define CI_MAX(x,y) (((x) > (y)) ? (x) : (y)) + +/********************************************************************** + * abs + **********************************************************************/ + +#define CI_ABS(x) (((x) < 0) ? -(x) : (x)) + +/********************************************************************** + * Conditional debugging + **********************************************************************/ + +#ifdef NDEBUG +# define CI_DEBUG(x) +# define CI_NDEBUG(x) x +# define CI_IF_DEBUG(y,n) (n) +# define CI_DEBUG_ARG(x) +#else +# define CI_DEBUG(x) x +# define CI_NDEBUG(x) +# define CI_IF_DEBUG(y,n) (y) +# define CI_DEBUG_ARG(x) ,x +#endif + +#ifdef __KERNEL__ +#define CI_KERNEL_ARG(x) ,x +#else +#define CI_KERNEL_ARG(x) +#endif + +#ifdef _WIN32 +# define CI_KERNEL_ARG_WIN(x) CI_KERNEL_ARG(x) +# define CI_ARG_WIN(x) ,x +#else +# define CI_KERNEL_ARG_WIN(x) +# define CI_ARG_WIN(x) +#endif + +#ifdef __unix__ +# define CI_KERNEL_ARG_UNIX(x) CI_KERNEL_ARG(x) +# define CI_ARG_UNIX(x) ,x +#else +# define CI_KERNEL_ARG_UNIX(x) +# define CI_ARG_UNIX(x) +#endif + +#ifdef __linux__ +# define CI_KERNEL_ARG_LINUX(x) CI_KERNEL_ARG(x) +# define CI_ARG_LINUX(x) ,x +#else +# define CI_KERNEL_ARG_LINUX(x) +# define CI_ARG_LINUX(x) +#endif + + +#endif /* __CI_COMPAT_UTILS_H__ */ +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/x86.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/x86.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_compat */ + +#ifndef __CI_COMPAT_X86_H__ +#define __CI_COMPAT_X86_H__ + + +#define CI_MY_BYTE_ORDER CI_LITTLE_ENDIAN + +#define CI_WORD_SIZE 4 +#define CI_PTR_SIZE 4 + +#define CI_PAGE_SIZE 4096 +#define CI_PAGE_SHIFT 12 +#define CI_PAGE_MASK (~(CI_PAGE_SIZE - 1)) + +#define CI_CPU_HAS_SSE 1 /* SSE extensions supported */ +#define CI_CPU_HAS_SSE2 0 /* SSE2 extensions supported */ +#define CI_CPU_OOS 0 /* CPU does out of order stores */ + + +#endif /* __CI_COMPAT_X86_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/compat/x86_64.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/compat/x86_64.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/* + * \author djr + * \brief Arch stuff for AMD x86_64. + * \date 2004/08/17 + */ + +/*! \cidoxg_include_ci_compat */ +#ifndef __CI_COMPAT_X86_64_H__ +#define __CI_COMPAT_X86_64_H__ + + +#define CI_MY_BYTE_ORDER CI_LITTLE_ENDIAN + +#define CI_WORD_SIZE 8 +#define CI_PTR_SIZE 8 + +#define CI_PAGE_SIZE 4096 +#define CI_PAGE_SHIFT 12 +#define CI_PAGE_MASK (~(CI_PAGE_SIZE - 1)) + +#define CI_CPU_HAS_SSE 1 /* SSE extensions supported */ + +/* SSE2 disabled while investigating BUG1060 */ +#define CI_CPU_HAS_SSE2 0 /* SSE2 extensions supported */ +#define CI_CPU_OOS 0 /* CPU does out of order stores */ + + +#endif /* __CI_COMPAT_X86_64_H__ */ +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/driver/resource/efx_vi.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/driver/resource/efx_vi.h @@ -0,0 +1,276 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file contains public EFX VI API to Solarflare resource manager. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_DRIVER_RESOURCE_EFX_VI_H__ +#define __CI_DRIVER_RESOURCE_EFX_VI_H__ + +/* Default size of event queue in the efx_vi resource. Copied from + * CI_CFG_NETIF_EVENTQ_SIZE */ +#define EFX_VI_EVENTQ_SIZE_DEFAULT 1024 + +extern int efx_vi_eventq_size; + +/************************************************************************** + * efx_vi_state types, allocation and free + **************************************************************************/ + +/*! Handle for refering to a efx_vi */ +struct efx_vi_state; + +/*! + * Allocate an efx_vi, including event queue and pt_endpoint + * + * \param vih_out Pointer to a handle that is set on success + * \param nic_index Index of NIC to apply this resource to + * \return Zero on success (and vih_out set), non-zero on failure. + */ +extern int +efx_vi_alloc(struct efx_vi_state **vih_out, int nic_index); + +/*! + * Free a previously allocated efx_vi + * + * \param vih The handle of the efx_vi to free + */ +extern void +efx_vi_free(struct efx_vi_state *vih); + +/*! + * Reset a previously allocated efx_vi + * + * \param vih The handle of the efx_vi to reset + */ +extern void +efx_vi_reset(struct efx_vi_state *vih); + +/************************************************************************** + * efx_vi_eventq types and functions + **************************************************************************/ + +/*! + * Register a function to receive callbacks when event queue timeouts + * or wakeups occur. Only one function per efx_vi can be registered + * at once. + * + * \param vih The handle to identify the efx_vi + * \param callback The function to callback + * \param context An argument to pass to the callback function + * \return Zero on success, non-zero on failure. + */ +extern int +efx_vi_eventq_register_callback(struct efx_vi_state *vih, + void (*callback)(void *context, int is_timeout), + void *context); + +/*! + * Remove the current eventq timeout or wakeup callback function + * + * \param vih The handle to identify the efx_vi + * \return Zero on success, non-zero on failure + */ +extern int +efx_vi_eventq_kill_callback(struct efx_vi_state *vih); + +/************************************************************************** + * efx_vi_dma_map types and functions + **************************************************************************/ + +/*! + * Handle for refering to a efx_vi + */ +struct efx_vi_dma_map_state; + +/*! + * Map a list of buffer pages so they are registered with the hardware + * + * \param vih The handle to identify the efx_vi + * \param addrs An array of page pointers to map + * \param n_addrs Length of the page pointer array. Must be a power of two. + * \param dmh_out Set on success to a handle used to refer to this mapping + * \return Zero on success, non-zero on failure. + */ +extern int +efx_vi_dma_map_pages(struct efx_vi_state *vih, struct page **pages, + int n_pages, struct efx_vi_dma_map_state **dmh_out); +extern int +efx_vi_dma_map_addrs(struct efx_vi_state *vih, + unsigned long long *dev_bus_addrs, int n_pages, + struct efx_vi_dma_map_state **dmh_out); + +/*! + * Unmap a previously mapped set of pages so they are no longer registered + * with the hardware. + * + * \param vih The handle to identify the efx_vi + * \param dmh The handle to identify the dma mapping + */ +extern void +efx_vi_dma_unmap_pages(struct efx_vi_state *vih, + struct efx_vi_dma_map_state *dmh); +extern void +efx_vi_dma_unmap_addrs(struct efx_vi_state *vih, + struct efx_vi_dma_map_state *dmh); + +/*! + * Retrieve the buffer address of the mapping + * + * \param vih The handle to identify the efx_vi + * \param dmh The handle to identify the buffer mapping + * \return The buffer address on success, or zero on failure + */ +extern unsigned +efx_vi_dma_get_map_addr(struct efx_vi_state *vih, + struct efx_vi_dma_map_state *dmh); + +/************************************************************************** + * efx_vi filter functions + **************************************************************************/ + +#define EFX_VI_STATIC_FILTERS 32 + +/*! Handle to refer to a filter instance */ +struct filter_resource_t; + +/*! + * Allocate and add a filter + * + * \param vih The handle to identify the efx_vi + * \param protocol The protocol of the new filter: UDP or TCP + * \param ip_addr_be32 The local ip address of the filter + * \param port_le16 The local port of the filter + * \param fh_out Set on success to be a handle to refer to this filter + * \return Zero on success, non-zero on failure. + */ +extern int +efx_vi_filter(struct efx_vi_state *vih, int protocol, unsigned ip_addr_be32, + int port_le16, struct filter_resource_t **fh_out); + +/*! + * Remove a filter and free resources associated with it + * + * \param vih The handle to identify the efx_vi + * \param fh The handle to identify the filter + * \return Zero on success, non-zero on failure + */ +extern int +efx_vi_filter_stop(struct efx_vi_state *vih, struct filter_resource_t *fh); + +/************************************************************************** + * efx_vi hw resources types and functions + **************************************************************************/ + +/*! Constants for the type field in efx_vi_hw_resource */ +#define EFX_VI_HW_RESOURCE_TXDMAQ 0x0 /* PFN of TX DMA Q */ +#define EFX_VI_HW_RESOURCE_RXDMAQ 0x1 /* PFN of RX DMA Q */ +#define EFX_VI_HW_RESOURCE_TXBELL 0x2 /* PFN of TX Doorbell (EF1) */ +#define EFX_VI_HW_RESOURCE_RXBELL 0x3 /* PFN of RX Doorbell (EF1) */ +#define EFX_VI_HW_RESOURCE_EVQTIMER 0x4 /* Address of event q timer */ + +/* Address of event q pointer (EF1) */ +#define EFX_VI_HW_RESOURCE_EVQPTR 0x5 +/* Address of register pointer (Falcon A) */ +#define EFX_VI_HW_RESOURCE_EVQRPTR 0x6 +/* Offset of register pointer (Falcon B) */ +#define EFX_VI_HW_RESOURCE_EVQRPTR_OFFSET 0x7 +/* Address of mem KVA */ +#define EFX_VI_HW_RESOURCE_EVQMEMKVA 0x8 +/* PFN of doorbell page (Falcon) */ +#define EFX_VI_HW_RESOURCE_BELLPAGE 0x9 + +/*! How large an array to allocate for the get_() functions - smaller + than the total number of constants as some are mutually exclusive */ +#define EFX_VI_HW_RESOURCE_MAXSIZE 0x7 + +/*! Constants for the mem_type field in efx_vi_hw_resource */ +#define EFX_VI_HW_RESOURCE_IOBUFFER 0 /* Host memory */ +#define EFX_VI_HW_RESOURCE_PERIPHERAL 1 /* Card memory/registers */ + +/*! + * Data structure providing information on a hardware resource mapping + */ +struct efx_vi_hw_resource { + u8 type; /*!< What this resource represents */ + u8 mem_type; /*!< What type of memory is it in, eg, + * host or iomem */ + u8 more_to_follow; /*!< Is this part of a multi-region resource */ + u32 length; /*!< Length of the resource in bytes */ + unsigned long address; /*!< Address of this resource */ +}; + +/*! + * Metadata concerning the list of hardware resource mappings + */ +struct efx_vi_hw_resource_metadata { + int version; + int evq_order; + int evq_offs; + int evq_capacity; + int instance; + unsigned rx_capacity; + unsigned tx_capacity; + int nic_arch; + int nic_revision; + char nic_variant; +}; + +/*! + * Obtain a list of hardware resource mappings, using virtual addresses + * + * \param vih The handle to identify the efx_vi + * \param mdata Pointer to a structure to receive the metadata + * \param hw_res_array An array to receive the list of hardware resources + * \param length The length of hw_res_array. Updated on success to contain + * the number of entries in the supplied array that were used. + * \return Zero on success, non-zero on failure + */ +extern int +efx_vi_hw_resource_get_virt(struct efx_vi_state *vih, + struct efx_vi_hw_resource_metadata *mdata, + struct efx_vi_hw_resource *hw_res_array, + int *length); + +/*! + * Obtain a list of hardware resource mappings, using physical addresses + * + * \param vih The handle to identify the efx_vi + * \param mdata Pointer to a structure to receive the metadata + * \param hw_res_array An array to receive the list of hardware resources + * \param length The length of hw_res_array. Updated on success to contain + * the number of entries in the supplied array that were used. + * \return Zero on success, non-zero on failure + */ +extern int +efx_vi_hw_resource_get_phys(struct efx_vi_state *vih, + struct efx_vi_hw_resource_metadata *mdata, + struct efx_vi_hw_resource *hw_res_array, + int *length); + +#endif /* __CI_DRIVER_RESOURCE_EFX_VI_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/common.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/common.h @@ -0,0 +1,102 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides API of the efhw library which may be used both from + * the kernel and from the user-space code. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_COMMON_H__ +#define __CI_EFHW_COMMON_H__ + +#include + +enum efhw_arch { + EFHW_ARCH_FALCON, + EFHW_ARCH_SIENA, +}; + +typedef uint32_t efhw_buffer_addr_t; +#define EFHW_BUFFER_ADDR_FMT "[ba:%"PRIx32"]" + +/*! Comment? */ +typedef union { + uint64_t u64; + struct { + uint32_t a; + uint32_t b; + } opaque; + struct { + uint32_t code; + uint32_t status; + } ev1002; +} efhw_event_t; + +/* Flags for TX/RX queues */ +#define EFHW_VI_JUMBO_EN 0x01 /*! scatter RX over multiple desc */ +#define EFHW_VI_ISCSI_RX_HDIG_EN 0x02 /*! iscsi rx header digest */ +#define EFHW_VI_ISCSI_TX_HDIG_EN 0x04 /*! iscsi tx header digest */ +#define EFHW_VI_ISCSI_RX_DDIG_EN 0x08 /*! iscsi rx data digest */ +#define EFHW_VI_ISCSI_TX_DDIG_EN 0x10 /*! iscsi tx data digest */ +#define EFHW_VI_TX_PHYS_ADDR_EN 0x20 /*! TX physical address mode */ +#define EFHW_VI_RX_PHYS_ADDR_EN 0x40 /*! RX physical address mode */ +#define EFHW_VI_RM_WITH_INTERRUPT 0x80 /*! VI with an interrupt */ +#define EFHW_VI_TX_IP_CSUM_DIS 0x100 /*! enable ip checksum generation */ +#define EFHW_VI_TX_TCPUDP_CSUM_DIS 0x200 /*! enable tcp/udp checksum + generation */ +#define EFHW_VI_TX_TCPUDP_ONLY 0x400 /*! drop non-tcp/udp packets */ + +/* Types of hardware filter */ +/* Each of these values implicitly selects scatter filters on B0 - or in + EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK if a non-scatter filter is required */ +#define EFHW_IP_FILTER_TYPE_UDP_WILDCARD (0) /* dest host only */ +#define EFHW_IP_FILTER_TYPE_UDP_FULL (1) /* dest host and port */ +#define EFHW_IP_FILTER_TYPE_TCP_WILDCARD (2) /* dest based filter */ +#define EFHW_IP_FILTER_TYPE_TCP_FULL (3) /* src filter */ +/* Same again, but with RSS (for B0 only) */ +#define EFHW_IP_FILTER_TYPE_UDP_WILDCARD_RSS_B0 (4) +#define EFHW_IP_FILTER_TYPE_UDP_FULL_RSS_B0 (5) +#define EFHW_IP_FILTER_TYPE_TCP_WILDCARD_RSS_B0 (6) +#define EFHW_IP_FILTER_TYPE_TCP_FULL_RSS_B0 (7) + +#define EFHW_IP_FILTER_TYPE_FULL_MASK (0x1) /* Mask for full / wildcard */ +#define EFHW_IP_FILTER_TYPE_TCP_MASK (0x2) /* Mask for TCP type */ +#define EFHW_IP_FILTER_TYPE_RSS_B0_MASK (0x4) /* Mask for B0 RSS enable */ +#define EFHW_IP_FILTER_TYPE_NOSCAT_B0_MASK (0x8) /* Mask for B0 SCATTER dsbl */ + +#define EFHW_IP_FILTER_TYPE_MASK (0xffff) /* Mask of types above */ + +#define EFHW_IP_FILTER_BROADCAST (0x10000) /* driverlink filter + support */ + +#endif /* __CI_EFHW_COMMON_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/common_sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/common_sysdep.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides version-independent Linux kernel API for + * userland-to-kernel interfaces. + * Only kernels >=2.6.9 are supported. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_COMMON_LINUX_H__ +#define __CI_EFHW_COMMON_LINUX_H__ + +#include +#include + +/* Dirty hack, but Linux kernel does not provide DMA_ADDR_T_FMT */ +#if BITS_PER_LONG == 64 || defined(CONFIG_HIGHMEM64G) +#define DMA_ADDR_T_FMT "%llx" +#else +#define DMA_ADDR_T_FMT "%x" +#endif + +/* Linux kernel also does not provide PRIx32... Sigh. */ +#define PRIx32 "x" +#define PRIx64 "llx" + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +enum { + false = 0, + true = 1 +}; + +typedef _Bool bool; +#endif /* LINUX_VERSION_CODE < 2.6.19 */ + +#endif /* __CI_EFHW_COMMON_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/debug.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/debug.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides debug-related API for efhw library using Linux kernel + * primitives. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_DEBUG_LINUX_H__ +#define __CI_EFHW_DEBUG_LINUX_H__ + +#define EFHW_PRINTK_PREFIX "[sfc efhw] " + +#define EFHW_PRINTK(level, fmt, ...) \ + printk(level EFHW_PRINTK_PREFIX fmt "\n", __VA_ARGS__) + +/* Following macros should be used with non-zero format parameters + * due to __VA_ARGS__ limitations. Use "%s" with __FUNCTION__ if you can't + * find better parameters. */ +#define EFHW_ERR(fmt, ...) EFHW_PRINTK(KERN_ERR, fmt, __VA_ARGS__) +#define EFHW_WARN(fmt, ...) EFHW_PRINTK(KERN_WARNING, fmt, __VA_ARGS__) +#define EFHW_NOTICE(fmt, ...) EFHW_PRINTK(KERN_NOTICE, fmt, __VA_ARGS__) +#if 0 && !defined(NDEBUG) +#define EFHW_TRACE(fmt, ...) EFHW_PRINTK(KERN_DEBUG, fmt, __VA_ARGS__) +#else +#define EFHW_TRACE(fmt, ...) +#endif + +#ifndef NDEBUG +#define EFHW_ASSERT(cond) BUG_ON((cond) == 0) +#define EFHW_DO_DEBUG(expr) expr +#else +#define EFHW_ASSERT(cond) +#define EFHW_DO_DEBUG(expr) +#endif + +#define EFHW_TEST(expr) \ + do { \ + if (unlikely(!(expr))) \ + BUG(); \ + } while (0) + +/* Build time asserts. We paste the line number into the type name + * so that the macro can be used more than once per file even if the + * compiler objects to multiple identical typedefs. Collisions + * between use in different header files is still possible. */ +#ifndef EFHW_BUILD_ASSERT +#define __EFHW_BUILD_ASSERT_NAME(_x) __EFHW_BUILD_ASSERT_ILOATHECPP(_x) +#define __EFHW_BUILD_ASSERT_ILOATHECPP(_x) __EFHW_BUILD_ASSERT__ ##_x +#define EFHW_BUILD_ASSERT(e) \ + typedef char __EFHW_BUILD_ASSERT_NAME(__LINE__)[(e) ? 1 : -1] +#endif + +#endif /* __CI_EFHW_DEBUG_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/efhw_config.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/efhw_config.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides some limits used in both kernel and userland code. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_EFAB_CONFIG_H__ +#define __CI_EFHW_EFAB_CONFIG_H__ + +#define EFHW_MAX_NR_DEVS 5 /* max number of efhw devices supported */ + +#endif /* __CI_EFHW_EFAB_CONFIG_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/efhw_types.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/efhw_types.h @@ -0,0 +1,342 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides struct efhw_nic and some related types. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_EFAB_TYPES_H__ +#define __CI_EFHW_EFAB_TYPES_H__ + +#include +#include +#include +#include + +/*-------------------------------------------------------------------- + * + * hardware limits used in the types + * + *--------------------------------------------------------------------*/ + +#define EFHW_KEVENTQ_MAX 8 + +/*-------------------------------------------------------------------- + * + * forward type declarations + * + *--------------------------------------------------------------------*/ + +struct efhw_nic; + +/*-------------------------------------------------------------------- + * + * Managed interface + * + *--------------------------------------------------------------------*/ + +struct efhw_buffer_table_allocation{ + unsigned base; + unsigned order; +}; + +struct eventq_resource_hardware { + /*!iobuffer allocated for eventq - can be larger than eventq */ + efhw_iopages_t iobuff; + unsigned iobuff_off; + struct efhw_buffer_table_allocation buf_tbl_alloc; + int capacity; /*!< capacity of event queue */ +}; + +/*-------------------------------------------------------------------- + * + * event queues and event driven callbacks + * + *--------------------------------------------------------------------*/ + +struct efhw_keventq { + volatile int lock; + caddr_t evq_base; + int32_t evq_ptr; + uint32_t evq_mask; + unsigned instance; + struct eventq_resource_hardware hw; + struct efhw_ev_handler *ev_handlers; +}; + +/********************************************************************** + * Portable HW interface. *************************************** + **********************************************************************/ + +/*-------------------------------------------------------------------- + * + * EtherFabric Functional units - configuration and control + * + *--------------------------------------------------------------------*/ + +struct efhw_func_ops { + + /*-------------- Initialisation ------------ */ + + /*! close down all hardware functional units - leaves NIC in a safe + state for driver unload */ + void (*close_hardware) (struct efhw_nic *nic); + + /*! initialise all hardware functional units */ + int (*init_hardware) (struct efhw_nic *nic, + struct efhw_ev_handler *, + const uint8_t *mac_addr); + + /*-------------- Interrupt support ------------ */ + + /*! Main interrupt routine + ** This function returns, + ** - zero, if the IRQ was not generated by EF1 + ** - non-zero, if EF1 was the source of the IRQ + ** + ** + ** opaque is an OS provided pointer for use by the OS callbacks + ** e.g in Windows used to indicate DPC scheduled + */ + int (*interrupt) (struct efhw_nic *nic); + + /*! Enable given interrupt mask for the given IRQ unit */ + void (*interrupt_enable) (struct efhw_nic *nic, uint idx); + + /*! Disable given interrupt mask for the given IRQ unit */ + void (*interrupt_disable) (struct efhw_nic *nic, uint idx); + + /*! Set interrupt moderation strategy for the given IRQ unit + ** val is in usec + */ + void (*set_interrupt_moderation)(struct efhw_nic *nic, + uint idx, uint val); + + /*-------------- Event support ------------ */ + + /*! Enable the given event queue + depending on the underlying implementation (EF1 or Falcon) then + either a q_base_addr in host memory, or a buffer base id should + be proivded + */ + void (*event_queue_enable) (struct efhw_nic *nic, + uint evq, /* evnt queue index */ + uint evq_size, /* units of #entries */ + dma_addr_t q_base_addr, uint buf_base_id); + + /*! Disable the given event queue (and any associated timer) */ + void (*event_queue_disable) (struct efhw_nic *nic, uint evq, + int timer_only); + + /*! request wakeup from the NIC on a given event Q */ + void (*wakeup_request) (struct efhw_nic *nic, dma_addr_t q_base_addr, + int next_i, int evq); + + /*! Push a SW event on a given eventQ */ + void (*sw_event) (struct efhw_nic *nic, int data, int evq); + + /*-------------- Filter support ------------ */ + + /*! Setup a given filter - The software can request a filter_i, + * but some EtherFabric implementations will override with + * a more suitable index + */ + int (*ipfilter_set) (struct efhw_nic *nic, int type, + int *filter_i, int dmaq, + unsigned saddr_be32, unsigned sport_be16, + unsigned daddr_be32, unsigned dport_be16); + + /*! Attach a given filter to a DMAQ */ + void (*ipfilter_attach) (struct efhw_nic *nic, int filter_idx, + int dmaq_idx); + + /*! Detach a filter from its DMAQ */ + void (*ipfilter_detach) (struct efhw_nic *nic, int filter_idx); + + /*! Clear down a given filter */ + void (*ipfilter_clear) (struct efhw_nic *nic, int filter_idx); + + /*-------------- DMA support ------------ */ + + /*! Initialise NIC state for a given TX DMAQ */ + void (*dmaq_tx_q_init) (struct efhw_nic *nic, + uint dmaq, uint evq, uint owner, uint tag, + uint dmaq_size, uint buf_idx, uint flags); + + /*! Initialise NIC state for a given RX DMAQ */ + void (*dmaq_rx_q_init) (struct efhw_nic *nic, + uint dmaq, uint evq, uint owner, uint tag, + uint dmaq_size, uint buf_idx, uint flags); + + /*! Disable a given TX DMAQ */ + void (*dmaq_tx_q_disable) (struct efhw_nic *nic, uint dmaq); + + /*! Disable a given RX DMAQ */ + void (*dmaq_rx_q_disable) (struct efhw_nic *nic, uint dmaq); + + /*! Flush a given TX DMA channel */ + int (*flush_tx_dma_channel) (struct efhw_nic *nic, uint dmaq); + + /*! Flush a given RX DMA channel */ + int (*flush_rx_dma_channel) (struct efhw_nic *nic, uint dmaq); + + /*-------------- Buffer table Support ------------ */ + + /*! Initialise a buffer table page */ + void (*buffer_table_set) (struct efhw_nic *nic, + dma_addr_t dma_addr, + uint bufsz, uint region, + int own_id, int buffer_id); + + /*! Initialise a block of buffer table pages */ + void (*buffer_table_set_n) (struct efhw_nic *nic, int buffer_id, + dma_addr_t dma_addr, + uint bufsz, uint region, + int n_pages, int own_id); + + /*! Clear a block of buffer table pages */ + void (*buffer_table_clear) (struct efhw_nic *nic, int buffer_id, + int num); + + /*! Commit a buffer table update */ + void (*buffer_table_commit) (struct efhw_nic *nic); + +}; + + +/*---------------------------------------------------------------------------- + * + * NIC type + * + *---------------------------------------------------------------------------*/ + +struct efhw_device_type { + int arch; /* enum efhw_arch */ + char variant; /* 'A', 'B', ... */ + int revision; /* 0, 1, ... */ +}; + + +/*---------------------------------------------------------------------------- + * + * EtherFabric NIC instance - nic.c for HW independent functions + * + *---------------------------------------------------------------------------*/ + +/*! */ +struct efhw_nic { + /*! zero base index in efrm_nic_table.nic array */ + volatile int index; + int ifindex; /*!< OS level nic index */ +#ifdef HAS_NET_NAMESPACE + struct net *nd_net; +#endif + + struct efhw_device_type devtype; + + /*! Options that can be set by user. */ + unsigned options; +# define NIC_OPT_EFTEST 0x1 /* owner is an eftest app */ + +# define NIC_OPT_DEFAULT 0 + + /*! Internal flags that indicate hardware properties at runtime. */ + unsigned flags; +# define NIC_FLAG_NO_INTERRUPT 0x01 /* to be set at init time only */ +# define NIC_FLAG_TRY_MSI 0x02 +# define NIC_FLAG_MSI 0x04 +# define NIC_FLAG_OS_IRQ_EN 0x08 +# define NIC_FLAG_10G 0x10 + + unsigned mtu; /*!< MAC MTU (includes MAC hdr) */ + + /* hardware resources */ + + /*! I/O address of the start of the bar */ + efhw_ioaddr_t bar_ioaddr; + + /*! Bar number of control aperture. */ + unsigned ctr_ap_bar; + /*! Length of control aperture in bytes. */ + unsigned ctr_ap_bytes; + + uint8_t mac_addr[ETH_ALEN]; /*!< mac address */ + + /*! EtherFabric Functional Units -- functions */ + const struct efhw_func_ops *efhw_func; + + /* Value read from FPGA version register. Zero for asic. */ + unsigned fpga_version; + + /*! This lock protects a number of misc NIC resources. It should + * only be used for things that can be at the bottom of the lock + * order. ie. You mustn't attempt to grab any other lock while + * holding this one. + */ + spinlock_t *reg_lock; + spinlock_t the_reg_lock; + + int buf_commit_outstanding; /*!< outstanding buffer commits */ + + /*! interrupt callbacks (hard-irq) */ + void (*irq_handler) (struct efhw_nic *, int unit); + + /*! event queues per driver */ + struct efhw_keventq evq[EFHW_KEVENTQ_MAX]; + +/* for marking when we are not using an IRQ unit + - 0 is a valid offset to an IRQ unit on EF1! */ +#define EFHW_IRQ_UNIT_UNUSED 0xffff + /*! interrupt unit in use */ + unsigned int irq_unit[EFHW_KEVENTQ_MAX]; + efhw_iopage_t irq_iobuff; /*!< Falcon SYSERR interrupt */ + + /* The new driverlink infrastructure. */ + struct efx_dl_device *net_driver_dev; + struct efx_dlfilt_cb_s *dlfilter_cb; + + /*! Bit masks of the sizes of event queues and dma queues supported + * by the nic. */ + unsigned evq_sizes; + unsigned rxq_sizes; + unsigned txq_sizes; + + /* Size of filter table (including odd and even banks). */ + unsigned filter_tbl_size; +}; + + +#define EFHW_KVA(nic) ((nic)->bar_ioaddr) + + +#endif /* __CI_EFHW_EFHW_TYPES_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/hardware_sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/hardware_sysdep.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides version-independent Linux kernel API for header files + * with hardware-related definitions (in ci/driver/efab/hardware*). + * Only kernels >=2.6.9 are supported. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_HARDWARE_LINUX_H__ +#define __CI_EFHW_HARDWARE_LINUX_H__ + +#include + +#ifdef __LITTLE_ENDIAN +#define EFHW_IS_LITTLE_ENDIAN +#elif __BIG_ENDIAN +#define EFHW_IS_BIG_ENDIAN +#else +#error Unknown endianness +#endif + +#ifndef mmiowb + #if defined(__i386__) || defined(__x86_64__) + #define mmiowb() + #elif defined(__ia64__) + #ifndef ia64_mfa + #define ia64_mfa() asm volatile ("mf.a" ::: "memory") + #endif + #define mmiowb ia64_mfa + #else + #error "Need definition for mmiowb()" + #endif +#endif + +typedef char *efhw_ioaddr_t; + +#ifndef readq +static inline uint64_t __readq(void __iomem *addr) +{ + return *(volatile uint64_t *)addr; +} +#define readq(x) __readq(x) +#endif + +#ifndef writeq +static inline void __writeq(uint64_t v, void __iomem *addr) +{ + *(volatile uint64_t *)addr = v; +} +#define writeq(val, addr) __writeq((val), (addr)) +#endif + +#endif /* __CI_EFHW_HARDWARE_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/iopage_types.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/iopage_types.h @@ -0,0 +1,188 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides efhw_page_t and efhw_iopage_t for Linux kernel. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_IOPAGE_LINUX_H__ +#define __CI_EFHW_IOPAGE_LINUX_H__ + +#include +#include +#include + +/*-------------------------------------------------------------------- + * + * efhw_page_t: A single page of memory. Directly mapped in the driver, + * and can be mapped to userlevel. + * + *--------------------------------------------------------------------*/ + +typedef struct { + unsigned long kva; +} efhw_page_t; + +static inline int efhw_page_alloc(efhw_page_t *p) +{ + p->kva = __get_free_page(in_interrupt()? GFP_ATOMIC : GFP_KERNEL); + return p->kva ? 0 : -ENOMEM; +} + +static inline int efhw_page_alloc_zeroed(efhw_page_t *p) +{ + p->kva = get_zeroed_page(in_interrupt()? GFP_ATOMIC : GFP_KERNEL); + return p->kva ? 0 : -ENOMEM; +} + +static inline void efhw_page_free(efhw_page_t *p) +{ + free_page(p->kva); + EFHW_DO_DEBUG(memset(p, 0, sizeof(*p))); +} + +static inline char *efhw_page_ptr(efhw_page_t *p) +{ + return (char *)p->kva; +} + +static inline unsigned efhw_page_pfn(efhw_page_t *p) +{ + return (unsigned)(__pa(p->kva) >> PAGE_SHIFT); +} + +static inline void efhw_page_mark_invalid(efhw_page_t *p) +{ + p->kva = 0; +} + +static inline int efhw_page_is_valid(efhw_page_t *p) +{ + return p->kva != 0; +} + +static inline void efhw_page_init_from_va(efhw_page_t *p, void *va) +{ + p->kva = (unsigned long)va; +} + +/*-------------------------------------------------------------------- + * + * efhw_iopage_t: A single page of memory. Directly mapped in the driver, + * and can be mapped to userlevel. Can also be accessed by the NIC. + * + *--------------------------------------------------------------------*/ + +typedef struct { + efhw_page_t p; + dma_addr_t dma_addr; +} efhw_iopage_t; + +static inline dma_addr_t efhw_iopage_dma_addr(efhw_iopage_t *p) +{ + return p->dma_addr; +} + +#define efhw_iopage_ptr(iop) efhw_page_ptr(&(iop)->p) +#define efhw_iopage_pfn(iop) efhw_page_pfn(&(iop)->p) +#define efhw_iopage_mark_invalid(iop) efhw_page_mark_invalid(&(iop)->p) +#define efhw_iopage_is_valid(iop) efhw_page_is_valid(&(iop)->p) + +/*-------------------------------------------------------------------- + * + * efhw_iopages_t: A set of pages that are contiguous in physical memory. + * Directly mapped in the driver, and can be mapped to userlevel. Can also + * be accessed by the NIC. + * + * NB. The O/S may be unwilling to allocate many, or even any of these. So + * only use this type where the NIC really needs a physically contiguous + * buffer. + * + *--------------------------------------------------------------------*/ + +typedef struct { + caddr_t kva; + unsigned order; + dma_addr_t dma_addr; +} efhw_iopages_t; + +static inline caddr_t efhw_iopages_ptr(efhw_iopages_t *p) +{ + return p->kva; +} + +static inline unsigned efhw_iopages_pfn(efhw_iopages_t *p) +{ + return (unsigned)(__pa(p->kva) >> PAGE_SHIFT); +} + +static inline dma_addr_t efhw_iopages_dma_addr(efhw_iopages_t *p) +{ + return p->dma_addr; +} + +static inline unsigned efhw_iopages_size(efhw_iopages_t *p) +{ + return 1u << (p->order + PAGE_SHIFT); +} + +/* efhw_iopage_t <-> efhw_iopages_t conversions for handling physically + * contiguous allocations in iobufsets for iSCSI. This allows the + * essential information about contiguous allocations from + * efhw_iopages_alloc() to be saved away in the efhw_iopage_t array in an + * iobufset. (Changing the iobufset resource to use a union type would + * involve a lot of code changes, and make the iobufset's metadata larger + * which could be bad as it's supposed to fit into a single page on some + * platforms.) + */ +static inline void +efhw_iopage_init_from_iopages(efhw_iopage_t *iopage, + efhw_iopages_t *iopages, unsigned pageno) +{ + iopage->p.kva = ((unsigned long)efhw_iopages_ptr(iopages)) + + (pageno * PAGE_SIZE); + iopage->dma_addr = efhw_iopages_dma_addr(iopages) + + (pageno * PAGE_SIZE); +} + +static inline void +efhw_iopages_init_from_iopage(efhw_iopages_t *iopages, + efhw_iopage_t *iopage, unsigned order) +{ + iopages->kva = (caddr_t) efhw_iopage_ptr(iopage); + EFHW_ASSERT(iopages->kva); + iopages->order = order; + iopages->dma_addr = efhw_iopage_dma_addr(iopage); +} + +#endif /* __CI_EFHW_IOPAGE_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/public.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/public.h @@ -0,0 +1,83 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides public API of efhw library exported from the SFC + * resource driver. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_PUBLIC_H__ +#define __CI_EFHW_PUBLIC_H__ + +#include +#include + +/*! Returns true if we have some EtherFabric functional units - + whether configured or not */ +static inline int efhw_nic_have_functional_units(struct efhw_nic *nic) +{ + return nic->efhw_func != 0; +} + +/*! Returns true if the EtherFabric functional units have been configured */ +static inline int efhw_nic_have_hw(struct efhw_nic *nic) +{ + return efhw_nic_have_functional_units(nic) && (EFHW_KVA(nic) != 0); +} + +/*! Helper function to allocate the iobuffer needed by an eventq + * - it ensures the eventq has the correct alignment for the NIC + * + * \param rm Event-queue resource manager + * \param instance Event-queue instance (index) + * \param buf_bytes Requested size of eventq + * \return < 0 if iobuffer allocation fails + */ +int efhw_nic_event_queue_alloc_iobuffer(struct efhw_nic *nic, + struct eventq_resource_hardware *h, + int evq_instance, unsigned buf_bytes); + +extern void falcon_nic_set_rx_usr_buf_size(struct efhw_nic *, + int rx_usr_buf_size); + +extern void +falcon_nic_rx_filter_ctl_set(struct efhw_nic *nic, uint32_t tcp_full, + uint32_t tcp_wild, + uint32_t udp_full, uint32_t udp_wild); + +extern void +falcon_nic_rx_filter_ctl_get(struct efhw_nic *nic, uint32_t *tcp_full, + uint32_t *tcp_wild, + uint32_t *udp_full, uint32_t *udp_wild); + +#endif /* __CI_EFHW_PUBLIC_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efhw/sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efhw/sysdep.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides version-independent Linux kernel API for efhw library. + * Only kernels >=2.6.9 are supported. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFHW_SYSDEP_LINUX_H__ +#define __CI_EFHW_SYSDEP_LINUX_H__ + +#include +#include +#include +#include +#include + +#include /* necessary for etherdevice.h on some kernels */ +#include + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21) +static inline int is_local_ether_addr(const u8 *addr) +{ + return (0x02 & addr[0]); +} +#endif + +typedef unsigned long irq_flags_t; + +#define spin_lock_destroy(l_) do {} while (0) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +#define HAS_NET_NAMESPACE +#endif + +/* Funny, but linux has round_up for x86 only, defined in + * x86-specific header */ +#ifndef round_up +#define round_up(x, y) (((x) + (y) - 1) & ~((y)-1)) +#endif + +#endif /* __CI_EFHW_SYSDEP_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efrm/nic_table.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efrm/nic_table.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides public API for NIC table. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFRM_NIC_TABLE_H__ +#define __CI_EFRM_NIC_TABLE_H__ + +#include +#include + +/*-------------------------------------------------------------------- + * + * struct efrm_nic_table - top level driver object keeping all NICs - + * implemented in driver_object.c + * + *--------------------------------------------------------------------*/ + +/*! Comment? */ +struct efrm_nic_table { + /*! nics attached to this driver */ + struct efhw_nic *nic[EFHW_MAX_NR_DEVS]; + /*! pointer to an arbitrary struct efhw_nic if one exists; + * for code which does not care which NIC it wants but + * still needs one. Note you cannot assume nic[0] exists. */ + struct efhw_nic *a_nic; + uint32_t nic_count; /*!< number of nics attached to this driver */ + spinlock_t lock; /*!< lock for table modifications */ + atomic_t ref_count; /*!< refcount for users of nic table */ +}; + +/* Resource driver structures used by other drivers as well */ +extern struct efrm_nic_table efrm_nic_table; + +static inline void efrm_nic_table_hold(void) +{ + atomic_inc(&efrm_nic_table.ref_count); +} + +static inline void efrm_nic_table_rele(void) +{ + atomic_dec(&efrm_nic_table.ref_count); +} + +static inline int efrm_nic_table_held(void) +{ + return (atomic_read(&efrm_nic_table.ref_count) != 0); +} + +/* Run code block _x multiple times with variable nic set to each + * registered NIC in turn. + * DO NOT "break" out of this loop early. */ +#define EFRM_FOR_EACH_NIC(_nic_i, _nic) \ + for ((_nic_i) = (efrm_nic_table_hold(), 0); \ + (_nic_i) < EFHW_MAX_NR_DEVS || (efrm_nic_table_rele(), 0); \ + (_nic_i)++) \ + if (((_nic) = efrm_nic_table.nic[_nic_i])) + +#define EFRM_FOR_EACH_NIC_IN_SET(_set, _i, _nic) \ + for ((_i) = (efrm_nic_table_hold(), 0); \ + (_i) < EFHW_MAX_NR_DEVS || (efrm_nic_table_rele(), 0); \ + ++(_i)) \ + if (((_nic) = efrm_nic_table.nic[_i]) && \ + efrm_nic_set_read((_set), (_i))) + +#endif /* __CI_EFRM_NIC_TABLE_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efrm/sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efrm/sysdep.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides Linux-like system-independent API for efrm library. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFRM_SYSDEP_H__ +#define __CI_EFRM_SYSDEP_H__ + +/* Spinlocks are defined in efhw/sysdep.h */ +#include + +#if defined(__linux__) && defined(__KERNEL__) + +# include + +#else + +# include + +#endif + +#endif /* __CI_EFRM_SYSDEP_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/efrm/sysdep_linux.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/efrm/sysdep_linux.h @@ -0,0 +1,248 @@ +/**************************************************************************** + * Driver for Solarflare network controllers - + * resource management for Xen backend, OpenOnload, etc + * (including support for SFE4001 10GBT NIC) + * + * This file provides version-independent Linux kernel API for efrm library. + * Only kernels >=2.6.9 are supported. + * + * Copyright 2005-2007: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Kfifo API is partially stolen from linux-2.6.22/include/linux/list.h + * Copyright (C) 2004 Stelian Pop + * + * Developed and maintained by Solarflare Communications: + * + * + * + * Certain parts of the driver were implemented by + * Alexandra Kossovsky + * OKTET Labs Ltd, Russia, + * http://oktetlabs.ru, + * by request of Solarflare Communications + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +#ifndef __CI_EFRM_SYSDEP_LINUX_H__ +#define __CI_EFRM_SYSDEP_LINUX_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +/* get roundup_pow_of_two(), which was in kernel.h in early kernel versions */ +#include +#endif + +/******************************************************************** + * + * List API + * + ********************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +static inline void +list_replace_init(struct list_head *old, struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; + INIT_LIST_HEAD(old); +} +#endif + +static inline struct list_head *list_pop(struct list_head *list) +{ + struct list_head *link = list->next; + list_del(link); + return link; +} + +static inline struct list_head *list_pop_tail(struct list_head *list) +{ + struct list_head *link = list->prev; + list_del(link); + return link; +} + +/******************************************************************** + * + * Workqueue API + * + ********************************************************************/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +#define NEED_OLD_WORK_API + +/** + * The old and new work function prototypes just change + * the type of the pointer in the only argument, so it's + * safe to cast one function type to the other + */ +typedef void (*efrm_old_work_func_t) (void *p); + +#undef INIT_WORK +#define INIT_WORK(_work, _func) \ + do { \ + INIT_LIST_HEAD(&(_work)->entry); \ + (_work)->pending = 0; \ + PREPARE_WORK((_work), \ + (efrm_old_work_func_t) (_func), \ + (_work)); \ + } while (0) + +#endif + +/******************************************************************** + * + * Kfifo API + * + ********************************************************************/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + +#if !defined(RHEL_RELEASE_CODE) || (RHEL_RELEASE_CODE < 1029) +typedef unsigned gfp_t; +#endif + +#define HAS_NO_KFIFO + +struct kfifo { + unsigned char *buffer; /* the buffer holding the data */ + unsigned int size; /* the size of the allocated buffer */ + unsigned int in; /* data is added at offset (in % size) */ + unsigned int out; /* data is extracted from off. (out % size) */ + spinlock_t *lock; /* protects concurrent modifications */ +}; + +extern struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, + gfp_t gfp_mask, spinlock_t *lock); +extern struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, + spinlock_t *lock); +extern void kfifo_free(struct kfifo *fifo); +extern unsigned int __kfifo_put(struct kfifo *fifo, + unsigned char *buffer, unsigned int len); +extern unsigned int __kfifo_get(struct kfifo *fifo, + unsigned char *buffer, unsigned int len); + +/** + * kfifo_put - puts some data into the FIFO + * @fifo: the fifo to be used. + * @buffer: the data to be added. + * @len: the length of the data to be added. + * + * This function copies at most @len bytes from the @buffer into + * the FIFO depending on the free space, and returns the number of + * bytes copied. + */ +static inline unsigned int +kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(fifo->lock, flags); + + ret = __kfifo_put(fifo, buffer, len); + + spin_unlock_irqrestore(fifo->lock, flags); + + return ret; +} + +/** + * kfifo_get - gets some data from the FIFO + * @fifo: the fifo to be used. + * @buffer: where the data must be copied. + * @len: the size of the destination buffer. + * + * This function copies at most @len bytes from the FIFO into the + * @buffer and returns the number of copied bytes. + */ +static inline unsigned int +kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(fifo->lock, flags); + + ret = __kfifo_get(fifo, buffer, len); + + /* + * optimization: if the FIFO is empty, set the indices to 0 + * so we don't wrap the next time + */ + if (fifo->in == fifo->out) + fifo->in = fifo->out = 0; + + spin_unlock_irqrestore(fifo->lock, flags); + + return ret; +} + +/** + * __kfifo_len - returns the number of bytes available in the FIFO, no locking version + * @fifo: the fifo to be used. + */ +static inline unsigned int __kfifo_len(struct kfifo *fifo) +{ + return fifo->in - fifo->out; +} + +/** + * kfifo_len - returns the number of bytes available in the FIFO + * @fifo: the fifo to be used. + */ +static inline unsigned int kfifo_len(struct kfifo *fifo) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(fifo->lock, flags); + + ret = __kfifo_len(fifo); + + spin_unlock_irqrestore(fifo->lock, flags); + + return ret; +} + +#else +#include +#endif + +static inline void kfifo_vfree(struct kfifo *fifo) +{ + vfree(fifo->buffer); + kfree(fifo); +} + +#endif /* __CI_EFRM_SYSDEP_LINUX_H__ */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/config.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/config.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_tools */ + +#ifndef __CI_TOOLS_CONFIG_H__ +#define __CI_TOOLS_CONFIG_H__ + + +/********************************************************************** + * Debugging. + */ + +#define CI_INCLUDE_ASSERT_VALID 0 + +/* Set non-zero to allow info about who has allocated what to appear in + * /proc/drivers/level5/mem. + * However - Note that doing so can lead to segfault when you unload the + * driver, and other weirdness. i.e. I don't think the code for is quite + * right (written by Oktet, hacked by gel), but it does work well enough to be + * useful. + */ +#define CI_MEMLEAK_DEBUG_ALLOC_TABLE 0 + + +#endif /* __CI_TOOLS_CONFIG_H__ */ +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/debug.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/debug.h @@ -0,0 +1,336 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_tools */ + +#ifndef __CI_TOOLS_DEBUG_H__ +#define __CI_TOOLS_DEBUG_H__ + +#define CI_LOG_E(x) x /* errors */ +#define CI_LOG_W(x) x /* warnings */ +#define CI_LOG_I(x) x /* information */ +#define CI_LOG_V(x) x /* verbose */ + +/* Build time asserts. We paste the line number into the type name + * so that the macro can be used more than once per file even if the + * compiler objects to multiple identical typedefs. Collisions + * between use in different header files is still possible. */ +#ifndef CI_BUILD_ASSERT +#define __CI_BUILD_ASSERT_NAME(_x) __CI_BUILD_ASSERT_ILOATHECPP(_x) +#define __CI_BUILD_ASSERT_ILOATHECPP(_x) __CI_BUILD_ASSERT__ ##_x +#define CI_BUILD_ASSERT(e)\ + typedef char __CI_BUILD_ASSERT_NAME(__LINE__)[(e)?1:-1] +#endif + + +#ifdef NDEBUG + +# define _ci_check(exp, file, line) +# define _ci_assert2(e, x, y, file, line) +# define _ci_assert(exp, file, line) +# define _ci_assert_equal(exp1, exp2, file, line) +# define _ci_assert_equiv(exp1, exp2, file, line) +# define _ci_assert_nequal(exp1, exp2, file, line) +# define _ci_assert_le(exp1, exp2, file, line) +# define _ci_assert_lt(exp1, exp2, file, line) +# define _ci_assert_ge(exp1, exp2, file, line) +# define _ci_assert_gt(exp1, exp2, file, line) +# define _ci_assert_impl(exp1, exp2, file, line) + +# define _ci_verify(exp, file, line) \ + do { \ + (void)(exp); \ + } while (0) + +# define CI_DEBUG_TRY(exp) \ + do { \ + (void)(exp); \ + } while (0) + +#define CI_TRACE(exp,fmt) +#define CI_TRACE_INT(integer) +#define CI_TRACE_INT32(integer) +#define CI_TRACE_INT64(integer) +#define CI_TRACE_UINT(integer) +#define CI_TRACE_UINT32(integer) +#define CI_TRACE_UINT64(integer) +#define CI_TRACE_HEX(integer) +#define CI_TRACE_HEX32(integer) +#define CI_TRACE_HEX64(integer) +#define CI_TRACE_PTR(pointer) +#define CI_TRACE_STRING(string) +#define CI_TRACE_MAC(mac) +#define CI_TRACE_IP(ip_be32) +#define CI_TRACE_ARP(arp_pkt) + +#else + +# define _CI_ASSERT_FMT "\nfrom %s:%d" + +# define _ci_check(exp, file, line) \ + do { \ + if (CI_UNLIKELY(!(exp))) \ + ci_warn(("ci_check(%s)"_CI_ASSERT_FMT, #exp, \ + (file), (line))); \ + } while (0) + +/* + * NOTE: ci_fail() emits the file and line where the assert is actually + * coded. + */ + +# define _ci_assert(exp, file, line) \ + do { \ + if (CI_UNLIKELY(!(exp))) \ + ci_fail(("ci_assert(%s)"_CI_ASSERT_FMT, #exp, \ + (file), (line))); \ + } while (0) + +# define _ci_assert2(e, x, y, file, line) do { \ + if(CI_UNLIKELY( ! (e) )) \ + ci_fail(("ci_assert(%s)\nwhere [%s=%"CI_PRIx64"] " \ + "[%s=%"CI_PRIx64"]\nat %s:%d\nfrom %s:%d", #e \ + , #x, (ci_uint64)(ci_uintptr_t)(x) \ + , #y, (ci_uint64)(ci_uintptr_t)(y), \ + __FILE__, __LINE__, (file), (line))); \ + } while (0) + +# define _ci_verify(exp, file, line) \ + do { \ + if (CI_UNLIKELY(!(exp))) \ + ci_fail(("ci_verify(%s)"_CI_ASSERT_FMT, #exp, \ + (file), (line))); \ + } while (0) + +# define _ci_assert_equal(x, y, f, l) _ci_assert2((x)==(y), x, y, (f), (l)) +# define _ci_assert_nequal(x, y, f, l) _ci_assert2((x)!=(y), x, y, (f), (l)) +# define _ci_assert_le(x, y, f, l) _ci_assert2((x)<=(y), x, y, (f), (l)) +# define _ci_assert_lt(x, y, f, l) _ci_assert2((x)< (y), x, y, (f), (l)) +# define _ci_assert_ge(x, y, f, l) _ci_assert2((x)>=(y), x, y, (f), (l)) +# define _ci_assert_gt(x, y, f, l) _ci_assert2((x)> (y), x, y, (f), (l)) +# define _ci_assert_or(x, y, f, l) _ci_assert2((x)||(y), x, y, (f), (l)) +# define _ci_assert_impl(x, y, f, l) _ci_assert2(!(x) || (y), x, y, (f), (l)) +# define _ci_assert_equiv(x, y, f, l) _ci_assert2(!(x)== !(y), x, y, (f), (l)) + +#define _ci_assert_equal_msg(exp1, exp2, msg, file, line) \ + do { \ + if (CI_UNLIKELY((exp1)!=(exp2))) \ + ci_fail(("ci_assert_equal_msg(%s == %s) were " \ + "(%"CI_PRIx64":%"CI_PRIx64") with msg[%c%c%c%c]" \ + _CI_ASSERT_FMT, #exp1, #exp2, \ + (ci_uint64)(ci_uintptr_t)(exp1), \ + (ci_uint64)(ci_uintptr_t)(exp2), \ + (((ci_uint32)msg) >> 24) && 0xff, \ + (((ci_uint32)msg) >> 16) && 0xff, \ + (((ci_uint32)msg) >> 8 ) && 0xff, \ + (((ci_uint32)msg) ) && 0xff, \ + (file), (line))); \ + } while (0) + +# define CI_DEBUG_TRY(exp) CI_TRY(exp) + +#define CI_TRACE(exp,fmt) \ + ci_log("%s:%d:%s] " #exp "=" fmt, \ + __FILE__, __LINE__, __FUNCTION__, (exp)) + + +#define CI_TRACE_INT(integer) \ + ci_log("%s:%d:%s] " #integer "=%d", \ + __FILE__, __LINE__, __FUNCTION__, (integer)) + + +#define CI_TRACE_INT32(integer) \ + ci_log("%s:%d:%s] " #integer "=%d", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_int32)integer)) + + +#define CI_TRACE_INT64(integer) \ + ci_log("%s:%d:%s] " #integer "=%lld", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_int64)integer)) + + +#define CI_TRACE_UINT(integer) \ + ci_log("%s:%d:%s] " #integer "=%ud", \ + __FILE__, __LINE__, __FUNCTION__, (integer)) + + +#define CI_TRACE_UINT32(integer) \ + ci_log("%s:%d:%s] " #integer "=%ud", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_uint32)integer)) + + +#define CI_TRACE_UINT64(integer) \ + ci_log("%s:%d:%s] " #integer "=%ulld", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_uint64)integer)) + + +#define CI_TRACE_HEX(integer) \ + ci_log("%s:%d:%s] " #integer "=0x%x", \ + __FILE__, __LINE__, __FUNCTION__, (integer)) + + +#define CI_TRACE_HEX32(integer) \ + ci_log("%s:%d:%s] " #integer "=0x%x", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_uint32)integer)) + + +#define CI_TRACE_HEX64(integer) \ + ci_log("%s:%d:%s] " #integer "=0x%llx", \ + __FILE__, __LINE__, __FUNCTION__, ((ci_uint64)integer)) + + +#define CI_TRACE_PTR(pointer) \ + ci_log("%s:%d:%s] " #pointer "=0x%p", \ + __FILE__, __LINE__, __FUNCTION__, (pointer)) + + +#define CI_TRACE_STRING(string) \ + ci_log("%s:%d:%s] " #string "=%s", \ + __FILE__, __LINE__, __FUNCTION__, (string)) + + +#define CI_TRACE_MAC(mac) \ + ci_log("%s:%d:%s] " #mac "=" CI_MAC_PRINTF_FORMAT, \ + __FILE__, __LINE__, __FUNCTION__, CI_MAC_PRINTF_ARGS(mac)) + + +#define CI_TRACE_IP(ip_be32) \ + ci_log("%s:%d:%s] " #ip_be32 "=" CI_IP_PRINTF_FORMAT, __FILE__, \ + __LINE__, __FUNCTION__, CI_IP_PRINTF_ARGS(&(ip_be32))) + + +#define CI_TRACE_ARP(arp_pkt) \ + ci_log("%s:%d:%s]\n"CI_ARP_PRINTF_FORMAT, \ + __FILE__, __LINE__, __FUNCTION__, CI_ARP_PRINTF_ARGS(arp_pkt)) + +#endif /* NDEBUG */ + +#define ci_check(exp) \ + _ci_check(exp, __FILE__, __LINE__) + +#define ci_assert(exp) \ + _ci_assert(exp, __FILE__, __LINE__) + +#define ci_verify(exp) \ + _ci_verify(exp, __FILE__, __LINE__) + +#define ci_assert_equal(exp1, exp2) \ + _ci_assert_equal(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_equal_msg(exp1, exp2, msg) \ + _ci_assert_equal_msg(exp1, exp2, msg, __FILE__, __LINE__) + +#define ci_assert_nequal(exp1, exp2) \ + _ci_assert_nequal(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_le(exp1, exp2) \ + _ci_assert_le(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_lt(exp1, exp2) \ + _ci_assert_lt(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_ge(exp1, exp2) \ + _ci_assert_ge(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_gt(exp1, exp2) \ + _ci_assert_gt(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_impl(exp1, exp2) \ + _ci_assert_impl(exp1, exp2, __FILE__, __LINE__) + +#define ci_assert_equiv(exp1, exp2) \ + _ci_assert_equiv(exp1, exp2, __FILE__, __LINE__) + + +#define CI_TEST(exp) \ + do{ \ + if( CI_UNLIKELY(!(exp)) ) \ + ci_fail(("CI_TEST(%s)", #exp)); \ + }while(0) + + +#define CI_TRY(exp) \ + do{ \ + int _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(_trc < 0) ) \ + ci_sys_fail(#exp, _trc); \ + }while(0) + + +#define CI_TRY_RET(exp) \ + do{ \ + int _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(_trc < 0) ) { \ + ci_log("%s returned %d at %s:%d", #exp, _trc, __FILE__, __LINE__); \ + return _trc; \ + } \ + }while(0) + +#define CI_LOGLEVEL_TRY_RET(logfn, exp) \ + do{ \ + int _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(_trc < 0) ) { \ + logfn (ci_log("%s returned %d at %s:%d", #exp, _trc, __FILE__, __LINE__)); \ + return _trc; \ + } \ + }while(0) + + +#define CI_SOCK_TRY(exp) \ + do{ \ + ci_sock_err_t _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(!ci_sock_errok(_trc)) ) \ + ci_sys_fail(#exp, _trc.val); \ + }while(0) + + +#define CI_SOCK_TRY_RET(exp) \ + do{ \ + ci_sock_err_t _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(!ci_sock_errok(_trc)) ) { \ + ci_log("%s returned %d at %s:%d", #exp, _trc.val, __FILE__, __LINE__); \ + return ci_sock_errcode(_trc); \ + } \ + }while(0) + + +#define CI_SOCK_TRY_SOCK_RET(exp) \ + do{ \ + ci_sock_err_t _trc; \ + _trc=(exp); \ + if( CI_UNLIKELY(!ci_sock_errok(_trc)) ) { \ + ci_log("%s returned %d at %s:%d", #exp, _trc.val, __FILE__, __LINE__); \ + return _trc; \ + } \ + }while(0) + +#endif /* __CI_TOOLS_DEBUG_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/log.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/log.h @@ -0,0 +1,262 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/* + * \author djr + * \brief Functions for logging and pretty-printing. + * \date 2002/08/07 + */ + +/*! \cidoxg_include_ci_tools */ + +#ifndef __CI_TOOLS_LOG_H__ +#define __CI_TOOLS_LOG_H__ + +#include + + +/********************************************************************** + * Logging. + */ + +/* size of internal log buffer */ +#define CI_LOG_MAX_LINE 512 +/* uses of ci_log must ensure that all trace messages are shorter than this */ +#define CI_LOG_MAX_MSG_LENGTH (CI_LOG_MAX_LINE-50) + +extern void ci_vlog(const char* fmt, va_list args) CI_HF; +extern void ci_log(const char* fmt, ...) CI_PRINTF_LIKE(1,2) CI_HF; + + /*! Set the prefix for log messages. + ** + ** Uses the storage pointed to by \em prefix. Therefore \em prefix must + ** be allocated on the heap, or statically. + */ +extern void ci_set_log_prefix(const char* prefix) CI_HF; + +typedef void (*ci_log_fn_t)(const char* msg); +extern ci_log_fn_t ci_log_fn CI_HV; + +/* Log functions. */ +extern void ci_log_null(const char* msg) CI_HF; +extern void ci_log_stderr(const char* msg) CI_HF; +extern void ci_log_stdout(const char* msg) CI_HF; +extern void ci_log_syslog(const char* msg) CI_HF; + +/*! Call the following to install special logging behaviours. */ +extern void ci_log_buffer_till_fail(void) CI_HF; +extern void ci_log_buffer_till_exit(void) CI_HF; + +extern void __ci_log_unique(const char* msg) CI_HF; +extern ci_log_fn_t __ci_log_unique_fn CI_HV; +ci_inline void ci_log_uniquify(void) { + if( ci_log_fn != __ci_log_unique ) { + __ci_log_unique_fn = ci_log_fn; + ci_log_fn = __ci_log_unique; + } +} + +extern void ci_log_file(const char* msg) CI_HF; +extern int ci_log_file_fd CI_HV; + +extern void __ci_log_nth(const char* msg) CI_HF; +extern ci_log_fn_t __ci_log_nth_fn CI_HV; +extern int ci_log_nth_n CI_HV; /* default 100 */ +ci_inline void ci_log_nth(void) { + if( ci_log_fn != __ci_log_nth ) { + __ci_log_nth_fn = ci_log_fn; + ci_log_fn = __ci_log_nth; + } +} + +extern int ci_log_level CI_HV; + +extern int ci_log_options CI_HV; +#define CI_LOG_PID 0x1 +#define CI_LOG_TID 0x2 +#define CI_LOG_TIME 0x4 +#define CI_LOG_DELTA 0x8 + +/********************************************************************** + * Used to define which mode we are in + */ +#if (defined(_WIN32) && !defined(__KERNEL__)) +typedef enum { + ci_log_md_NULL=0, + ci_log_md_ioctl, + ci_log_md_stderr, + ci_log_md_stdout, + ci_log_md_file, + ci_log_md_serial, + ci_log_md_syslog, + ci_log_md_pidfile +} ci_log_mode_t; +extern ci_log_mode_t ci_log_mode; +#endif + +/********************************************************************** + * Pretty-printing. + */ + +extern char ci_printable_char(char c) CI_HF; + +extern void (*ci_hex_dump_formatter)(char* buf, const ci_octet* s, + int i, int off, int len) CI_HV; +extern void ci_hex_dump_format_octets(char*,const ci_octet*,int,int,int) CI_HF; +extern void ci_hex_dump_format_dwords(char*,const ci_octet*,int,int,int) CI_HF; + +extern void ci_hex_dump_row(char* buf, volatile const void* s, int len, + ci_ptr_arith_t address) CI_HF; + /*!< A row contains up to 16 bytes. Row starts at [address & 15u], so + ** therefore [len + (address & 15u)] must be <= 16. + */ + +extern void ci_hex_dump(ci_log_fn_t, volatile const void*, + int len, ci_ptr_arith_t address) CI_HF; + +extern int ci_hex_dump_to_raw(const char* src_hex, void* buf, + unsigned* addr_out_opt, int* skip) CI_HF; + /*!< Recovers raw data from a single line of a hex dump. [buf] must be at + ** least 16 bytes long. Returns the number of bytes written to [buf] (in + ** range 1 -> 16), or -1 if [src_hex] doesn't contain hex data. Does not + ** cope with missing bytes at the start of a line. + */ + +extern int ci_format_eth_addr(char* buf, const void* eth_mac_addr, + char sep) CI_HF; + /*!< This will write 18 characters to including terminating null. + ** Returns number of bytes written excluding null. If [sep] is zero, ':' + ** is used. + */ + +extern int ci_parse_eth_addr(void* eth_mac_addr, + const char* str, char sep) CI_HF; + /*!< If [sep] is zero, absolutely any separator is accepted (even + ** inconsistent separators). Returns 0 on success, -1 on error. + */ + +extern int ci_format_ip4_addr(char* buf, unsigned addr_be32) CI_HF; + /*!< Formats the IP address (in network endian) in dotted-quad. Returns + ** the number of bytes written (up to 15), excluding the null. [buf] + ** must be at least 16 bytes long. + */ + + +/********************************************************************** + * Error checking. + */ + +extern void (*ci_fail_stop_fn)(void) CI_HV; + +extern void ci_fail_stop(void) CI_HF; +extern void ci_fail_hang(void) CI_HF; +extern void ci_fail_bomb(void) CI_HF; +extern void ci_backtrace(void) CI_HF; + +#if defined __linux__ && !defined __KERNEL__ +extern void ci_fail_abort (void) CI_HF; +#endif + +#ifdef __GNUC__ +extern void +__ci_fail(const char*, ...) CI_PRINTF_LIKE(1,2) CI_HF; +#else +# if _PREFAST_ + extern void _declspec(noreturn) __ci_fail(const char* fmt, ...); +# else + extern void __ci_fail(const char* fmt, ...); +# endif + +#endif + +#define ci_warn(x) \ + do{ ci_log("WARN at %s:%d", __FILE__, __LINE__); }while(0) + +#define ci_fail(x) \ + do{ ci_log("FAIL at %s:%d", __FILE__, __LINE__); __ci_fail x; }while(0) + +extern void __ci_sys_fail(const char* fn, int rc, + const char* file, int line) CI_HF; +#define ci_sys_fail(fn, rc) __ci_sys_fail(fn, rc, __FILE__, __LINE__) + +/********************************************************************** + * Logging to buffer (src/citools/log_buffer.c) + */ + +/*! Divert ci_log() messages to the log buffer + * normally they go to the system console */ +extern void ci_log_buffer_till_fail(void) CI_HF; + +/*! Dump the contents of the log buffer to the system console */ +extern void ci_log_buffer_dump(void) CI_HF; + + +/********************************************************************** + * Some useful pretty-printing. + */ + +#ifdef __linux__ +# define CI_SOCKCALL_FLAGS_FMT "%s%s%s%s%s%s%s%s%s%s%s" + +# define CI_SOCKCALL_FLAGS_PRI_ARG(x) \ + (((x) & MSG_OOB ) ? "OOB " :""), \ + (((x) & MSG_PEEK ) ? "PEEK " :""), \ + (((x) & MSG_DONTROUTE ) ? "DONTROUTE " :""), \ + (((x) & MSG_EOR ) ? "EOR " :""), \ + (((x) & MSG_CTRUNC ) ? "CTRUNC " :""), \ + (((x) & MSG_TRUNC ) ? "TRUNC " :""), \ + (((x) & MSG_WAITALL ) ? "WAITALL " :""), \ + (((x) & MSG_DONTWAIT ) ? "DONTWAIT " :""), \ + (((x) & MSG_NOSIGNAL ) ? "NOSIGNAL " :""), \ + (((x) & MSG_ERRQUEUE ) ? "ERRQUEUE " :""), \ + (((x) & MSG_CONFIRM ) ? "CONFIRM " :"") +#endif + +#ifdef _WIN32 +# define CI_SOCKCALL_FLAGS_FMT "%s%s%s" + +# define CI_SOCKCALL_FLAGS_PRI_ARG(x) \ + (((x) & MSG_OOB ) ? "OOB " :""), \ + (((x) & MSG_PEEK ) ? "PEEK " :""), \ + (((x) & MSG_DONTROUTE ) ? "DONTROUTE " :"") +#endif + +#ifdef __sun__ +# define CI_SOCKCALL_FLAGS_FMT "%s%s%s%s%s%s%s%s%s" + +# define CI_SOCKCALL_FLAGS_PRI_ARG(x) \ + (((x) & MSG_OOB ) ? "OOB " :""), \ + (((x) & MSG_PEEK ) ? "PEEK " :""), \ + (((x) & MSG_DONTROUTE ) ? "DONTROUTE " :""), \ + (((x) & MSG_EOR ) ? "EOR " :""), \ + (((x) & MSG_CTRUNC ) ? "CTRUNC " :""), \ + (((x) & MSG_TRUNC ) ? "TRUNC " :""), \ + (((x) & MSG_WAITALL ) ? "WAITALL " :""), \ + (((x) & MSG_DONTWAIT ) ? "DONTWAIT " :""), \ + (((x) & MSG_NOTIFICATION) ? "NOTIFICATION" :"") +#endif + +#endif /* __CI_TOOLS_LOG_H__ */ +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/platform/gcc_x86.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/platform/gcc_x86.h @@ -0,0 +1,361 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_tools_platform */ + +#ifndef __CI_TOOLS_GCC_X86_H__ +#define __CI_TOOLS_GCC_X86_H__ + + +/********************************************************************** + * Free-running cycle counters. + */ + +#define CI_HAVE_FRC64 +#define CI_HAVE_FRC32 + +#define ci_frc32(pval) __asm__ __volatile__("rdtsc" : "=a" (*pval) : : "edx") + +#if defined(__x86_64__) +ci_inline void ci_frc64(ci_uint64* pval) { + /* temp fix until we figure how to get this out in one bite */ + ci_uint64 low, high; + __asm__ __volatile__("rdtsc" : "=a" (low) , "=d" (high)); + *pval = (high << 32) | low; +} + +#else +#define ci_frc64(pval) __asm__ __volatile__("rdtsc" : "=A" (*pval)) +#endif + +#define ci_frc_flush() /* ?? Need a pipeline barrier. */ + + +/********************************************************************** + * Atomic integer. + */ + +/* +** int ci_atomic_read(a) { return a->n; } +** void ci_atomic_set(a, v) { a->n = v; } +** void ci_atomic_inc(a) { ++a->n; } +** void ci_atomic_dec(a) { --a->n; } +** int ci_atomic_inc_and_test(a) { return ++a->n == 0; } +** int ci_atomic_dec_and_test(a) { return --a->n == 0; } +** void ci_atomic_and(a, v) { a->n &= v; } +** void ci_atomic_or(a, v) { a->n |= v; } +*/ + +typedef struct { volatile ci_int32 n; } ci_atomic_t; + +#define CI_ATOMIC_INITIALISER(i) {(i)} + +static inline ci_int32 ci_atomic_read(const ci_atomic_t* a) { return a->n; } +static inline void ci_atomic_set(ci_atomic_t* a, int v) { a->n = v; ci_wmb(); } + +static inline void ci_atomic_inc(ci_atomic_t* a) +{ __asm__ __volatile__("lock; incl %0" : "+m" (a->n)); } + + +static inline void ci_atomic_dec(ci_atomic_t* a) +{ __asm__ __volatile__("lock; decl %0" : "+m" (a->n)); } + +static inline int ci_atomic_inc_and_test(ci_atomic_t* a) { + char r; + __asm__ __volatile__("lock; incl %0; sete %1" + : "+m" (a->n), "=qm" (r)); + return r; +} + +static inline int ci_atomic_dec_and_test(ci_atomic_t* a) { + char r; + __asm__ __volatile__("lock; decl %0; sete %1" + : "+m" (a->n), "=qm" (r)); + return r; +} + +ci_inline int +ci_atomic_xadd (ci_atomic_t *a, int v) { + __asm__ ("lock xadd %0, %1" : "=r" (v), "+m" (a->n) : "0" (v)); + return v; +} +ci_inline int +ci_atomic_xchg (ci_atomic_t *a, int v) { + __asm__ ("lock xchg %0, %1" : "=r" (v), "+m" (a->n) : "0" (v)); + return v; +} + +ci_inline void ci_atomic32_or(volatile ci_uint32* p, ci_uint32 mask) +{ __asm__ __volatile__("lock; orl %1, %0" : "+m" (*p) : "ir" (mask)); } + +ci_inline void ci_atomic32_and(volatile ci_uint32* p, ci_uint32 mask) +{ __asm__ __volatile__("lock; andl %1, %0" : "+m" (*p) : "ir" (mask)); } + +ci_inline void ci_atomic32_add(volatile ci_uint32* p, ci_uint32 v) +{ __asm__ __volatile__("lock; addl %1, %0" : "+m" (*p) : "ir" (v)); } + +#define ci_atomic_or(a, v) ci_atomic32_or ((ci_uint32*) &(a)->n, (v)) +#define ci_atomic_and(a, v) ci_atomic32_and((ci_uint32*) &(a)->n, (v)) +#define ci_atomic_add(a, v) ci_atomic32_add((ci_uint32*) &(a)->n, (v)) + +extern int ci_glibc_uses_nptl (void) CI_HF; +extern int ci_glibc_nptl_broken(void) CI_HF; +extern int ci_glibc_gs_get_is_multihreaded_offset (void) CI_HF; +extern int ci_glibc_gs_is_multihreaded_offset CI_HV; + +#if !defined(__x86_64__) +#ifdef __GLIBC__ +/* Returns non-zero if the calling process might be mulithreaded, returns 0 if + * it definitely isn't (i.e. if reimplementing this function for other + * architectures and platforms, you can safely just return 1). + */ +static inline int ci_is_multithreaded (void) { + + while (1) { + if (ci_glibc_gs_is_multihreaded_offset >= 0) { + /* NPTL keeps a variable that tells us this hanging off gs (i.e. in thread- + * local storage); just return this + */ + int r; + __asm__ __volatile__ ("movl %%gs:(%1), %0" + : "=r" (r) + : "r" (ci_glibc_gs_is_multihreaded_offset)); + return r; + } + + if (ci_glibc_gs_is_multihreaded_offset == -2) { + /* This means we've already determined that the libc version is NOT good + * for our funky "is multithreaded" hack + */ + return 1; + } + + /* If we get here, it means this is the first time the function has been + * called -- detect the libc version and go around again. + */ + ci_glibc_gs_is_multihreaded_offset = ci_glibc_gs_get_is_multihreaded_offset (); + + /* Go around again. We do the test here rather than at the top so that we go + * quicker in the common the case + */ + } +} + +#else /* def __GLIBC__ */ + +#define ci_is_multithreaded() 1 /* ?? Is the the POSIX way of finding out */ + /* whether the appication is single */ + /* threaded? */ + +#endif /* def __GLIBC__ */ + +#else /* defined __x86_64__ */ + +static inline int ci_is_multithreaded (void) { + /* Now easy way to tell on x86_64; so assume we're multithreaded */ + return 1; +} + +#endif /* defined __x86_64__ */ + + +/********************************************************************** + * Compare and swap. + */ + +#define CI_HAVE_COMPARE_AND_SWAP + +ci_inline int ci_cas32_succeed(volatile ci_int32* p, ci_int32 oldval, + ci_int32 newval) { + char ret; + ci_int32 prevval; + __asm__ __volatile__("lock; cmpxchgl %3, %1; sete %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +ci_inline int ci_cas32_fail(volatile ci_int32* p, ci_int32 oldval, + ci_int32 newval) { + char ret; + ci_int32 prevval; + __asm__ __volatile__("lock; cmpxchgl %3, %1; setne %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +#ifdef __x86_64__ +ci_inline int ci_cas64_succeed(volatile ci_int64* p, ci_int64 oldval, + ci_int64 newval) { + char ret; + ci_int64 prevval; + __asm__ __volatile__("lock; cmpxchgq %3, %1; sete %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +ci_inline int ci_cas64_fail(volatile ci_int64* p, ci_int64 oldval, + ci_int64 newval) { + char ret; + ci_int64 prevval; + __asm__ __volatile__("lock; cmpxchgq %3, %1; setne %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} +#endif + +ci_inline int ci_cas32u_succeed(volatile ci_uint32* p, ci_uint32 oldval, ci_uint32 newval) { + char ret; + ci_uint32 prevval; + __asm__ __volatile__("lock; cmpxchgl %3, %1; sete %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +ci_inline int ci_cas32u_fail(volatile ci_uint32* p, ci_uint32 oldval, ci_uint32 newval) { + char ret; + ci_uint32 prevval; + __asm__ __volatile__("lock; cmpxchgl %3, %1; setne %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +ci_inline int ci_cas64u_succeed(volatile ci_uint64* p, ci_uint64 oldval, + ci_uint64 newval) { + char ret; + ci_uint64 prevval; + __asm__ __volatile__("lock; cmpxchgq %3, %1; sete %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +ci_inline int ci_cas64u_fail(volatile ci_uint64* p, ci_uint64 oldval, + ci_uint64 newval) { + char ret; + ci_uint64 prevval; + __asm__ __volatile__("lock; cmpxchgq %3, %1; setne %0" + : "=q"(ret), "+m"(*p), "=a"(prevval) + : "r"(newval), "a"(oldval)); + return ret; +} + +#ifdef __x86_64__ + +# define ci_cas_uintptr_succeed(p,o,n) \ + ci_cas64u_succeed((volatile ci_uint64*) (p), (o), (n)) +# define ci_cas_uintptr_fail(p,o,n) \ + ci_cas64u_fail((volatile ci_uint64*) (p), (o), (n)) + +#else + +# define ci_cas_uintptr_succeed(p,o,n) \ + ci_cas32u_succeed((volatile ci_uint32*) (p), (o), (n)) +# define ci_cas_uintptr_fail(p,o,n) \ + ci_cas32u_fail((volatile ci_uint32*) (p), (o), (n)) + +#endif + + +/********************************************************************** + * Atomic bit field. + */ + +typedef ci_uint32 ci_bits; +#define CI_BITS_N 32u + +#define CI_BITS_DECLARE(name, n) \ + ci_bits name[((n) + CI_BITS_N - 1u) / CI_BITS_N] + +ci_inline void ci_bits_clear_all(volatile ci_bits* b, int n_bits) +{ memset((void*) b, 0, (n_bits+CI_BITS_N-1u) / CI_BITS_N * sizeof(ci_bits)); } + +ci_inline void ci_bit_set(volatile ci_bits* b, int i) { + __asm__ __volatile__("lock; btsl %1, %0" + : "=m" (*b) + : "Ir" (i)); +} + +ci_inline void ci_bit_clear(volatile ci_bits* b, int i) { + __asm__ __volatile__("lock; btrl %1, %0" + : "=m" (*b) + : "Ir" (i)); +} + +ci_inline int ci_bit_test(volatile ci_bits* b, int i) { + char rc; + __asm__("btl %2, %1; setc %0" + : "=r" (rc) + : "m" (*b), "Ir" (i)); + return rc; +} + +ci_inline int ci_bit_test_and_set(volatile ci_bits* b, int i) { + char rc; + __asm__ __volatile__("lock; btsl %2, %1; setc %0" + : "=r" (rc), "+m" (*b) + : "Ir" (i)); + return rc; +} + +ci_inline int ci_bit_test_and_clear(volatile ci_bits* b, int i) { + char rc; + __asm__ __volatile__("lock; btrl %2, %1; setc %0" + : "=r" (rc), "+m" (*b) + : "Ir" (i)); + return rc; +} + +/* These mask ops only work within a single ci_bits word. */ +#define ci_bit_mask_set(b,m) ci_atomic32_or((b), (m)) +#define ci_bit_mask_clear(b,m) ci_atomic32_and((b), ~(m)) + + +/********************************************************************** + * Misc. + */ + +#if __GNUC__ >= 3 +# define ci_spinloop_pause() __asm__("pause") +#else +# define ci_spinloop_pause() __asm__(".byte 0xf3, 0x90") +#endif + + +#define CI_HAVE_ADDC32 +#define ci_add_carry32(sum, v) __asm__("addl %1, %0 ;" \ + "adcl $0, %0 ;" \ + : "=r" (sum) \ + : "g" ((ci_uint32) v), "0" (sum)) + + +#endif /* __CI_TOOLS_GCC_X86_H__ */ + +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/platform/linux_kernel.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/platform/linux_kernel.h @@ -0,0 +1,362 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + + +/*! \cidoxg_include_ci_tools_platform */ + +#ifndef __CI_TOOLS_LINUX_KERNEL_H__ +#define __CI_TOOLS_LINUX_KERNEL_H__ + +/********************************************************************** + * Need to know the kernel version. + */ + +#ifndef LINUX_VERSION_CODE +# include +# ifndef UTS_RELEASE + /* 2.6.18 onwards defines UTS_RELEASE in a separate header */ +# include +# endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(2,7,0) +# error "Linux 2.6 required" +#endif + + +#include /* kmalloc / kfree */ +#include /* vmalloc / vfree */ +#include /* in_interrupt() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ci_in_irq in_irq +#define ci_in_interrupt in_interrupt +#define ci_in_atomic in_atomic + + +/********************************************************************** + * Misc stuff. + */ + +#ifdef BUG +# define CI_BOMB BUG +#endif + +ci_inline void* __ci_alloc(size_t n) +{ return kmalloc(n, (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)); } + +ci_inline void* __ci_atomic_alloc(size_t n) +{ return kmalloc(n, GFP_ATOMIC ); } + +ci_inline void __ci_free(void* p) { return kfree(p); } +ci_inline void* __ci_vmalloc(size_t n) { return vmalloc(n); } +ci_inline void __ci_vfree(void* p) { return vfree(p); } + + +#if CI_MEMLEAK_DEBUG_ALLOC_TABLE + #define ci_alloc(s) ci_alloc_memleak_debug (s, __FILE__, __LINE__) + #define ci_atomic_alloc(s) ci_atomic_alloc_memleak_debug(s, __FILE__, __LINE__) + #define ci_free ci_free_memleak_debug + #define ci_vmalloc(s) ci_vmalloc_memleak_debug (s, __FILE__,__LINE__) + #define ci_vfree ci_vfree_memleak_debug + #define ci_alloc_fn ci_alloc_fn_memleak_debug + #define ci_vmalloc_fn ci_vmalloc_fn_memleak_debug +#else /* !CI_MEMLEAK_DEBUG_ALLOC_TABLE */ + #define ci_alloc_fn __ci_alloc + #define ci_vmalloc_fn __ci_vmalloc +#endif + +#ifndef ci_alloc + #define ci_atomic_alloc __ci_atomic_alloc + #define ci_alloc __ci_alloc + #define ci_free __ci_free + #define ci_vmalloc __ci_vmalloc + #define ci_vmalloc_fn __ci_vmalloc + #define ci_vfree __ci_vfree +#endif + +#define ci_sprintf sprintf +#define ci_vsprintf vsprintf +#define ci_snprintf snprintf +#define ci_vsnprintf vsnprintf +#define ci_sscanf sscanf + + +#define CI_LOG_FN_DEFAULT ci_log_syslog + + +/*-------------------------------------------------------------------- + * + * irqs_disabled - needed for kmap helpers on some kernels + * + *--------------------------------------------------------------------*/ +#ifdef irqs_disabled +# define ci_irqs_disabled irqs_disabled +#else +# if defined(__i386__) | defined(__x86_64__) +# define ci_irqs_disabled(x) \ + ({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !(flags & (1<<9)); \ + }) +# else +# error "Need to implement irqs_disabled() for your architecture" +# endif +#endif + + +/********************************************************************** + * kmap helpers. + * + * Use ci_k(un)map for code paths which are not in an atomic context. + * For atomic code you need to use ci_k(un)map_in_atomic. This will grab + * one of the per-CPU kmap slots. + * + * NB in_interrupt != in_irq. If you don't know the difference then + * don't use kmap_in_atomic + * + * 2.4 allocates kmap slots by function. We are going to re-use the + * skb module's slot - we also use the same interlock + * + * 2.6 allocates kmap slots by type as well as by function. We are + * going to use the currently (2.6.10) unsused SOFTIRQ slot + * + */ + +ci_inline void* ci_kmap(struct page *page) { + CI_DEBUG(if( ci_in_atomic() | ci_in_interrupt() | ci_in_irq() ) BUG()); + return kmap(page); +} + +ci_inline void ci_kunmap(struct page *page) { + kunmap(page); +} + +#define CI_KM_SLOT KM_SOFTIRQ0 + + +typedef struct semaphore ci_semaphore_t; + +ci_inline void +ci_sem_init (ci_semaphore_t *sem, int val) { + sema_init (sem, val); +} + +ci_inline void +ci_sem_down (ci_semaphore_t *sem) { + down (sem); +} + +ci_inline int +ci_sem_trydown (ci_semaphore_t *sem) { + return down_trylock (sem); +} + +ci_inline void +ci_sem_up (ci_semaphore_t *sem) { + up (sem); +} + +ci_inline int +ci_sem_get_count(ci_semaphore_t *sem) { + return sem->count.counter; +} + +ci_inline void* ci_kmap_in_atomic(struct page *page) +{ + CI_DEBUG(if( ci_in_irq() ) BUG()); + + /* iSCSI can call without in_interrupt() but with irqs_disabled() + and in a context that can't sleep, so we need to check that + too */ + if(ci_in_interrupt() || ci_irqs_disabled()) + return kmap_atomic(page, CI_KM_SLOT); + else + return kmap(page); +} + +ci_inline void ci_kunmap_in_atomic(struct page *page, void* kaddr) +{ + CI_DEBUG(if( ci_in_irq() ) BUG()); + + /* iSCSI can call without in_interrupt() but with irqs_disabled() + and in a context that can't sleep, so we need to check that + too */ + if(ci_in_interrupt() || ci_irqs_disabled()) + kunmap_atomic(kaddr, CI_KM_SLOT); + else + kunmap(page); +} + +/********************************************************************** + * spinlock implementation: used by + */ + +#define CI_HAVE_SPINLOCKS + +typedef ci_uintptr_t ci_lock_holder_t; +#define ci_lock_thisthread (ci_lock_holder_t)current +#define ci_lock_no_holder (ci_lock_holder_t)NULL + +typedef spinlock_t ci_lock_i; +typedef spinlock_t ci_irqlock_i; +typedef unsigned long ci_irqlock_state_t; + +#define IRQLOCK_CYCLES 500000 + +#define ci_lock_ctor_i(l) spin_lock_init(l) +#define ci_lock_dtor_i(l) do{}while(0) +#define ci_lock_lock_i(l) spin_lock(l) +#define ci_lock_trylock_i(l) spin_trylock(l) +#define ci_lock_unlock_i(l) spin_unlock(l) + +#define ci_irqlock_ctor_i(l) spin_lock_init(l) +#define ci_irqlock_dtor_i(l) do{}while(0) +#define ci_irqlock_lock_i(l,s) spin_lock_irqsave(l,*(s)) +#define ci_irqlock_unlock_i(l,s) spin_unlock_irqrestore(l, *(s)) + + +/********************************************************************** + * register access + */ + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) +typedef volatile void __iomem* ioaddr_t; +#else +typedef unsigned long ioaddr_t; +#endif + + + +/********************************************************************** + * thread implementation -- kernel dependancies probably should be + * moved to driver/linux_kernel.h + */ + +#define ci_linux_daemonize(name) daemonize(name) + +#include + + +typedef struct { + void* (*fn)(void* arg); + void* arg; + const char* name; + int thrd_id; + struct completion exit_event; + struct work_struct keventd_witem; +} ci_kernel_thread_t; + + +typedef ci_kernel_thread_t* cithread_t; + + +extern int cithread_create(cithread_t* tid, void* (*fn)(void*), void* arg, + const char* name); +extern int cithread_detach(cithread_t kt); +extern int cithread_join(cithread_t kt); + + +/* Kernel sysctl variables. */ +extern int sysctl_tcp_wmem[3]; +extern int sysctl_tcp_rmem[3]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +#define LINUX_HAS_SYSCTL_MEM_MAX +extern ci_uint32 sysctl_wmem_max; +extern ci_uint32 sysctl_rmem_max; +#endif + + +/*-------------------------------------------------------------------- + * + * ci_bigbuf_t: An abstraction of a large buffer. Needed because in the + * Linux kernel, large buffers need to be allocated with vmalloc(), whereas + * smaller buffers should use kmalloc(). This abstraction chooses the + * appropriate mechansim. + * + *--------------------------------------------------------------------*/ + +typedef struct { + char* p; + int is_vmalloc; +} ci_bigbuf_t; + + +ci_inline int ci_bigbuf_alloc(ci_bigbuf_t* bb, size_t bytes) { + if( bytes >= CI_PAGE_SIZE && ! ci_in_atomic() ) { + bb->is_vmalloc = 1; + if( (bb->p = vmalloc(bytes)) ) return 0; + } + bb->is_vmalloc = 0; + bb->p = kmalloc(bytes, ci_in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + return bb->p ? 0 : -ENOMEM; +} + +ci_inline void ci_bigbuf_free(ci_bigbuf_t* bb) { + if( bb->is_vmalloc ) vfree(bb->p); + else kfree(bb->p); +} + +ci_inline char* ci_bigbuf_ptr(ci_bigbuf_t* bb) +{ return bb->p; } + +/********************************************************************** + * struct iovec abstraction (for Windows port) + */ + +typedef struct iovec ci_iovec; + +/* Accessors for buffer/length */ +#define CI_IOVEC_BASE(i) ((i)->iov_base) +#define CI_IOVEC_LEN(i) ((i)->iov_len) + +/********************************************************************** + * Signals + */ + +ci_inline void +ci_send_sig(int signum) +{ + send_sig(signum, current, 0); +} + +#endif /* __CI_TOOLS_LINUX_KERNEL_H__ */ +/*! \cidoxg_end */ diff -r 6dcdd8348e5b drivers/xen/sfc_netback/ci/tools/sysdep.h --- /dev/null +++ b/drivers/xen/sfc_netback/ci/tools/sysdep.h @@ -0,0 +1,132 @@ +/**************************************************************************** + * Copyright 2002-2005: Level 5 Networks Inc. + * Copyright 2005-2008: Solarflare Communications Inc, + * 9501 Jeronimo Road, Suite 250, + * Irvine, CA 92618, USA + * + * Maintained by Solarflare Communications + * + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + **************************************************************************** + */ + +/*! \cidoxg_include_ci_tools */ + +#ifndef __CI_TOOLS_SYSDEP_H__ +#define __CI_TOOLS_SYSDEP_H__ + +/* Make this header self-sufficient */ +#include +#include +#include + + +/********************************************************************** + * Platform dependencies. + */ + +#if defined(__KERNEL__) + +# if defined(__linux__) +# include +# elif defined(_WIN32) +# include +# elif defined(__sun__) +# include +# else +# error Unknown platform. +# endif + +#elif defined(_WIN32) + +# include + +#elif defined(__unix__) + +# include + +#else + +# error Unknown platform. + +#endif + +#if defined(__linux__) +/*! Linux sendfile() support enable/disable. */ +# define CI_HAVE_SENDFILE /* provide sendfile i/f */ + +# define CI_HAVE_OS_NOPAGE +#endif + +#if defined(__sun__) +# define CI_HAVE_SENDFILE /* provide sendfile i/f */ +# define CI_HAVE_SENDFILEV /* provide sendfilev i/f */ + +# define CI_IOCTL_SENDFILE /* use efrm CI_SENDFILEV ioctl */ +#endif + +#if defined(_WIN32) +typedef ci_uint32 ci_uerr_t; /* range of OS user-mode return codes */ +typedef ci_uint32 ci_kerr_t; /* range of OS kernel-mode return codes */ +#elif defined(__unix__) +typedef ci_int32 ci_uerr_t; /* range of OS user-mode return codes */ +typedef ci_int32 ci_kerr_t; /* range of OS kernel-mode return codes */ +#endif + + +/********************************************************************** + * Compiler and processor dependencies. + */ + +#if defined(__GNUC__) + +#if defined(__i386__) || defined(__x86_64__) +# include +#elif defined(__PPC__) +# include +#elif defined(__ia64__) +# include +#else +# error Unknown processor. +#endif + +#elif defined(_MSC_VER) + +#if defined(__i386__) +# include +# elif defined(__x86_64__) +# include +#else +# error Unknown processor. +#endif + +#elif defined(__PGI) + +# include + +#elif defined(__INTEL_COMPILER) + +/* Intel compilers v7 claim to be very gcc compatible. */ +# include + +#else +# error Unknown compiler. +#endif + + +#endif /* __CI_TOOLS_SYSDEP_H__ */ + +/*! \cidoxg_end */