diff options
-rw-r--r-- | drivers/net/ethernet/ibm/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/ibm/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/ibm/iseries_veth.c | 1708 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.c | 63 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/efx.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/falcon_boards.c | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/nic.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/nic.h | 21 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/rx.c | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/selftest.c | 192 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/selftest.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/siena.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/siena_sriov.c | 2 | ||||
-rw-r--r-- | drivers/ptp/ptp_pch.c | 6 | ||||
-rw-r--r-- | include/linux/netdevice.h | 52 | ||||
-rw-r--r-- | include/linux/skbuff.h | 6 | ||||
-rw-r--r-- | include/net/ip.h | 2 | ||||
-rw-r--r-- | net/ipv4/ip_input.c | 14 |
19 files changed, 262 insertions, 1856 deletions
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig index 9e16f3fa97b2..b9773d229192 100644 --- a/drivers/net/ethernet/ibm/Kconfig +++ b/drivers/net/ethernet/ibm/Kconfig @@ -29,10 +29,6 @@ config IBMVETH To compile this driver as a module, choose M here. The module will be called ibmveth. -config ISERIES_VETH - tristate "iSeries Virtual Ethernet driver support" - depends on PPC_ISERIES - source "drivers/net/ethernet/ibm/emac/Kconfig" config EHEA diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile index 5a7d4e9ac803..2f04e71a5926 100644 --- a/drivers/net/ethernet/ibm/Makefile +++ b/drivers/net/ethernet/ibm/Makefile @@ -3,6 +3,5 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o -obj-$(CONFIG_ISERIES_VETH) += iseries_veth.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/ diff --git a/drivers/net/ethernet/ibm/iseries_veth.c b/drivers/net/ethernet/ibm/iseries_veth.c deleted file mode 100644 index 1cafa6562a06..000000000000 --- a/drivers/net/ethernet/ibm/iseries_veth.c +++ /dev/null @@ -1,1708 +0,0 @@ -/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ -/* - * IBM eServer iSeries Virtual Ethernet Device Driver - * Copyright (C) 2001 Kyle A. Lucke ([email protected]), IBM Corp. - * Substantially cleaned up by: - * Copyright (C) 2003 David Gibson <[email protected]>, IBM Corporation. - * Copyright (C) 2004-2005 Michael Ellerman, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * - * - * This module implements the virtual ethernet device for iSeries LPAR - * Linux. It uses hypervisor message passing to implement an - * ethernet-like network device communicating between partitions on - * the iSeries. - * - * The iSeries LPAR hypervisor currently allows for up to 16 different - * virtual ethernets. These are all dynamically configurable on - * OS/400 partitions, but dynamic configuration is not supported under - * Linux yet. An ethXX network device will be created for each - * virtual ethernet this partition is connected to. - * - * - This driver is responsible for routing packets to and from other - * partitions. The MAC addresses used by the virtual ethernets - * contains meaning and must not be modified. - * - * - Having 2 virtual ethernets to the same remote partition DOES NOT - * double the available bandwidth. The 2 devices will share the - * available hypervisor bandwidth. - * - * - If you send a packet to your own mac address, it will just be - * dropped, you won't get it on the receive side. - * - * - Multicast is implemented by sending the frame frame to every - * other partition. It is the responsibility of the receiving - * partition to filter the addresses desired. - * - * Tunable parameters: - * - * VETH_NUMBUFFERS: This compile time option defaults to 120. It - * controls how much memory Linux will allocate per remote partition - * it is communicating with. It can be thought of as the maximum - * number of packets outstanding to a remote partition at a time. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/mm.h> -#include <linux/ethtool.h> -#include <linux/if_ether.h> -#include <linux/slab.h> - -#include <asm/abs_addr.h> -#include <asm/iseries/mf.h> -#include <asm/uaccess.h> -#include <asm/firmware.h> -#include <asm/iseries/hv_lp_config.h> -#include <asm/iseries/hv_types.h> -#include <asm/iseries/hv_lp_event.h> -#include <asm/iommu.h> -#include <asm/vio.h> - -#undef DEBUG - -MODULE_AUTHOR("Kyle Lucke <[email protected]>"); -MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); -MODULE_LICENSE("GPL"); - -#define VETH_EVENT_CAP (0) -#define VETH_EVENT_FRAMES (1) -#define VETH_EVENT_MONITOR (2) -#define VETH_EVENT_FRAMES_ACK (3) - -#define VETH_MAX_ACKS_PER_MSG (20) -#define VETH_MAX_FRAMES_PER_MSG (6) - -struct veth_frames_data { - u32 addr[VETH_MAX_FRAMES_PER_MSG]; - u16 len[VETH_MAX_FRAMES_PER_MSG]; - u32 eofmask; -}; -#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) - -struct veth_frames_ack_data { - u16 token[VETH_MAX_ACKS_PER_MSG]; -}; - -struct veth_cap_data { - u8 caps_version; - u8 rsvd1; - u16 num_buffers; - u16 ack_threshold; - u16 rsvd2; - u32 ack_timeout; - u32 rsvd3; - u64 rsvd4[3]; -}; - -struct veth_lpevent { - struct HvLpEvent base_event; - union { - struct veth_cap_data caps_data; - struct veth_frames_data frames_data; - struct veth_frames_ack_data frames_ack_data; - } u; - -}; - -#define DRV_NAME "iseries_veth" -#define DRV_VERSION "2.0" - -#define VETH_NUMBUFFERS (120) -#define VETH_ACKTIMEOUT (1000000) /* microseconds */ -#define VETH_MAX_MCAST (12) - -#define VETH_MAX_MTU (9000) - -#if VETH_NUMBUFFERS < 10 -#define ACK_THRESHOLD (1) -#elif VETH_NUMBUFFERS < 20 -#define ACK_THRESHOLD (4) -#elif VETH_NUMBUFFERS < 40 -#define ACK_THRESHOLD (10) -#else -#define ACK_THRESHOLD (20) -#endif - -#define VETH_STATE_SHUTDOWN (0x0001) -#define VETH_STATE_OPEN (0x0002) -#define VETH_STATE_RESET (0x0004) -#define VETH_STATE_SENTMON (0x0008) -#define VETH_STATE_SENTCAPS (0x0010) -#define VETH_STATE_GOTCAPACK (0x0020) -#define VETH_STATE_GOTCAPS (0x0040) -#define VETH_STATE_SENTCAPACK (0x0080) -#define VETH_STATE_READY (0x0100) - -struct veth_msg { - struct veth_msg *next; - struct veth_frames_data data; - int token; - int in_use; - struct sk_buff *skb; - struct device *dev; -}; - -struct veth_lpar_connection { - HvLpIndex remote_lp; - struct delayed_work statemachine_wq; - struct veth_msg *msgs; - int num_events; - struct veth_cap_data local_caps; - - struct kobject kobject; - struct timer_list ack_timer; - - struct timer_list reset_timer; - unsigned int reset_timeout; - unsigned long last_contact; - int outstanding_tx; - - spinlock_t lock; - unsigned long state; - HvLpInstanceId src_inst; - HvLpInstanceId dst_inst; - struct veth_lpevent cap_event, cap_ack_event; - u16 pending_acks[VETH_MAX_ACKS_PER_MSG]; - u32 num_pending_acks; - - int num_ack_events; - struct veth_cap_data remote_caps; - u32 ack_timeout; - - struct veth_msg *msg_stack_head; -}; - -struct veth_port { - struct device *dev; - u64 mac_addr; - HvLpIndexMap lpar_map; - - /* queue_lock protects the stopped_map and dev's queue. */ - spinlock_t queue_lock; - HvLpIndexMap stopped_map; - - /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */ - rwlock_t mcast_gate; - int promiscuous; - int num_mcast; - u64 mcast_addr[VETH_MAX_MCAST]; - - struct kobject kobject; -}; - -static HvLpIndex this_lp; -static struct veth_lpar_connection *veth_cnx[HVMAXARCHITECTEDLPS]; /* = 0 */ -static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */ - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); -static void veth_wake_queues(struct veth_lpar_connection *cnx); -static void veth_stop_queues(struct veth_lpar_connection *cnx); -static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *); -static void veth_release_connection(struct kobject *kobject); -static void veth_timed_ack(unsigned long ptr); -static void veth_timed_reset(unsigned long ptr); - -/* - * Utility functions - */ - -#define veth_info(fmt, args...) \ - printk(KERN_INFO DRV_NAME ": " fmt, ## args) - -#define veth_error(fmt, args...) \ - printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args) - -#ifdef DEBUG -#define veth_debug(fmt, args...) \ - printk(KERN_DEBUG DRV_NAME ": " fmt, ## args) -#else -#define veth_debug(fmt, args...) do {} while (0) -#endif - -/* You must hold the connection's lock when you call this function. */ -static inline void veth_stack_push(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - msg->next = cnx->msg_stack_head; - cnx->msg_stack_head = msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx) -{ - struct veth_msg *msg; - - msg = cnx->msg_stack_head; - if (msg) - cnx->msg_stack_head = cnx->msg_stack_head->next; - - return msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx) -{ - return cnx->msg_stack_head == NULL; -} - -static inline HvLpEvent_Rc -veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype, - HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype, - u64 token, - u64 data1, u64 data2, u64 data3, u64 data4, u64 data5) -{ - return HvCallEvent_signalLpEventFast(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - subtype, ackind, acktype, - cnx->src_inst, - cnx->dst_inst, - token, data1, data2, data3, - data4, data5); -} - -static inline HvLpEvent_Rc veth_signaldata(struct veth_lpar_connection *cnx, - u16 subtype, u64 token, void *data) -{ - u64 *p = (u64 *) data; - - return veth_signalevent(cnx, subtype, HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - token, p[0], p[1], p[2], p[3], p[4]); -} - -struct veth_allocation { - struct completion c; - int num; -}; - -static void veth_complete_allocation(void *parm, int number) -{ - struct veth_allocation *vc = (struct veth_allocation *)parm; - - vc->num = number; - complete(&vc->c); -} - -static int veth_allocate_events(HvLpIndex rlp, int number) -{ - struct veth_allocation vc = - { COMPLETION_INITIALIZER_ONSTACK(vc.c), 0 }; - - mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan, - sizeof(struct veth_lpevent), number, - &veth_complete_allocation, &vc); - wait_for_completion(&vc.c); - - return vc.num; -} - -/* - * sysfs support - */ - -struct veth_cnx_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_lpar_connection *, char *buf); - ssize_t (*store)(struct veth_lpar_connection *, const char *buf); -}; - -static ssize_t veth_cnx_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_cnx_attribute *cnx_attr; - struct veth_lpar_connection *cnx; - - cnx_attr = container_of(attr, struct veth_cnx_attribute, attr); - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - - if (!cnx_attr->show) - return -EIO; - - return cnx_attr->show(cnx, buf); -} - -#define CUSTOM_CNX_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_CNX_ATTR(_name) \ - CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name) - -SIMPLE_CNX_ATTR(outstanding_tx); -SIMPLE_CNX_ATTR(remote_lp); -SIMPLE_CNX_ATTR(num_events); -SIMPLE_CNX_ATTR(src_inst); -SIMPLE_CNX_ATTR(dst_inst); -SIMPLE_CNX_ATTR(num_pending_acks); -SIMPLE_CNX_ATTR(num_ack_events); -CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout)); -CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout)); -CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state); -CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ? - jiffies_to_msecs(jiffies - cnx->last_contact) : 0); - -#define GET_CNX_ATTR(_name) (&veth_cnx_attr_##_name.attr) - -static struct attribute *veth_cnx_default_attrs[] = { - GET_CNX_ATTR(outstanding_tx), - GET_CNX_ATTR(remote_lp), - GET_CNX_ATTR(num_events), - GET_CNX_ATTR(reset_timeout), - GET_CNX_ATTR(last_contact), - GET_CNX_ATTR(state), - GET_CNX_ATTR(src_inst), - GET_CNX_ATTR(dst_inst), - GET_CNX_ATTR(num_pending_acks), - GET_CNX_ATTR(num_ack_events), - GET_CNX_ATTR(ack_timeout), - NULL -}; - -static const struct sysfs_ops veth_cnx_sysfs_ops = { - .show = veth_cnx_attribute_show -}; - -static struct kobj_type veth_lpar_connection_ktype = { - .release = veth_release_connection, - .sysfs_ops = &veth_cnx_sysfs_ops, - .default_attrs = veth_cnx_default_attrs -}; - -struct veth_port_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_port *, char *buf); - ssize_t (*store)(struct veth_port *, const char *buf); -}; - -static ssize_t veth_port_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_port_attribute *port_attr; - struct veth_port *port; - - port_attr = container_of(attr, struct veth_port_attribute, attr); - port = container_of(kobj, struct veth_port, kobject); - - if (!port_attr->show) - return -EIO; - - return port_attr->show(port, buf); -} - -#define CUSTOM_PORT_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_port *port, char *buf) \ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_PORT_ATTR(_name) \ - CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name) - -SIMPLE_PORT_ATTR(promiscuous); -SIMPLE_PORT_ATTR(num_mcast); -CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map); -CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map); -CUSTOM_PORT_ATTR(mac_addr, "0x%llX\n", port->mac_addr); - -#define GET_PORT_ATTR(_name) (&veth_port_attr_##_name.attr) -static struct attribute *veth_port_default_attrs[] = { - GET_PORT_ATTR(mac_addr), - GET_PORT_ATTR(lpar_map), - GET_PORT_ATTR(stopped_map), - GET_PORT_ATTR(promiscuous), - GET_PORT_ATTR(num_mcast), - NULL -}; - -static const struct sysfs_ops veth_port_sysfs_ops = { - .show = veth_port_attribute_show -}; - -static struct kobj_type veth_port_ktype = { - .sysfs_ops = &veth_port_sysfs_ops, - .default_attrs = veth_port_default_attrs -}; - -/* - * LPAR connection code - */ - -static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx) -{ - schedule_delayed_work(&cnx->statemachine_wq, 0); -} - -static void veth_take_cap(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - /* Receiving caps may mean the other end has just come up, so - * we need to reload the instance ID of the far end */ - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - if (cnx->state & VETH_STATE_GOTCAPS) { - veth_error("Received a second capabilities from LPAR %d.\n", - cnx->remote_lp); - event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable; - HvCallEvent_ackLpEvent((struct HvLpEvent *) event); - } else { - memcpy(&cnx->cap_event, event, sizeof(cnx->cap_event)); - cnx->state |= VETH_STATE_GOTCAPS; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_cap_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->state & VETH_STATE_GOTCAPACK) { - veth_error("Received a second capabilities ack from LPAR %d.\n", - cnx->remote_lp); - } else { - memcpy(&cnx->cap_ack_event, event, - sizeof(cnx->cap_ack_event)); - cnx->state |= VETH_STATE_GOTCAPACK; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - veth_debug("cnx %d: lost connection.\n", cnx->remote_lp); - - /* Avoid kicking the statemachine once we're shutdown. - * It's unnecessary and it could break veth_stop_connection(). */ - - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_handle_ack(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xTargetLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap_ack(cnx, event); - break; - case VETH_EVENT_MONITOR: - veth_take_monitor_ack(cnx, event); - break; - default: - veth_error("Unknown ack type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_int(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xSourceLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - unsigned long flags; - int i, acked = 0; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap(cnx, event); - break; - case VETH_EVENT_MONITOR: - /* do nothing... this'll hang out here til we're dead, - * and the hypervisor will return it for us. */ - break; - case VETH_EVENT_FRAMES_ACK: - spin_lock_irqsave(&cnx->lock, flags); - - for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { - u16 msgnum = event->u.frames_ack_data.token[i]; - - if (msgnum < VETH_NUMBUFFERS) { - veth_recycle_msg(cnx, cnx->msgs + msgnum); - cnx->outstanding_tx--; - acked++; - } - } - - if (acked > 0) { - cnx->last_contact = jiffies; - veth_wake_queues(cnx); - } - - spin_unlock_irqrestore(&cnx->lock, flags); - break; - case VETH_EVENT_FRAMES: - veth_receive(cnx, event); - break; - default: - veth_error("Unknown interrupt type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_event(struct HvLpEvent *event) -{ - struct veth_lpevent *veth_event = (struct veth_lpevent *)event; - - if (hvlpevent_is_ack(event)) - veth_handle_ack(veth_event); - else - veth_handle_int(veth_event); -} - -static int veth_process_caps(struct veth_lpar_connection *cnx) -{ - struct veth_cap_data *remote_caps = &cnx->remote_caps; - int num_acks_needed; - - /* Convert timer to jiffies */ - cnx->ack_timeout = remote_caps->ack_timeout * HZ / 1000000; - - if ( (remote_caps->num_buffers == 0) || - (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) || - (remote_caps->ack_threshold == 0) || - (cnx->ack_timeout == 0) ) { - veth_error("Received incompatible capabilities from LPAR %d.\n", - cnx->remote_lp); - return HvLpEvent_Rc_InvalidSubtypeData; - } - - num_acks_needed = (remote_caps->num_buffers - / remote_caps->ack_threshold) + 1; - - /* FIXME: locking on num_ack_events? */ - if (cnx->num_ack_events < num_acks_needed) { - int num; - - num = veth_allocate_events(cnx->remote_lp, - num_acks_needed-cnx->num_ack_events); - if (num > 0) - cnx->num_ack_events += num; - - if (cnx->num_ack_events < num_acks_needed) { - veth_error("Couldn't allocate enough ack events " - "for LPAR %d.\n", cnx->remote_lp); - - return HvLpEvent_Rc_BufferNotAvailable; - } - } - - - return HvLpEvent_Rc_Good; -} - -/* FIXME: The gotos here are a bit dubious */ -static void veth_statemachine(struct work_struct *work) -{ - struct veth_lpar_connection *cnx = - container_of(work, struct veth_lpar_connection, - statemachine_wq.work); - int rlp = cnx->remote_lp; - int rc; - - spin_lock_irq(&cnx->lock); - - restart: - if (cnx->state & VETH_STATE_RESET) { - if (cnx->state & VETH_STATE_OPEN) - HvCallEvent_closeLpEventPath(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - /* - * Reset ack data. This prevents the ack_timer actually - * doing anything, even if it runs one more time when - * we drop the lock below. - */ - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - cnx->num_pending_acks = 0; - - cnx->state &= ~(VETH_STATE_RESET | VETH_STATE_SENTMON - | VETH_STATE_OPEN | VETH_STATE_SENTCAPS - | VETH_STATE_GOTCAPACK | VETH_STATE_GOTCAPS - | VETH_STATE_SENTCAPACK | VETH_STATE_READY); - - /* Clean up any leftover messages */ - if (cnx->msgs) { - int i; - for (i = 0; i < VETH_NUMBUFFERS; ++i) - veth_recycle_msg(cnx, cnx->msgs + i); - } - - cnx->outstanding_tx = 0; - veth_wake_queues(cnx); - - /* Drop the lock so we can do stuff that might sleep or - * take other locks. */ - spin_unlock_irq(&cnx->lock); - - del_timer_sync(&cnx->ack_timer); - del_timer_sync(&cnx->reset_timer); - - spin_lock_irq(&cnx->lock); - - if (cnx->state & VETH_STATE_RESET) - goto restart; - - /* Hack, wait for the other end to reset itself. */ - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ); - goto out; - } - } - - if (cnx->state & VETH_STATE_SHUTDOWN) - /* It's all over, do nothing */ - goto out; - - if ( !(cnx->state & VETH_STATE_OPEN) ) { - if (! cnx->msgs || (cnx->num_events < (2 + VETH_NUMBUFFERS)) ) - goto cant_cope; - - HvCallEvent_openLpEventPath(rlp, HvLpEvent_Type_VirtualLan); - cnx->src_inst = - HvCallEvent_getSourceLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->state |= VETH_STATE_OPEN; - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTMON) ) { - rc = veth_signalevent(cnx, VETH_EVENT_MONITOR, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_DeferredAck, - 0, 0, 0, 0, 0, 0); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTMON; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending monitor to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTCAPS)) { - u64 *rawcap = (u64 *)&cnx->local_caps; - - rc = veth_signalevent(cnx, VETH_EVENT_CAP, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - 0, rawcap[0], rawcap[1], rawcap[2], - rawcap[3], rawcap[4]); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTCAPS; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending caps to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ((cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_SENTCAPACK)) { - struct veth_cap_data *remote_caps = &cnx->remote_caps; - - memcpy(remote_caps, &cnx->cap_event.u.caps_data, - sizeof(*remote_caps)); - - spin_unlock_irq(&cnx->lock); - rc = veth_process_caps(cnx); - spin_lock_irq(&cnx->lock); - - /* We dropped the lock, so recheck for anything which - * might mess us up */ - if (cnx->state & (VETH_STATE_RESET|VETH_STATE_SHUTDOWN)) - goto restart; - - cnx->cap_event.base_event.xRc = rc; - HvCallEvent_ackLpEvent((struct HvLpEvent *)&cnx->cap_event); - if (rc == HvLpEvent_Rc_Good) - cnx->state |= VETH_STATE_SENTCAPACK; - else - goto cant_cope; - } - - if ((cnx->state & VETH_STATE_GOTCAPACK) && - (cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_READY)) { - if (cnx->cap_ack_event.base_event.xRc == HvLpEvent_Rc_Good) { - /* Start the ACK timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - cnx->state |= VETH_STATE_READY; - } else { - veth_error("Caps rejected by LPAR %d, rc = %d\n", - rlp, cnx->cap_ack_event.base_event.xRc); - goto cant_cope; - } - } - - out: - spin_unlock_irq(&cnx->lock); - return; - - cant_cope: - /* FIXME: we get here if something happens we really can't - * cope with. The link will never work once we get here, and - * all we can do is not lock the rest of the system up */ - veth_error("Unrecoverable error on connection to LPAR %d, shutting down" - " (state = 0x%04lx)\n", rlp, cnx->state); - cnx->state |= VETH_STATE_SHUTDOWN; - spin_unlock_irq(&cnx->lock); -} - -static int veth_init_connection(u8 rlp) -{ - struct veth_lpar_connection *cnx; - struct veth_msg *msgs; - int i; - - if ( (rlp == this_lp) || - ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) - return 0; - - cnx = kzalloc(sizeof(*cnx), GFP_KERNEL); - if (! cnx) - return -ENOMEM; - - cnx->remote_lp = rlp; - spin_lock_init(&cnx->lock); - INIT_DELAYED_WORK(&cnx->statemachine_wq, veth_statemachine); - - init_timer(&cnx->ack_timer); - cnx->ack_timer.function = veth_timed_ack; - cnx->ack_timer.data = (unsigned long) cnx; - - init_timer(&cnx->reset_timer); - cnx->reset_timer.function = veth_timed_reset; - cnx->reset_timer.data = (unsigned long) cnx; - cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000); - - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - - veth_cnx[rlp] = cnx; - - /* This gets us 1 reference, which is held on behalf of the driver - * infrastructure. It's released at module unload. */ - kobject_init(&cnx->kobject, &veth_lpar_connection_ktype); - - msgs = kcalloc(VETH_NUMBUFFERS, sizeof(struct veth_msg), GFP_KERNEL); - if (! msgs) { - veth_error("Can't allocate buffers for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->msgs = msgs; - - for (i = 0; i < VETH_NUMBUFFERS; i++) { - msgs[i].token = i; - veth_stack_push(cnx, msgs + i); - } - - cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS); - - if (cnx->num_events < (2 + VETH_NUMBUFFERS)) { - veth_error("Can't allocate enough events for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->local_caps.num_buffers = VETH_NUMBUFFERS; - cnx->local_caps.ack_threshold = ACK_THRESHOLD; - cnx->local_caps.ack_timeout = VETH_ACKTIMEOUT; - - return 0; -} - -static void veth_stop_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - spin_lock_irq(&cnx->lock); - cnx->state |= VETH_STATE_RESET | VETH_STATE_SHUTDOWN; - veth_kick_statemachine(cnx); - spin_unlock_irq(&cnx->lock); - - /* ensure the statemachine runs now and waits for its completion */ - flush_delayed_work_sync(&cnx->statemachine_wq); -} - -static void veth_destroy_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - if (cnx->num_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_events, - NULL, NULL); - if (cnx->num_ack_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_ack_events, - NULL, NULL); - - kfree(cnx->msgs); - veth_cnx[cnx->remote_lp] = NULL; - kfree(cnx); -} - -static void veth_release_connection(struct kobject *kobj) -{ - struct veth_lpar_connection *cnx; - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - veth_stop_connection(cnx); - veth_destroy_connection(cnx); -} - -/* - * net_device code - */ - -static int veth_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int veth_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int veth_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > VETH_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static void veth_set_multicast_list(struct net_device *dev) -{ - struct veth_port *port = netdev_priv(dev); - unsigned long flags; - - write_lock_irqsave(&port->mcast_gate, flags); - - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || - (netdev_mc_count(dev) > VETH_MAX_MCAST)) { - port->promiscuous = 1; - } else { - struct netdev_hw_addr *ha; - - port->promiscuous = 0; - - /* Update table */ - port->num_mcast = 0; - - netdev_for_each_mc_addr(ha, dev) { - u8 *addr = ha->addr; - u64 xaddr = 0; - - memcpy(&xaddr, addr, ETH_ALEN); - port->mcast_addr[port->num_mcast] = xaddr; - port->num_mcast++; - } - } - - write_unlock_irqrestore(&port->mcast_gate, flags); -} - -static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - info->driver[sizeof(info->driver) - 1] = '\0'; - strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); - info->version[sizeof(info->version) - 1] = '\0'; -} - -static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -{ - ecmd->supported = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->advertising = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = 0; - ecmd->speed = SPEED_1000; - ecmd->duplex = DUPLEX_FULL; - ecmd->autoneg = AUTONEG_ENABLE; - ecmd->maxtxpkt = 120; - ecmd->maxrxpkt = 120; - return 0; -} - -static const struct ethtool_ops ops = { - .get_drvinfo = veth_get_drvinfo, - .get_settings = veth_get_settings, - .get_link = ethtool_op_get_link, -}; - -static const struct net_device_ops veth_netdev_ops = { - .ndo_open = veth_open, - .ndo_stop = veth_close, - .ndo_start_xmit = veth_start_xmit, - .ndo_change_mtu = veth_change_mtu, - .ndo_set_rx_mode = veth_set_multicast_list, - .ndo_set_mac_address = NULL, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct net_device *veth_probe_one(int vlan, - struct vio_dev *vio_dev) -{ - struct net_device *dev; - struct veth_port *port; - struct device *vdev = &vio_dev->dev; - int i, rc; - const unsigned char *mac_addr; - - mac_addr = vio_get_attribute(vio_dev, "local-mac-address", NULL); - if (mac_addr == NULL) - mac_addr = vio_get_attribute(vio_dev, "mac-address", NULL); - if (mac_addr == NULL) { - veth_error("Unable to fetch MAC address from device tree.\n"); - return NULL; - } - - dev = alloc_etherdev(sizeof (struct veth_port)); - if (!dev) - return NULL; - - port = netdev_priv(dev); - - spin_lock_init(&port->queue_lock); - rwlock_init(&port->mcast_gate); - port->stopped_map = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - HvLpVirtualLanIndexMap map; - - if (i == this_lp) - continue; - map = HvLpConfig_getVirtualLanIndexMapForLp(i); - if (map & (0x8000 >> vlan)) - port->lpar_map |= (1 << i); - } - port->dev = vdev; - - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - - dev->mtu = VETH_MAX_MTU; - - memcpy(&port->mac_addr, mac_addr, ETH_ALEN); - - dev->netdev_ops = &veth_netdev_ops; - SET_ETHTOOL_OPS(dev, &ops); - - SET_NETDEV_DEV(dev, vdev); - - rc = register_netdev(dev); - if (rc != 0) { - veth_error("Failed registering net device for vlan%d.\n", vlan); - free_netdev(dev); - return NULL; - } - - kobject_init(&port->kobject, &veth_port_ktype); - if (0 != kobject_add(&port->kobject, &dev->dev.kobj, "veth_port")) - veth_error("Failed adding port for %s to sysfs.\n", dev->name); - - veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n", - dev->name, vlan, port->lpar_map); - - return dev; -} - -/* - * Tx path - */ - -static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, - struct net_device *dev) -{ - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - struct veth_port *port = netdev_priv(dev); - HvLpEvent_Rc rc; - struct veth_msg *msg = NULL; - unsigned long flags; - - if (! cnx) - return 0; - - spin_lock_irqsave(&cnx->lock, flags); - - if (! (cnx->state & VETH_STATE_READY)) - goto no_error; - - if ((skb->len - ETH_HLEN) > VETH_MAX_MTU) - goto drop; - - msg = veth_stack_pop(cnx); - if (! msg) - goto drop; - - msg->in_use = 1; - msg->skb = skb_get(skb); - - msg->data.addr[0] = dma_map_single(port->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - if (dma_mapping_error(port->dev, msg->data.addr[0])) - goto recycle_and_drop; - - msg->dev = port->dev; - msg->data.len[0] = skb->len; - msg->data.eofmask = 1 << VETH_EOF_SHIFT; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data); - - if (rc != HvLpEvent_Rc_Good) - goto recycle_and_drop; - - /* If the timer's not already running, start it now. */ - if (0 == cnx->outstanding_tx) - mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout); - - cnx->last_contact = jiffies; - cnx->outstanding_tx++; - - if (veth_stack_is_empty(cnx)) - veth_stop_queues(cnx); - - no_error: - spin_unlock_irqrestore(&cnx->lock, flags); - return 0; - - recycle_and_drop: - veth_recycle_msg(cnx, msg); - drop: - spin_unlock_irqrestore(&cnx->lock, flags); - return 1; -} - -static void veth_transmit_to_many(struct sk_buff *skb, - HvLpIndexMap lpmask, - struct net_device *dev) -{ - int i, success, error; - - success = error = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - if ((lpmask & (1 << i)) == 0) - continue; - - if (veth_transmit_to_one(skb, i, dev)) - error = 1; - else - success = 1; - } - - if (error) - dev->stats.tx_errors++; - - if (success) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - } -} - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *frame = skb->data; - struct veth_port *port = netdev_priv(dev); - HvLpIndexMap lpmask; - - if (is_unicast_ether_addr(frame)) { - /* unicast packet */ - HvLpIndex rlp = frame[5]; - - if ( ! ((1 << rlp) & port->lpar_map) ) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - lpmask = 1 << rlp; - } else { - lpmask = port->lpar_map; - } - - veth_transmit_to_many(skb, lpmask, dev); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* You must hold the connection's lock when you call this function. */ -static void veth_recycle_msg(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - u32 dma_address, dma_length; - - if (msg->in_use) { - msg->in_use = 0; - dma_address = msg->data.addr[0]; - dma_length = msg->data.len[0]; - - if (!dma_mapping_error(msg->dev, dma_address)) - dma_unmap_single(msg->dev, dma_address, dma_length, - DMA_TO_DEVICE); - - if (msg->skb) { - dev_kfree_skb_any(msg->skb); - msg->skb = NULL; - } - - memset(&msg->data, 0, sizeof(msg->data)); - veth_stack_push(cnx, msg); - } else if (cnx->state & VETH_STATE_OPEN) { - veth_error("Non-pending frame (# %d) acked by LPAR %d.\n", - cnx->remote_lp, msg->token); - } -} - -static void veth_wake_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - unsigned long flags; - - if (! dev) - continue; - - port = netdev_priv(dev); - - if (! (port->lpar_map & (1<<cnx->remote_lp))) - continue; - - spin_lock_irqsave(&port->queue_lock, flags); - - port->stopped_map &= ~(1 << cnx->remote_lp); - - if (0 == port->stopped_map && netif_queue_stopped(dev)) { - veth_debug("cnx %d: woke queue for %s.\n", - cnx->remote_lp, dev->name); - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&port->queue_lock, flags); - } -} - -static void veth_stop_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - - if (! dev) - continue; - - port = netdev_priv(dev); - - /* If this cnx is not on the vlan for this port, continue */ - if (! (port->lpar_map & (1 << cnx->remote_lp))) - continue; - - spin_lock(&port->queue_lock); - - netif_stop_queue(dev); - port->stopped_map |= (1 << cnx->remote_lp); - - veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n", - cnx->remote_lp, dev->name, port->stopped_map); - - spin_unlock(&port->queue_lock); - } -} - -static void veth_timed_reset(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr; - unsigned long trigger_time, flags; - - /* FIXME is it possible this fires after veth_stop_connection()? - * That would reschedule the statemachine for 5 seconds and probably - * execute it after the module's been unloaded. Hmm. */ - - spin_lock_irqsave(&cnx->lock, flags); - - if (cnx->outstanding_tx > 0) { - trigger_time = cnx->last_contact + cnx->reset_timeout; - - if (trigger_time < jiffies) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - veth_error("%d packets not acked by LPAR %d within %d " - "seconds, resetting.\n", - cnx->outstanding_tx, cnx->remote_lp, - cnx->reset_timeout / HZ); - } else { - /* Reschedule the timer */ - trigger_time = jiffies + cnx->reset_timeout; - mod_timer(&cnx->reset_timer, trigger_time); - } - } - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -/* - * Rx path - */ - -static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr) -{ - int wanted = 0; - int i; - unsigned long flags; - - if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) ) - return 1; - - read_lock_irqsave(&port->mcast_gate, flags); - - if (port->promiscuous) { - wanted = 1; - goto out; - } - - for (i = 0; i < port->num_mcast; ++i) { - if (port->mcast_addr[i] == mac_addr) { - wanted = 1; - break; - } - } - - out: - read_unlock_irqrestore(&port->mcast_gate, flags); - - return wanted; -} - -struct dma_chunk { - u64 addr; - u64 size; -}; - -#define VETH_MAX_PAGES_PER_FRAME ( (VETH_MAX_MTU+PAGE_SIZE-2)/PAGE_SIZE + 1 ) - -static inline void veth_build_dma_list(struct dma_chunk *list, - unsigned char *p, unsigned long length) -{ - unsigned long done; - int i = 1; - - /* FIXME: skbs are contiguous in real addresses. Do we - * really need to break it into PAGE_SIZE chunks, or can we do - * it just at the granularity of iSeries real->absolute - * mapping? Indeed, given the way the allocator works, can we - * count on them being absolutely contiguous? */ - list[0].addr = iseries_hv_addr(p); - list[0].size = min(length, - PAGE_SIZE - ((unsigned long)p & ~PAGE_MASK)); - - done = list[0].size; - while (done < length) { - list[i].addr = iseries_hv_addr(p + done); - list[i].size = min(length-done, PAGE_SIZE); - done += list[i].size; - i++; - } -} - -static void veth_flush_acks(struct veth_lpar_connection *cnx) -{ - HvLpEvent_Rc rc; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK, - 0, &cnx->pending_acks); - - if (rc != HvLpEvent_Rc_Good) - veth_error("Failed acking frames from LPAR %d, rc = %d\n", - cnx->remote_lp, (int)rc); - - cnx->num_pending_acks = 0; - memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks)); -} - -static void veth_receive(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - struct veth_frames_data *senddata = &event->u.frames_data; - int startchunk = 0; - int nchunks; - unsigned long flags; - HvLpDma_Rc rc; - - do { - u16 length = 0; - struct sk_buff *skb; - struct dma_chunk local_list[VETH_MAX_PAGES_PER_FRAME]; - struct dma_chunk remote_list[VETH_MAX_FRAMES_PER_MSG]; - u64 dest; - HvLpVirtualLanIndex vlan; - struct net_device *dev; - struct veth_port *port; - - /* FIXME: do we need this? */ - memset(local_list, 0, sizeof(local_list)); - memset(remote_list, 0, sizeof(remote_list)); - - /* a 0 address marks the end of the valid entries */ - if (senddata->addr[startchunk] == 0) - break; - - /* make sure that we have at least 1 EOF entry in the - * remaining entries */ - if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) { - veth_error("Missing EOF fragment in event " - "eofmask = 0x%x startchunk = %d\n", - (unsigned)senddata->eofmask, - startchunk); - break; - } - - /* build list of chunks in this frame */ - nchunks = 0; - do { - remote_list[nchunks].addr = - (u64) senddata->addr[startchunk+nchunks] << 32; - remote_list[nchunks].size = - senddata->len[startchunk+nchunks]; - length += remote_list[nchunks].size; - } while (! (senddata->eofmask & - (1 << (VETH_EOF_SHIFT + startchunk + nchunks++)))); - - /* length == total length of all chunks */ - /* nchunks == # of chunks in this frame */ - - if ((length - ETH_HLEN) > VETH_MAX_MTU) { - veth_error("Received oversize frame from LPAR %d " - "(length = %d)\n", - cnx->remote_lp, length); - continue; - } - - skb = alloc_skb(length, GFP_ATOMIC); - if (!skb) - continue; - - veth_build_dma_list(local_list, skb->data, length); - - rc = HvCallEvent_dmaBufList(HvLpEvent_Type_VirtualLan, - event->base_event.xSourceLp, - HvLpDma_Direction_RemoteToLocal, - cnx->src_inst, - cnx->dst_inst, - HvLpDma_AddressType_RealAddress, - HvLpDma_AddressType_TceIndex, - iseries_hv_addr(&local_list), - iseries_hv_addr(&remote_list), - length); - if (rc != HvLpDma_Rc_Good) { - dev_kfree_skb_irq(skb); - continue; - } - - vlan = skb->data[9]; - dev = veth_dev[vlan]; - if (! dev) { - /* - * Some earlier versions of the driver sent - * broadcasts down all connections, even to lpars - * that weren't on the relevant vlan. So ignore - * packets belonging to a vlan we're not on. - * We can also be here if we receive packets while - * the driver is going down, because then dev is NULL. - */ - dev_kfree_skb_irq(skb); - continue; - } - - port = netdev_priv(dev); - dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000; - - if ((vlan > HVMAXARCHITECTEDVIRTUALLANS) || !port) { - dev_kfree_skb_irq(skb); - continue; - } - if (! veth_frame_wanted(port, dest)) { - dev_kfree_skb_irq(skb); - continue; - } - - skb_put(skb, length); - skb->protocol = eth_type_trans(skb, dev); - skb_checksum_none_assert(skb); - netif_rx(skb); /* send it up */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; - } while (startchunk += nchunks, startchunk < VETH_MAX_FRAMES_PER_MSG); - - /* Ack it */ - spin_lock_irqsave(&cnx->lock, flags); - BUG_ON(cnx->num_pending_acks > VETH_MAX_ACKS_PER_MSG); - - cnx->pending_acks[cnx->num_pending_acks++] = - event->base_event.xCorrelationToken; - - if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) || - (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) ) - veth_flush_acks(cnx); - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_timed_ack(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *) ptr; - unsigned long flags; - - /* Ack all the events */ - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->num_pending_acks > 0) - veth_flush_acks(cnx); - - /* Reschedule the timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static int veth_remove(struct vio_dev *vdev) -{ - struct veth_lpar_connection *cnx; - struct net_device *dev; - struct veth_port *port; - int i; - - dev = veth_dev[vdev->unit_address]; - - if (! dev) - return 0; - - port = netdev_priv(dev); - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - cnx = veth_cnx[i]; - - if (cnx && (port->lpar_map & (1 << i))) { - /* Drop our reference to connections on our VLAN */ - kobject_put(&cnx->kobject); - } - } - - veth_dev[vdev->unit_address] = NULL; - kobject_del(&port->kobject); - kobject_put(&port->kobject); - unregister_netdev(dev); - free_netdev(dev); - - return 0; -} - -static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - int i = vdev->unit_address; - struct net_device *dev; - struct veth_port *port; - - dev = veth_probe_one(i, vdev); - if (dev == NULL) { - veth_remove(vdev); - return 1; - } - veth_dev[i] = dev; - - port = netdev_priv(dev); - - /* Start the state machine on each connection on this vlan. If we're - * the first dev to do so this will commence link negotiation */ - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - struct veth_lpar_connection *cnx; - - if (! (port->lpar_map & (1 << i))) - continue; - - cnx = veth_cnx[i]; - if (!cnx) - continue; - - kobject_get(&cnx->kobject); - veth_kick_statemachine(cnx); - } - - return 0; -} - -/** - * veth_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id veth_device_table[] __devinitdata = { - { "network", "IBM,iSeries-l-lan" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, veth_device_table); - -static struct vio_driver veth_driver = { - .id_table = veth_device_table, - .probe = veth_probe, - .remove = veth_remove, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - } -}; - -/* - * Module initialization/cleanup - */ - -static void __exit veth_module_cleanup(void) -{ - int i; - struct veth_lpar_connection *cnx; - - /* Disconnect our "irq" to stop events coming from the Hypervisor. */ - HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - cnx = veth_cnx[i]; - - if (!cnx) - continue; - - /* Cancel work queued from Hypervisor callbacks */ - cancel_delayed_work_sync(&cnx->statemachine_wq); - /* Remove the connection from sysfs */ - kobject_del(&cnx->kobject); - /* Drop the driver's reference to the connection */ - kobject_put(&cnx->kobject); - } - - /* Unregister the driver, which will close all the netdevs and stop - * the connections when they're no longer referenced. */ - vio_unregister_driver(&veth_driver); -} -module_exit(veth_module_cleanup); - -static int __init veth_module_init(void) -{ - int i; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - this_lp = HvLpConfig_getLpIndex_outline(); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - rc = veth_init_connection(i); - if (rc != 0) - goto error; - } - - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, - &veth_handle_event); - - rc = vio_register_driver(&veth_driver); - if (rc != 0) - goto error; - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - struct kobject *kobj; - - if (!veth_cnx[i]) - continue; - - kobj = &veth_cnx[i]->kobject; - /* If the add failes, complain but otherwise continue */ - if (0 != driver_add_kobj(&veth_driver.driver, kobj, - "cnx%.2d", veth_cnx[i]->remote_lp)) - veth_error("cnx %d: Failed adding to sysfs.\n", i); - } - - return 0; - -error: - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - veth_destroy_connection(veth_cnx[i]); - } - - return rc; -} -module_init(veth_module_init); diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 1908ba7ca7e6..3cbfbffe3f00 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -25,6 +25,7 @@ #include "net_driver.h" #include "efx.h" #include "nic.h" +#include "selftest.h" #include "mcdi.h" #include "workarounds.h" @@ -163,12 +164,12 @@ static int phy_flash_cfg; module_param(phy_flash_cfg, int, 0644); MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); -static unsigned irq_adapt_low_thresh = 10000; +static unsigned irq_adapt_low_thresh = 8000; module_param(irq_adapt_low_thresh, uint, 0644); MODULE_PARM_DESC(irq_adapt_low_thresh, "Threshold score for reducing IRQ moderation"); -static unsigned irq_adapt_high_thresh = 20000; +static unsigned irq_adapt_high_thresh = 16000; module_param(irq_adapt_high_thresh, uint, 0644); MODULE_PARM_DESC(irq_adapt_high_thresh, "Threshold score for increasing IRQ moderation"); @@ -1564,8 +1565,9 @@ static void efx_start_all(struct efx_nic *efx) * since we're holding the rtnl_lock at this point. */ static void efx_flush_all(struct efx_nic *efx) { - /* Make sure the hardware monitor is stopped */ + /* Make sure the hardware monitor and event self-test are stopped */ cancel_delayed_work_sync(&efx->monitor_work); + efx_selftest_async_cancel(efx); /* Stop scheduled port reconfigurations */ cancel_work_sync(&efx->mac_work); } @@ -1825,6 +1827,7 @@ static int efx_net_open(struct net_device *net_dev) efx_link_status_changed(efx); efx_start_all(efx); + efx_selftest_async_start(efx); return 0; } @@ -2375,6 +2378,7 @@ static int efx_init_struct(struct efx_nic *efx, const struct efx_nic_type *type, #endif INIT_WORK(&efx->reset_work, efx_reset_work); INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor); + INIT_DELAYED_WORK(&efx->selftest_work, efx_selftest_async_work); efx->pci_dev = pci_dev; efx->msg_enable = debug; efx->state = STATE_INIT; @@ -2493,6 +2497,57 @@ static void efx_pci_remove(struct pci_dev *pci_dev) free_netdev(efx->net_dev); }; +/* NIC VPD information + * Called during probe to display the part number of the + * installed NIC. VPD is potentially very large but this should + * always appear within the first 512 bytes. + */ +#define SFC_VPD_LEN 512 +static void efx_print_product_vpd(struct efx_nic *efx) +{ + struct pci_dev *dev = efx->pci_dev; + char vpd_data[SFC_VPD_LEN]; + ssize_t vpd_size; + int i, j; + + /* Get the vpd data from the device */ + vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data); + if (vpd_size <= 0) { + netif_err(efx, drv, efx->net_dev, "Unable to read VPD\n"); + return; + } + + /* Get the Read only section */ + i = pci_vpd_find_tag(vpd_data, 0, vpd_size, PCI_VPD_LRDT_RO_DATA); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "VPD Read-only not found\n"); + return; + } + + j = pci_vpd_lrdt_size(&vpd_data[i]); + i += PCI_VPD_LRDT_TAG_SIZE; + if (i + j > vpd_size) + j = vpd_size - i; + + /* Get the Part number */ + i = pci_vpd_find_info_keyword(vpd_data, i, j, "PN"); + if (i < 0) { + netif_err(efx, drv, efx->net_dev, "Part number not found\n"); + return; + } + + j = pci_vpd_info_field_size(&vpd_data[i]); + i += PCI_VPD_INFO_FLD_HDR_SIZE; + if (i + j > vpd_size) { + netif_err(efx, drv, efx->net_dev, "Incomplete part number\n"); + return; + } + + netif_info(efx, drv, efx->net_dev, + "Part Number : %.*s\n", j, &vpd_data[i]); +} + + /* Main body of NIC initialisation * This is called at module load (or hotplug insertion, theoretically). */ @@ -2582,6 +2637,8 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev, netif_info(efx, probe, efx->net_dev, "Solarflare NIC detected\n"); + efx_print_product_vpd(efx); + /* Set up basic I/O (BAR mappings etc) */ rc = efx_init_io(efx); if (rc) diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index 4debfe07fb88..be8f9158a714 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -148,7 +148,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel) static inline void efx_schedule_channel_irq(struct efx_channel *channel) { - channel->last_irq_cpu = raw_smp_processor_id(); + channel->event_test_cpu = raw_smp_processor_id(); efx_schedule_channel(channel); } diff --git a/drivers/net/ethernet/sfc/falcon_boards.c b/drivers/net/ethernet/sfc/falcon_boards.c index 2084cc6ede52..8687a6c3db0d 100644 --- a/drivers/net/ethernet/sfc/falcon_boards.c +++ b/drivers/net/ethernet/sfc/falcon_boards.c @@ -709,8 +709,6 @@ static int sfe4003_init(struct efx_nic *efx) static const struct falcon_board_type board_types[] = { { .id = FALCON_BOARD_SFE4001, - .ref_model = "SFE4001", - .gen_type = "10GBASE-T adapter", .init = sfe4001_init, .init_phy = efx_port_dummy_op_void, .fini = sfe4001_fini, @@ -719,8 +717,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFE4002, - .ref_model = "SFE4002", - .gen_type = "XFP adapter", .init = sfe4002_init, .init_phy = sfe4002_init_phy, .fini = efx_fini_lm87, @@ -729,8 +725,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFE4003, - .ref_model = "SFE4003", - .gen_type = "10GBASE-CX4 adapter", .init = sfe4003_init, .init_phy = sfe4003_init_phy, .fini = efx_fini_lm87, @@ -739,8 +733,6 @@ static const struct falcon_board_type board_types[] = { }, { .id = FALCON_BOARD_SFN4112F, - .ref_model = "SFN4112F", - .gen_type = "SFP+ adapter", .init = sfn4112f_init, .init_phy = sfn4112f_init_phy, .fini = efx_fini_lm87, @@ -763,11 +755,6 @@ int falcon_probe_board(struct efx_nic *efx, u16 revision_info) board->type = &board_types[i]; if (board->type) { - netif_info(efx, probe, efx->net_dev, "board is %s rev %c%d\n", - (efx->pci_dev->subsystem_vendor == - PCI_VENDOR_ID_SOLARFLARE) - ? board->type->ref_model : board->type->gen_type, - 'A' + board->major, board->minor); return 0; } else { netif_err(efx, probe, efx->net_dev, "unknown board type %d\n", diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 0b95505e8968..f0385e1fb2d8 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -324,8 +324,7 @@ enum efx_rx_alloc_method { * @eventq: Event queue buffer * @eventq_mask: Event queue pointer mask * @eventq_read_ptr: Event queue read pointer - * @last_eventq_read_ptr: Last event queue read pointer value. - * @last_irq_cpu: Last CPU to handle interrupt for this channel + * @event_test_cpu: Last CPU to handle interrupt or test event for this channel * @irq_count: Number of IRQs since last adaptive moderation decision * @irq_mod_score: IRQ moderation score * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors @@ -355,9 +354,8 @@ struct efx_channel { struct efx_special_buffer eventq; unsigned int eventq_mask; unsigned int eventq_read_ptr; - unsigned int last_eventq_read_ptr; + int event_test_cpu; - int last_irq_cpu; unsigned int irq_count; unsigned int irq_mod_score; #ifdef CONFIG_RFS_ACCEL @@ -678,6 +676,7 @@ struct vfdi_status; * @irq_status: Interrupt status buffer * @irq_zero_count: Number of legacy IRQs seen with queue flags == 0 * @irq_level: IRQ level/index for IRQs not triggered by an event queue + * @selftest_work: Work item for asynchronous self-test * @mtd_list: List of MTDs attached to the NIC * @nic_data: Hardware dependent state * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode, @@ -791,6 +790,7 @@ struct efx_nic { struct efx_buffer irq_status; unsigned irq_zero_count; unsigned irq_level; + struct delayed_work selftest_work; #ifdef CONFIG_SFC_MTD struct list_head mtd_list; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 2bf4283f05fe..4a9a5beec8fc 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -822,7 +822,6 @@ efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event) channel, tx_ev_q_label % EFX_TXQ_TYPES); tx_packets = ((tx_ev_desc_ptr - tx_queue->read_count) & tx_queue->ptr_mask); - channel->irq_mod_score += tx_packets; efx_xmit_done(tx_queue, tx_ev_desc_ptr); } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) { /* Rewrite the FIFO write pointer */ @@ -1084,7 +1083,7 @@ efx_handle_generated_event(struct efx_channel *channel, efx_qword_t *event) code = _EFX_CHANNEL_MAGIC_CODE(magic); if (magic == EFX_CHANNEL_MAGIC_TEST(channel)) { - /* ignore */ + channel->event_test_cpu = raw_smp_processor_id(); } else if (rx_queue && magic == EFX_CHANNEL_MAGIC_FILL(rx_queue)) { /* The queue must be empty, so we won't receive any rx * events, so efx_process_channel() won't refill the @@ -1333,8 +1332,10 @@ void efx_nic_remove_eventq(struct efx_channel *channel) } -void efx_nic_generate_test_event(struct efx_channel *channel) +void efx_nic_event_test_start(struct efx_channel *channel) { + channel->event_test_cpu = -1; + smp_wmb(); efx_magic_event(channel, EFX_CHANNEL_MAGIC_TEST(channel)); } @@ -1383,8 +1384,10 @@ void efx_nic_disable_interrupts(struct efx_nic *efx) * Interrupt must already have been enabled, otherwise nasty things * may happen. */ -void efx_nic_generate_interrupt(struct efx_nic *efx) +void efx_nic_irq_test_start(struct efx_nic *efx) { + efx->last_irq_cpu = -1; + smp_wmb(); efx_nic_interrupts(efx, true, true); } diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 246c4140453c..f48ccf6bb3b9 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -35,10 +35,6 @@ static inline int efx_nic_rev(struct efx_nic *efx) extern u32 efx_nic_fpga_ver(struct efx_nic *efx); -static inline bool efx_nic_has_mc(struct efx_nic *efx) -{ - return efx_nic_rev(efx) >= EFX_REV_SIENA_A0; -} /* NIC has two interlinked PCI functions for the same port. */ static inline bool efx_nic_is_dual_func(struct efx_nic *efx) { @@ -73,8 +69,6 @@ enum { /** * struct falcon_board_type - board operations and type information * @id: Board type id, as found in NVRAM - * @ref_model: Model number of Solarflare reference design - * @gen_type: Generic board type description * @init: Allocate resources and initialise peripheral hardware * @init_phy: Do board-specific PHY initialisation * @fini: Shut down hardware and free resources @@ -83,8 +77,6 @@ enum { */ struct falcon_board_type { u8 id; - const char *ref_model; - const char *gen_type; int (*init) (struct efx_nic *nic); void (*init_phy) (struct efx_nic *efx); void (*fini) (struct efx_nic *nic); @@ -305,14 +297,23 @@ extern void falcon_update_stats_xmac(struct efx_nic *efx); /* Interrupts and test events */ extern int efx_nic_init_interrupt(struct efx_nic *efx); extern void efx_nic_enable_interrupts(struct efx_nic *efx); -extern void efx_nic_generate_test_event(struct efx_channel *channel); -extern void efx_nic_generate_interrupt(struct efx_nic *efx); +extern void efx_nic_event_test_start(struct efx_channel *channel); +extern void efx_nic_irq_test_start(struct efx_nic *efx); extern void efx_nic_disable_interrupts(struct efx_nic *efx); extern void efx_nic_fini_interrupt(struct efx_nic *efx); extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx); extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id); extern void falcon_irq_ack_a1(struct efx_nic *efx); +static inline int efx_nic_event_test_irq_cpu(struct efx_channel *channel) +{ + return ACCESS_ONCE(channel->event_test_cpu); +} +static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx) +{ + return ACCESS_ONCE(efx->last_irq_cpu); +} + /* Global Resources */ extern int efx_nic_flush_queues(struct efx_nic *efx); extern void falcon_start_nic_stats(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 1ba290d0c21c..763fa2fe1a38 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -449,10 +449,8 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, efx_rx_queue_channel(rx_queue)->n_rx_overlength++; } -/* Pass a received packet up through the generic GRO stack - * - * Handles driverlink veto, and passes the fragment up via - * the appropriate GRO method +/* Pass a received packet up through GRO. GRO can handle pages + * regardless of checksum state and skbs with a good checksum. */ static void efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, @@ -461,7 +459,6 @@ static void efx_rx_packet_gro(struct efx_channel *channel, struct napi_struct *napi = &channel->napi_str; gro_result_t gro_result; - /* Pass the skb/page into the GRO engine */ if (rx_buf->flags & EFX_RX_BUF_PAGE) { struct efx_nic *efx = channel->efx; struct page *page = rx_buf->u.page; diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index febe2a9e6211..de4c0069f5b2 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -25,6 +25,16 @@ #include "selftest.h" #include "workarounds.h" +/* IRQ latency can be enormous because: + * - All IRQs may be disabled on a CPU for a *long* time by e.g. a + * slow serial console or an old IDE driver doing error recovery + * - The PREEMPT_RT patches mostly deal with this, but also allow a + * tasklet or normal task to be given higher priority than our IRQ + * threads + * Try to avoid blaming the hardware for this. + */ +#define IRQ_TIMEOUT HZ + /* * Loopback test packet structure * @@ -77,6 +87,9 @@ struct efx_loopback_state { struct efx_loopback_payload payload; }; +/* How long to wait for all the packets to arrive (in ms) */ +#define LOOPBACK_TIMEOUT_MS 1000 + /************************************************************************** * * MII, NVRAM and register tests @@ -130,23 +143,25 @@ static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) static int efx_test_interrupts(struct efx_nic *efx, struct efx_self_tests *tests) { + unsigned long timeout, wait; int cpu; netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n"); tests->interrupt = -1; - /* Reset interrupt flag */ - efx->last_irq_cpu = -1; - smp_wmb(); - - efx_nic_generate_interrupt(efx); + efx_nic_irq_test_start(efx); + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; /* Wait for arrival of test interrupt. */ netif_dbg(efx, drv, efx->net_dev, "waiting for test interrupt\n"); - schedule_timeout_uninterruptible(HZ / 10); - cpu = ACCESS_ONCE(efx->last_irq_cpu); - if (cpu >= 0) - goto success; + do { + schedule_timeout_uninterruptible(wait); + cpu = efx_nic_irq_test_irq_cpu(efx); + if (cpu >= 0) + goto success; + wait *= 2; + } while (time_before(jiffies, timeout)); netif_err(efx, drv, efx->net_dev, "timed out waiting for interrupt\n"); return -ETIMEDOUT; @@ -159,61 +174,86 @@ static int efx_test_interrupts(struct efx_nic *efx, } /* Test generation and receipt of interrupting events */ -static int efx_test_eventq_irq(struct efx_channel *channel, +static int efx_test_eventq_irq(struct efx_nic *efx, struct efx_self_tests *tests) { - struct efx_nic *efx = channel->efx; - unsigned int read_ptr; - bool napi_ran, dma_seen, int_seen; + struct efx_channel *channel; + unsigned int read_ptr[EFX_MAX_CHANNELS]; + unsigned long napi_ran = 0, dma_pend = 0, int_pend = 0; + unsigned long timeout, wait; - read_ptr = channel->eventq_read_ptr; - channel->last_irq_cpu = -1; - smp_wmb(); + BUILD_BUG_ON(EFX_MAX_CHANNELS > BITS_PER_LONG); - efx_nic_generate_test_event(channel); + efx_for_each_channel(channel, efx) { + read_ptr[channel->channel] = channel->eventq_read_ptr; + set_bit(channel->channel, &dma_pend); + set_bit(channel->channel, &int_pend); + efx_nic_event_test_start(channel); + } - /* Wait for arrival of interrupt. NAPI processing may or may + timeout = jiffies + IRQ_TIMEOUT; + wait = 1; + + /* Wait for arrival of interrupts. NAPI processing may or may * not complete in time, but we can cope in any case. */ - msleep(10); - napi_disable(&channel->napi_str); - if (channel->eventq_read_ptr != read_ptr) { - napi_ran = true; - dma_seen = true; - int_seen = true; - } else { - napi_ran = false; - dma_seen = efx_nic_event_present(channel); - int_seen = ACCESS_ONCE(channel->last_irq_cpu) >= 0; - } - napi_enable(&channel->napi_str); - efx_nic_eventq_read_ack(channel); + do { + schedule_timeout_uninterruptible(wait); + + efx_for_each_channel(channel, efx) { + napi_disable(&channel->napi_str); + if (channel->eventq_read_ptr != + read_ptr[channel->channel]) { + set_bit(channel->channel, &napi_ran); + clear_bit(channel->channel, &dma_pend); + clear_bit(channel->channel, &int_pend); + } else { + if (efx_nic_event_present(channel)) + clear_bit(channel->channel, &dma_pend); + if (efx_nic_event_test_irq_cpu(channel) >= 0) + clear_bit(channel->channel, &int_pend); + } + napi_enable(&channel->napi_str); + efx_nic_eventq_read_ack(channel); + } - tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; - tests->eventq_int[channel->channel] = int_seen ? 1 : -1; + wait *= 2; + } while ((dma_pend || int_pend) && time_before(jiffies, timeout)); - if (dma_seen && int_seen) { - netif_dbg(efx, drv, efx->net_dev, - "channel %d event queue passed (with%s NAPI)\n", - channel->channel, napi_ran ? "" : "out"); - return 0; - } else { - /* Report failure and whether either interrupt or DMA worked */ - netif_err(efx, drv, efx->net_dev, - "channel %d timed out waiting for event queue\n", - channel->channel); - if (int_seen) - netif_err(efx, drv, efx->net_dev, - "channel %d saw interrupt " - "during event queue test\n", - channel->channel); - if (dma_seen) + efx_for_each_channel(channel, efx) { + bool dma_seen = !test_bit(channel->channel, &dma_pend); + bool int_seen = !test_bit(channel->channel, &int_pend); + + tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; + tests->eventq_int[channel->channel] = int_seen ? 1 : -1; + + if (dma_seen && int_seen) { + netif_dbg(efx, drv, efx->net_dev, + "channel %d event queue passed (with%s NAPI)\n", + channel->channel, + test_bit(channel->channel, &napi_ran) ? + "" : "out"); + } else { + /* Report failure and whether either interrupt or DMA + * worked + */ netif_err(efx, drv, efx->net_dev, - "channel %d event was generated, but " - "failed to trigger an interrupt\n", + "channel %d timed out waiting for event queue\n", channel->channel); - return -ETIMEDOUT; + if (int_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d saw interrupt " + "during event queue test\n", + channel->channel); + if (dma_seen) + netif_err(efx, drv, efx->net_dev, + "channel %d event was generated, but " + "failed to trigger an interrupt\n", + channel->channel); + } } + + return (dma_pend || int_pend) ? -ETIMEDOUT : 0; } static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests, @@ -516,10 +556,10 @@ efx_test_loopback(struct efx_tx_queue *tx_queue, begin_rc = efx_begin_loopback(tx_queue); /* This will normally complete very quickly, but be - * prepared to wait up to 100 ms. */ + * prepared to wait much longer. */ msleep(1); if (!efx_poll_loopback(efx)) { - msleep(100); + msleep(LOOPBACK_TIMEOUT_MS); efx_poll_loopback(efx); } @@ -660,9 +700,10 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, enum efx_loopback_mode loopback_mode = efx->loopback_mode; int phy_mode = efx->phy_mode; enum reset_type reset_method = RESET_TYPE_INVISIBLE; - struct efx_channel *channel; int rc_test = 0, rc_reset = 0, rc; + efx_selftest_async_cancel(efx); + /* Online (i.e. non-disruptive) testing * This checks interrupt generation, event delivery and PHY presence. */ @@ -678,11 +719,9 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, if (rc && !rc_test) rc_test = rc; - efx_for_each_channel(channel, efx) { - rc = efx_test_eventq_irq(channel, tests); - if (rc && !rc_test) - rc_test = rc; - } + rc = efx_test_eventq_irq(efx, tests); + if (rc && !rc_test) + rc_test = rc; if (rc_test) return rc_test; @@ -757,3 +796,36 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, return rc_test; } +void efx_selftest_async_start(struct efx_nic *efx) +{ + struct efx_channel *channel; + + efx_for_each_channel(channel, efx) + efx_nic_event_test_start(channel); + schedule_delayed_work(&efx->selftest_work, IRQ_TIMEOUT); +} + +void efx_selftest_async_cancel(struct efx_nic *efx) +{ + cancel_delayed_work_sync(&efx->selftest_work); +} + +void efx_selftest_async_work(struct work_struct *data) +{ + struct efx_nic *efx = container_of(data, struct efx_nic, + selftest_work.work); + struct efx_channel *channel; + int cpu; + + efx_for_each_channel(channel, efx) { + cpu = efx_nic_event_test_irq_cpu(channel); + if (cpu < 0) + netif_err(efx, ifup, efx->net_dev, + "channel %d failed to trigger an interrupt\n", + channel->channel); + else + netif_dbg(efx, ifup, efx->net_dev, + "channel %d triggered interrupt on CPU %d\n", + channel->channel, cpu); + } +} diff --git a/drivers/net/ethernet/sfc/selftest.h b/drivers/net/ethernet/sfc/selftest.h index 87abe2a53846..aed24b736059 100644 --- a/drivers/net/ethernet/sfc/selftest.h +++ b/drivers/net/ethernet/sfc/selftest.h @@ -48,5 +48,8 @@ extern void efx_loopback_rx_packet(struct efx_nic *efx, extern int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, unsigned flags); +extern void efx_selftest_async_start(struct efx_nic *efx); +extern void efx_selftest_async_cancel(struct efx_nic *efx); +extern void efx_selftest_async_work(struct work_struct *data); #endif /* EFX_SELFTEST_H */ diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 7bea79017a05..9f8d7cea3967 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -409,8 +409,7 @@ static void siena_remove_nic(struct efx_nic *efx) siena_reset_hw(efx, RESET_TYPE_ALL); /* Relinquish the device back to the BMC */ - if (efx_nic_has_mc(efx)) - efx_mcdi_drv_attach(efx, false, NULL); + efx_mcdi_drv_attach(efx, false, NULL); /* Tear down the private nic state */ kfree(efx->nic_data); diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index 80976e84eee6..9cb3b84ecae9 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c @@ -514,7 +514,7 @@ static bool map_vi_index(struct efx_nic *efx, unsigned abs_index, if (abs_index < EFX_VI_BASE) return true; - vf_i = (abs_index - EFX_VI_BASE) * efx_vf_size(efx); + vf_i = (abs_index - EFX_VI_BASE) / efx_vf_size(efx); if (vf_i >= efx->vf_init_count) return true; diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c index 0b38ee1b0f85..375eb04c16ea 100644 --- a/drivers/ptp/ptp_pch.c +++ b/drivers/ptp/ptp_pch.c @@ -694,7 +694,7 @@ static DEFINE_PCI_DEVICE_TABLE(pch_ieee1588_pcidev_id) = { {0} }; -static struct pci_driver pch_pcidev = { +static struct pci_driver pch_driver = { .name = KBUILD_MODNAME, .id_table = pch_ieee1588_pcidev_id, .probe = pch_probe, @@ -705,7 +705,7 @@ static struct pci_driver pch_pcidev = { static void __exit ptp_pch_exit(void) { - pci_unregister_driver(&pch_pcidev); + pci_unregister_driver(&pch_driver); } static s32 __init ptp_pch_init(void) @@ -713,7 +713,7 @@ static s32 __init ptp_pch_init(void) s32 ret; /* register the driver with the pci core */ - ret = pci_register_driver(&pch_pcidev); + ret = pci_register_driver(&pch_driver); return ret; } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a89933bc4f2f..b195a34440bb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -417,7 +417,7 @@ typedef rx_handler_result_t rx_handler_func_t(struct sk_buff **pskb); extern void __napi_schedule(struct napi_struct *n); -static inline int napi_disable_pending(struct napi_struct *n) +static inline bool napi_disable_pending(struct napi_struct *n) { return test_bit(NAPI_STATE_DISABLE, &n->state); } @@ -431,7 +431,7 @@ static inline int napi_disable_pending(struct napi_struct *n) * insure only one NAPI poll instance runs. We also make * sure there is no pending NAPI disable. */ -static inline int napi_schedule_prep(struct napi_struct *n) +static inline bool napi_schedule_prep(struct napi_struct *n) { return !napi_disable_pending(n) && !test_and_set_bit(NAPI_STATE_SCHED, &n->state); @@ -451,13 +451,13 @@ static inline void napi_schedule(struct napi_struct *n) } /* Try to reschedule poll. Called by dev->poll() after napi_complete(). */ -static inline int napi_reschedule(struct napi_struct *napi) +static inline bool napi_reschedule(struct napi_struct *napi) { if (napi_schedule_prep(napi)) { __napi_schedule(napi); - return 1; + return true; } - return 0; + return false; } /** @@ -1868,7 +1868,7 @@ static inline void netif_tx_stop_all_queues(struct net_device *dev) } } -static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_tx_queue_stopped(const struct netdev_queue *dev_queue) { return test_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state); } @@ -1879,17 +1879,17 @@ static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) * * Test if transmit queue on device is currently unable to send. */ -static inline int netif_queue_stopped(const struct net_device *dev) +static inline bool netif_queue_stopped(const struct net_device *dev) { return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); } -static inline int netif_xmit_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_xmit_stopped(const struct netdev_queue *dev_queue) { return dev_queue->state & QUEUE_STATE_ANY_XOFF; } -static inline int netif_xmit_frozen_or_stopped(const struct netdev_queue *dev_queue) +static inline bool netif_xmit_frozen_or_stopped(const struct netdev_queue *dev_queue) { return dev_queue->state & QUEUE_STATE_ANY_XOFF_OR_FROZEN; } @@ -1954,7 +1954,7 @@ static inline void netdev_reset_queue(struct net_device *dev_queue) * * Test if the device has been brought up. */ -static inline int netif_running(const struct net_device *dev) +static inline bool netif_running(const struct net_device *dev) { return test_bit(__LINK_STATE_START, &dev->state); } @@ -2004,16 +2004,16 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) * * Check individual transmit queue of a device with multiple transmit queues. */ -static inline int __netif_subqueue_stopped(const struct net_device *dev, - u16 queue_index) +static inline bool __netif_subqueue_stopped(const struct net_device *dev, + u16 queue_index) { struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); return netif_tx_queue_stopped(txq); } -static inline int netif_subqueue_stopped(const struct net_device *dev, - struct sk_buff *skb) +static inline bool netif_subqueue_stopped(const struct net_device *dev, + struct sk_buff *skb) { return __netif_subqueue_stopped(dev, skb_get_queue_mapping(skb)); } @@ -2052,7 +2052,7 @@ static inline u16 skb_tx_hash(const struct net_device *dev, * * Check if device has multiple transmit queues */ -static inline int netif_is_multiqueue(const struct net_device *dev) +static inline bool netif_is_multiqueue(const struct net_device *dev) { return dev->num_tx_queues > 1; } @@ -2188,7 +2188,7 @@ extern void linkwatch_forget_dev(struct net_device *dev); * * Check if carrier is present on device */ -static inline int netif_carrier_ok(const struct net_device *dev) +static inline bool netif_carrier_ok(const struct net_device *dev) { return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); } @@ -2240,7 +2240,7 @@ static inline void netif_dormant_off(struct net_device *dev) * * Check if carrier is present on device */ -static inline int netif_dormant(const struct net_device *dev) +static inline bool netif_dormant(const struct net_device *dev) { return test_bit(__LINK_STATE_DORMANT, &dev->state); } @@ -2252,7 +2252,7 @@ static inline int netif_dormant(const struct net_device *dev) * * Check if carrier is operational */ -static inline int netif_oper_up(const struct net_device *dev) +static inline bool netif_oper_up(const struct net_device *dev) { return (dev->operstate == IF_OPER_UP || dev->operstate == IF_OPER_UNKNOWN /* backward compat */); @@ -2264,7 +2264,7 @@ static inline int netif_oper_up(const struct net_device *dev) * * Check if device has not been removed from system. */ -static inline int netif_device_present(struct net_device *dev) +static inline bool netif_device_present(struct net_device *dev) { return test_bit(__LINK_STATE_PRESENT, &dev->state); } @@ -2334,9 +2334,9 @@ static inline void __netif_tx_lock_bh(struct netdev_queue *txq) txq->xmit_lock_owner = smp_processor_id(); } -static inline int __netif_tx_trylock(struct netdev_queue *txq) +static inline bool __netif_tx_trylock(struct netdev_queue *txq) { - int ok = spin_trylock(&txq->_xmit_lock); + bool ok = spin_trylock(&txq->_xmit_lock); if (likely(ok)) txq->xmit_lock_owner = smp_processor_id(); return ok; @@ -2614,7 +2614,7 @@ void netif_stacked_transfer_operstate(const struct net_device *rootdev, netdev_features_t netif_skb_features(struct sk_buff *skb); -static inline int net_gso_ok(netdev_features_t features, int gso_type) +static inline bool net_gso_ok(netdev_features_t features, int gso_type) { netdev_features_t feature = gso_type << NETIF_F_GSO_SHIFT; @@ -2629,14 +2629,14 @@ static inline int net_gso_ok(netdev_features_t features, int gso_type) return (features & feature) == feature; } -static inline int skb_gso_ok(struct sk_buff *skb, netdev_features_t features) +static inline bool skb_gso_ok(struct sk_buff *skb, netdev_features_t features) { return net_gso_ok(features, skb_shinfo(skb)->gso_type) && (!skb_has_frag_list(skb) || (features & NETIF_F_FRAGLIST)); } -static inline int netif_needs_gso(struct sk_buff *skb, - netdev_features_t features) +static inline bool netif_needs_gso(struct sk_buff *skb, + netdev_features_t features) { return skb_is_gso(skb) && (!skb_gso_ok(skb, features) || unlikely(skb->ip_summed != CHECKSUM_PARTIAL)); @@ -2648,7 +2648,7 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } -static inline int netif_is_bond_slave(struct net_device *dev) +static inline bool netif_is_bond_slave(struct net_device *dev) { return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 79ef8209bbb7..8dc8257eab8b 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1173,7 +1173,7 @@ static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) } -static inline int skb_is_nonlinear(const struct sk_buff *skb) +static inline bool skb_is_nonlinear(const struct sk_buff *skb) { return skb->data_len; } @@ -2469,12 +2469,12 @@ static inline struct sec_path *skb_sec_path(struct sk_buff *skb) } #endif -static inline int skb_is_gso(const struct sk_buff *skb) +static inline bool skb_is_gso(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_size; } -static inline int skb_is_gso_v6(const struct sk_buff *skb) +static inline bool skb_is_gso_v6(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; } diff --git a/include/net/ip.h b/include/net/ip.h index 775009f9eaba..b53d65f24f7b 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -388,7 +388,7 @@ static inline int sk_mc_loop(struct sock *sk) return 1; } -extern int ip_call_ra_chain(struct sk_buff *skb); +extern bool ip_call_ra_chain(struct sk_buff *skb); /* * Functions provided by ip_fragment.c diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 073a9b01c40c..bdaa2c5c28e4 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -148,7 +148,7 @@ /* * Process Router Attention IP option (RFC 2113) */ -int ip_call_ra_chain(struct sk_buff *skb) +bool ip_call_ra_chain(struct sk_buff *skb) { struct ip_ra_chain *ra; u8 protocol = ip_hdr(skb)->protocol; @@ -167,7 +167,7 @@ int ip_call_ra_chain(struct sk_buff *skb) net_eq(sock_net(sk), dev_net(dev))) { if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN)) - return 1; + return true; } if (last) { struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); @@ -180,9 +180,9 @@ int ip_call_ra_chain(struct sk_buff *skb) if (last) { raw_rcv(last, skb); - return 1; + return true; } - return 0; + return false; } static int ip_local_deliver_finish(struct sk_buff *skb) @@ -265,7 +265,7 @@ int ip_local_deliver(struct sk_buff *skb) ip_local_deliver_finish); } -static inline int ip_rcv_options(struct sk_buff *skb) +static inline bool ip_rcv_options(struct sk_buff *skb) { struct ip_options *opt; const struct iphdr *iph; @@ -309,9 +309,9 @@ static inline int ip_rcv_options(struct sk_buff *skb) goto drop; } - return 0; + return false; drop: - return -1; + return true; } static int ip_rcv_finish(struct sk_buff *skb) |