diff options
Diffstat (limited to 'drivers/net/enic')
26 files changed, 1024 insertions, 635 deletions
diff --git a/drivers/net/enic/cq_desc.h b/drivers/net/enic/cq_desc.h index 1eb289f773bf..d6dd1b4edf6e 100644 --- a/drivers/net/enic/cq_desc.h +++ b/drivers/net/enic/cq_desc.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify diff --git a/drivers/net/enic/cq_enet_desc.h b/drivers/net/enic/cq_enet_desc.h index 337d1943af46..c2c0680a1146 100644 --- a/drivers/net/enic/cq_enet_desc.h +++ b/drivers/net/enic/cq_enet_desc.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -73,7 +73,16 @@ struct cq_enet_rq_desc { #define CQ_ENET_RQ_DESC_FLAGS_TRUNCATED (0x1 << 14) #define CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED (0x1 << 15) -#define CQ_ENET_RQ_DESC_FCOE_SOF_BITS 4 +#define CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_BITS 12 +#define CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_MASK \ + ((1 << CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_BITS) - 1) +#define CQ_ENET_RQ_DESC_VLAN_TCI_CFI_MASK (0x1 << 12) +#define CQ_ENET_RQ_DESC_VLAN_TCI_USER_PRIO_BITS 3 +#define CQ_ENET_RQ_DESC_VLAN_TCI_USER_PRIO_MASK \ + ((1 << CQ_ENET_RQ_DESC_VLAN_TCI_USER_PRIO_BITS) - 1) +#define CQ_ENET_RQ_DESC_VLAN_TCI_USER_PRIO_SHIFT 13 + +#define CQ_ENET_RQ_DESC_FCOE_SOF_BITS 8 #define CQ_ENET_RQ_DESC_FCOE_SOF_MASK \ ((1 << CQ_ENET_RQ_DESC_FCOE_SOF_BITS) - 1) #define CQ_ENET_RQ_DESC_FCOE_EOF_BITS 8 @@ -96,7 +105,7 @@ static inline void cq_enet_rq_desc_dec(struct cq_enet_rq_desc *desc, u8 *type, u8 *color, u16 *q_number, u16 *completed_index, u8 *ingress_port, u8 *fcoe, u8 *eop, u8 *sop, u8 *rss_type, u8 *csum_not_calc, u32 *rss_hash, u16 *bytes_written, u8 *packet_error, - u8 *vlan_stripped, u16 *vlan, u16 *checksum, u8 *fcoe_sof, + u8 *vlan_stripped, u16 *vlan_tci, u16 *checksum, u8 *fcoe_sof, u8 *fcoe_fc_crc_ok, u8 *fcoe_enc_error, u8 *fcoe_eof, u8 *tcp_udp_csum_ok, u8 *udp, u8 *tcp, u8 *ipv4_csum_ok, u8 *ipv6, u8 *ipv4, u8 *ipv4_fragment, u8 *fcs_ok) @@ -136,7 +145,10 @@ static inline void cq_enet_rq_desc_dec(struct cq_enet_rq_desc *desc, *vlan_stripped = (bytes_written_flags & CQ_ENET_RQ_DESC_FLAGS_VLAN_STRIPPED) ? 1 : 0; - *vlan = le16_to_cpu(desc->vlan); + /* + * Tag Control Information(16) = user_priority(3) + cfi(1) + vlan(12) + */ + *vlan_tci = le16_to_cpu(desc->vlan); if (*fcoe) { *fcoe_sof = (u8)(le16_to_cpu(desc->checksum_fcoe) & diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h index 45e86d1e5b1b..c91d364c5527 100644 --- a/drivers/net/enic/enic.h +++ b/drivers/net/enic/enic.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -20,8 +20,6 @@ #ifndef _ENIC_H_ #define _ENIC_H_ -#include <linux/inet_lro.h> - #include "vnic_enet.h" #include "vnic_dev.h" #include "vnic_wq.h" @@ -34,12 +32,8 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "1.3.1.1-pp" -#define DRV_COPYRIGHT "Copyright 2008-2009 Cisco Systems, Inc" -#define PFX DRV_NAME ": " - -#define ENIC_LRO_MAX_DESC 8 -#define ENIC_LRO_MAX_AGGR 64 +#define DRV_VERSION "1.4.1.6" +#define DRV_COPYRIGHT "Copyright 2008-2010 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 @@ -48,25 +42,6 @@ #define ENIC_CQ_MAX (ENIC_WQ_MAX + ENIC_RQ_MAX) #define ENIC_INTR_MAX (ENIC_CQ_MAX + 2) -enum enic_cq_index { - ENIC_CQ_RQ, - ENIC_CQ_WQ, -}; - -enum enic_intx_intr_index { - ENIC_INTX_WQ_RQ, - ENIC_INTX_ERR, - ENIC_INTX_NOTIFY, -}; - -enum enic_msix_intr_index { - ENIC_MSIX_RQ, - ENIC_MSIX_WQ, - ENIC_MSIX_ERR, - ENIC_MSIX_NOTIFY, - ENIC_MSIX_MAX, -}; - struct enic_msix_entry { int requested; char devname[IFNAMSIZ]; @@ -97,8 +72,8 @@ struct enic { struct vnic_dev *vdev; struct timer_list notify_timer; struct work_struct reset; - struct msix_entry msix_entry[ENIC_MSIX_MAX]; - struct enic_msix_entry msix[ENIC_MSIX_MAX]; + struct msix_entry msix_entry[ENIC_INTR_MAX]; + struct enic_msix_entry msix[ENIC_INTR_MAX]; u32 msg_enable; spinlock_t devcmd_lock; u8 mac_addr[ETH_ALEN]; @@ -116,6 +91,8 @@ struct enic { spinlock_t wq_lock[ENIC_WQ_MAX]; unsigned int wq_count; struct vlan_group *vlan_group; + u16 loop_enable; + u16 loop_tag; /* receive queue cache line section */ ____cacheline_aligned struct vnic_rq rq[ENIC_RQ_MAX]; @@ -123,9 +100,7 @@ struct enic { int (*rq_alloc_buf)(struct vnic_rq *rq); u64 rq_truncated_pkts; u64 rq_bad_fcs; - struct napi_struct napi; - struct net_lro_mgr lro_mgr; - struct net_lro_desc lro_desc[ENIC_LRO_MAX_DESC]; + struct napi_struct napi[ENIC_RQ_MAX]; /* interrupt resource cache line section */ ____cacheline_aligned struct vnic_intr intr[ENIC_INTR_MAX]; @@ -137,4 +112,9 @@ struct enic { unsigned int cq_count; }; +static inline struct device *enic_get_dev(struct enic *enic) +{ + return &(enic->pdev->dev); +} + #endif /* _ENIC_H_ */ diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index bc7d6b96de3d..a466ef91dd43 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -29,12 +29,12 @@ #include <linux/etherdevice.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> -#include <linux/if_link.h> #include <linux/ethtool.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/tcp.h> +#include <linux/rtnetlink.h> #include <net/ip6_checksum.h> #include "cq_enet_desc.h" @@ -122,6 +122,51 @@ static int enic_is_dynamic(struct enic *enic) return enic->pdev->device == PCI_DEVICE_ID_CISCO_VIC_ENET_DYN; } +static inline unsigned int enic_cq_rq(struct enic *enic, unsigned int rq) +{ + return rq; +} + +static inline unsigned int enic_cq_wq(struct enic *enic, unsigned int wq) +{ + return enic->rq_count + wq; +} + +static inline unsigned int enic_legacy_io_intr(void) +{ + return 0; +} + +static inline unsigned int enic_legacy_err_intr(void) +{ + return 1; +} + +static inline unsigned int enic_legacy_notify_intr(void) +{ + return 2; +} + +static inline unsigned int enic_msix_rq_intr(struct enic *enic, unsigned int rq) +{ + return rq; +} + +static inline unsigned int enic_msix_wq_intr(struct enic *enic, unsigned int wq) +{ + return enic->rq_count + wq; +} + +static inline unsigned int enic_msix_err_intr(struct enic *enic) +{ + return enic->rq_count + enic->wq_count; +} + +static inline unsigned int enic_msix_notify_intr(struct enic *enic) +{ + return enic->rq_count + enic->wq_count + 1; +} + static int enic_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { @@ -145,15 +190,25 @@ static int enic_get_settings(struct net_device *netdev, return 0; } +static int enic_dev_fw_info(struct enic *enic, + struct vnic_devcmd_fw_info **fw_info) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_fw_info(enic->vdev, fw_info); + spin_unlock(&enic->devcmd_lock); + + return err; +} + static void enic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct enic *enic = netdev_priv(netdev); struct vnic_devcmd_fw_info *fw_info; - spin_lock(&enic->devcmd_lock); - vnic_dev_fw_info(enic->vdev, &fw_info); - spin_unlock(&enic->devcmd_lock); + enic_dev_fw_info(enic, &fw_info); strncpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); strncpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); @@ -191,6 +246,17 @@ static int enic_get_sset_count(struct net_device *netdev, int sset) } } +static int enic_dev_stats_dump(struct enic *enic, struct vnic_stats **vstats) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_stats_dump(enic->vdev, vstats); + spin_unlock(&enic->devcmd_lock); + + return err; +} + static void enic_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { @@ -198,9 +264,7 @@ static void enic_get_ethtool_stats(struct net_device *netdev, struct vnic_stats *vstats; unsigned int i; - spin_lock(&enic->devcmd_lock); - vnic_dev_stats_dump(enic->vdev, &vstats); - spin_unlock(&enic->devcmd_lock); + enic_dev_stats_dump(enic, &vstats); for (i = 0; i < enic_n_tx_stats; i++) *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].offset]; @@ -287,6 +351,7 @@ static int enic_set_coalesce(struct net_device *netdev, struct enic *enic = netdev_priv(netdev); u32 tx_coalesce_usecs; u32 rx_coalesce_usecs; + unsigned int i, intr; tx_coalesce_usecs = min_t(u32, INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX), @@ -300,7 +365,8 @@ static int enic_set_coalesce(struct net_device *netdev, if (tx_coalesce_usecs != rx_coalesce_usecs) return -EINVAL; - vnic_intr_coalescing_timer_set(&enic->intr[ENIC_INTX_WQ_RQ], + intr = enic_legacy_io_intr(); + vnic_intr_coalescing_timer_set(&enic->intr[intr], INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); break; case VNIC_DEV_INTR_MODE_MSI: @@ -311,10 +377,18 @@ static int enic_set_coalesce(struct net_device *netdev, INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); break; case VNIC_DEV_INTR_MODE_MSIX: - vnic_intr_coalescing_timer_set(&enic->intr[ENIC_MSIX_WQ], - INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); - vnic_intr_coalescing_timer_set(&enic->intr[ENIC_MSIX_RQ], - INTR_COALESCE_USEC_TO_HW(rx_coalesce_usecs)); + for (i = 0; i < enic->wq_count; i++) { + intr = enic_msix_wq_intr(enic, i); + vnic_intr_coalescing_timer_set(&enic->intr[intr], + INTR_COALESCE_USEC_TO_HW(tx_coalesce_usecs)); + } + + for (i = 0; i < enic->rq_count; i++) { + intr = enic_msix_rq_intr(enic, i); + vnic_intr_coalescing_timer_set(&enic->intr[intr], + INTR_COALESCE_USEC_TO_HW(rx_coalesce_usecs)); + } + break; default: break; @@ -346,7 +420,6 @@ static const struct ethtool_ops enic_ethtool_ops = { .get_coalesce = enic_get_coalesce, .set_coalesce = enic_set_coalesce, .get_flags = ethtool_op_get_flags, - .set_flags = ethtool_op_set_flags, }; static void enic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) @@ -399,54 +472,55 @@ static void enic_log_q_error(struct enic *enic) for (i = 0; i < enic->wq_count; i++) { error_status = vnic_wq_error_status(&enic->wq[i]); if (error_status) - printk(KERN_ERR PFX "%s: WQ[%d] error_status %d\n", - enic->netdev->name, i, error_status); + netdev_err(enic->netdev, "WQ[%d] error_status %d\n", + i, error_status); } for (i = 0; i < enic->rq_count; i++) { error_status = vnic_rq_error_status(&enic->rq[i]); if (error_status) - printk(KERN_ERR PFX "%s: RQ[%d] error_status %d\n", - enic->netdev->name, i, error_status); + netdev_err(enic->netdev, "RQ[%d] error_status %d\n", + i, error_status); } } -static void enic_link_check(struct enic *enic) +static void enic_msglvl_check(struct enic *enic) { - int link_status = vnic_dev_link_status(enic->vdev); - int carrier_ok = netif_carrier_ok(enic->netdev); + u32 msg_enable = vnic_dev_msg_lvl(enic->vdev); - if (link_status && !carrier_ok) { - printk(KERN_INFO PFX "%s: Link UP\n", enic->netdev->name); - netif_carrier_on(enic->netdev); - } else if (!link_status && carrier_ok) { - printk(KERN_INFO PFX "%s: Link DOWN\n", enic->netdev->name); - netif_carrier_off(enic->netdev); + if (msg_enable != enic->msg_enable) { + netdev_info(enic->netdev, "msg lvl changed from 0x%x to 0x%x\n", + enic->msg_enable, msg_enable); + enic->msg_enable = msg_enable; } } static void enic_mtu_check(struct enic *enic) { u32 mtu = vnic_dev_mtu(enic->vdev); + struct net_device *netdev = enic->netdev; if (mtu && mtu != enic->port_mtu) { enic->port_mtu = mtu; - if (mtu < enic->netdev->mtu) - printk(KERN_WARNING PFX - "%s: interface MTU (%d) set higher " + if (mtu < netdev->mtu) + netdev_warn(netdev, + "interface MTU (%d) set higher " "than switch port MTU (%d)\n", - enic->netdev->name, enic->netdev->mtu, mtu); + netdev->mtu, mtu); } } -static void enic_msglvl_check(struct enic *enic) +static void enic_link_check(struct enic *enic) { - u32 msg_enable = vnic_dev_msg_lvl(enic->vdev); + int link_status = vnic_dev_link_status(enic->vdev); + int carrier_ok = netif_carrier_ok(enic->netdev); - if (msg_enable != enic->msg_enable) { - printk(KERN_INFO PFX "%s: msg lvl changed from 0x%x to 0x%x\n", - enic->netdev->name, enic->msg_enable, msg_enable); - enic->msg_enable = msg_enable; + if (link_status && !carrier_ok) { + netdev_info(enic->netdev, "Link UP\n"); + netif_carrier_on(enic->netdev); + } else if (!link_status && carrier_ok) { + netdev_info(enic->netdev, "Link DOWN\n"); + netif_carrier_off(enic->netdev); } } @@ -463,34 +537,37 @@ static irqreturn_t enic_isr_legacy(int irq, void *data) { struct net_device *netdev = data; struct enic *enic = netdev_priv(netdev); + unsigned int io_intr = enic_legacy_io_intr(); + unsigned int err_intr = enic_legacy_err_intr(); + unsigned int notify_intr = enic_legacy_notify_intr(); u32 pba; - vnic_intr_mask(&enic->intr[ENIC_INTX_WQ_RQ]); + vnic_intr_mask(&enic->intr[io_intr]); pba = vnic_intr_legacy_pba(enic->legacy_pba); if (!pba) { - vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); + vnic_intr_unmask(&enic->intr[io_intr]); return IRQ_NONE; /* not our interrupt */ } - if (ENIC_TEST_INTR(pba, ENIC_INTX_NOTIFY)) { - vnic_intr_return_all_credits(&enic->intr[ENIC_INTX_NOTIFY]); + if (ENIC_TEST_INTR(pba, notify_intr)) { + vnic_intr_return_all_credits(&enic->intr[notify_intr]); enic_notify_check(enic); } - if (ENIC_TEST_INTR(pba, ENIC_INTX_ERR)) { - vnic_intr_return_all_credits(&enic->intr[ENIC_INTX_ERR]); + if (ENIC_TEST_INTR(pba, err_intr)) { + vnic_intr_return_all_credits(&enic->intr[err_intr]); enic_log_q_error(enic); /* schedule recovery from WQ/RQ error */ schedule_work(&enic->reset); return IRQ_HANDLED; } - if (ENIC_TEST_INTR(pba, ENIC_INTX_WQ_RQ)) { - if (napi_schedule_prep(&enic->napi)) - __napi_schedule(&enic->napi); + if (ENIC_TEST_INTR(pba, io_intr)) { + if (napi_schedule_prep(&enic->napi[0])) + __napi_schedule(&enic->napi[0]); } else { - vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); + vnic_intr_unmask(&enic->intr[io_intr]); } return IRQ_HANDLED; @@ -516,17 +593,17 @@ static irqreturn_t enic_isr_msi(int irq, void *data) * writes). */ - napi_schedule(&enic->napi); + napi_schedule(&enic->napi[0]); return IRQ_HANDLED; } static irqreturn_t enic_isr_msix_rq(int irq, void *data) { - struct enic *enic = data; + struct napi_struct *napi = data; /* schedule NAPI polling for RQ cleanup */ - napi_schedule(&enic->napi); + napi_schedule(napi); return IRQ_HANDLED; } @@ -534,13 +611,15 @@ static irqreturn_t enic_isr_msix_rq(int irq, void *data) static irqreturn_t enic_isr_msix_wq(int irq, void *data) { struct enic *enic = data; + unsigned int cq = enic_cq_wq(enic, 0); + unsigned int intr = enic_msix_wq_intr(enic, 0); unsigned int wq_work_to_do = -1; /* no limit */ unsigned int wq_work_done; - wq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_WQ], + wq_work_done = vnic_cq_service(&enic->cq[cq], wq_work_to_do, enic_wq_service, NULL); - vnic_intr_return_credits(&enic->intr[ENIC_MSIX_WQ], + vnic_intr_return_credits(&enic->intr[intr], wq_work_done, 1 /* unmask intr */, 1 /* reset intr timer */); @@ -551,8 +630,9 @@ static irqreturn_t enic_isr_msix_wq(int irq, void *data) static irqreturn_t enic_isr_msix_err(int irq, void *data) { struct enic *enic = data; + unsigned int intr = enic_msix_err_intr(enic); - vnic_intr_return_all_credits(&enic->intr[ENIC_MSIX_ERR]); + vnic_intr_return_all_credits(&enic->intr[intr]); enic_log_q_error(enic); @@ -565,8 +645,9 @@ static irqreturn_t enic_isr_msix_err(int irq, void *data) static irqreturn_t enic_isr_msix_notify(int irq, void *data) { struct enic *enic = data; + unsigned int intr = enic_msix_notify_intr(enic); - vnic_intr_return_all_credits(&enic->intr[ENIC_MSIX_NOTIFY]); + vnic_intr_return_all_credits(&enic->intr[intr]); enic_notify_check(enic); return IRQ_HANDLED; @@ -574,7 +655,7 @@ static irqreturn_t enic_isr_msix_notify(int irq, void *data) static inline void enic_queue_wq_skb_cont(struct enic *enic, struct vnic_wq *wq, struct sk_buff *skb, - unsigned int len_left) + unsigned int len_left, int loopback) { skb_frag_t *frag; @@ -586,13 +667,14 @@ static inline void enic_queue_wq_skb_cont(struct enic *enic, frag->page_offset, frag->size, PCI_DMA_TODEVICE), frag->size, - (len_left == 0)); /* EOP? */ + (len_left == 0), /* EOP? */ + loopback); } } static inline void enic_queue_wq_skb_vlan(struct enic *enic, struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag) + int vlan_tag_insert, unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; @@ -608,15 +690,15 @@ static inline void enic_queue_wq_skb_vlan(struct enic *enic, head_len, PCI_DMA_TODEVICE), head_len, vlan_tag_insert, vlan_tag, - eop); + eop, loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left); + enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); } static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag) + int vlan_tag_insert, unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; @@ -636,15 +718,15 @@ static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, csum_offset, hdr_len, vlan_tag_insert, vlan_tag, - eop); + eop, loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left); + enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); } static inline void enic_queue_wq_skb_tso(struct enic *enic, struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, - int vlan_tag_insert, unsigned int vlan_tag) + int vlan_tag_insert, unsigned int vlan_tag, int loopback) { unsigned int frag_len_left = skb_headlen(skb); unsigned int len_left = skb->len - frag_len_left; @@ -681,7 +763,7 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, len, mss, hdr_len, vlan_tag_insert, vlan_tag, - eop && (len == frag_len_left)); + eop && (len == frag_len_left), loopback); frag_len_left -= len; offset += len; } @@ -707,7 +789,8 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, dma_addr, len, (len_left == 0) && - (len == frag_len_left)); /* EOP? */ + (len == frag_len_left), /* EOP? */ + loopback); frag_len_left -= len; offset += len; } @@ -720,22 +803,26 @@ static inline void enic_queue_wq_skb(struct enic *enic, unsigned int mss = skb_shinfo(skb)->gso_size; unsigned int vlan_tag = 0; int vlan_tag_insert = 0; + int loopback = 0; - if (enic->vlan_group && vlan_tx_tag_present(skb)) { + if (vlan_tx_tag_present(skb)) { /* VLAN tag from trunking driver */ vlan_tag_insert = 1; vlan_tag = vlan_tx_tag_get(skb); + } else if (enic->loop_enable) { + vlan_tag = enic->loop_tag; + loopback = 1; } if (mss) enic_queue_wq_skb_tso(enic, wq, skb, mss, - vlan_tag_insert, vlan_tag); + vlan_tag_insert, vlan_tag, loopback); else if (skb->ip_summed == CHECKSUM_PARTIAL) enic_queue_wq_skb_csum_l4(enic, wq, skb, - vlan_tag_insert, vlan_tag); + vlan_tag_insert, vlan_tag, loopback); else enic_queue_wq_skb_vlan(enic, wq, skb, - vlan_tag_insert, vlan_tag); + vlan_tag_insert, vlan_tag, loopback); } /* netif_tx_lock held, process context with BHs disabled, or BH */ @@ -769,8 +856,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb, skb_shinfo(skb)->nr_frags + ENIC_DESC_MAX_SPLITS) { netif_stop_queue(netdev); /* This is a hard error, log it */ - printk(KERN_ERR PFX "%s: BUG! Tx ring full when " - "queue awake!\n", netdev->name); + netdev_err(netdev, "BUG! Tx ring full when queue awake!\n"); spin_unlock_irqrestore(&enic->wq_lock[0], flags); return NETDEV_TX_BUSY; } @@ -792,9 +878,7 @@ static struct net_device_stats *enic_get_stats(struct net_device *netdev) struct net_device_stats *net_stats = &netdev->stats; struct vnic_stats *stats; - spin_lock(&enic->devcmd_lock); - vnic_dev_stats_dump(enic->vdev, &stats); - spin_unlock(&enic->devcmd_lock); + enic_dev_stats_dump(enic, &stats); net_stats->tx_packets = stats->tx.tx_frames_ok; net_stats->tx_bytes = stats->tx.tx_bytes_ok; @@ -812,9 +896,10 @@ static struct net_device_stats *enic_get_stats(struct net_device *netdev) return net_stats; } -static void enic_reset_mcaddrs(struct enic *enic) +static void enic_reset_multicast_list(struct enic *enic) { enic->mc_count = 0; + enic->flags = 0; } static int enic_set_mac_addr(struct net_device *netdev, char *addr) @@ -888,7 +973,55 @@ static int enic_set_mac_address_dynamic(struct net_device *netdev, void *p) static int enic_set_mac_address(struct net_device *netdev, void *p) { - return -EOPNOTSUPP; + struct sockaddr *saddr = p; + char *addr = saddr->sa_data; + struct enic *enic = netdev_priv(netdev); + int err; + + err = enic_dev_del_station_addr(enic); + if (err) + return err; + + err = enic_set_mac_addr(netdev, addr); + if (err) + return err; + + return enic_dev_add_station_addr(enic); +} + +static int enic_dev_packet_filter(struct enic *enic, int directed, + int multicast, int broadcast, int promisc, int allmulti) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_packet_filter(enic->vdev, directed, + multicast, broadcast, promisc, allmulti); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_dev_add_multicast_addr(struct enic *enic, u8 *addr) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_add_addr(enic->vdev, addr); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_dev_del_multicast_addr(struct enic *enic, u8 *addr) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_del_addr(enic->vdev, addr); + spin_unlock(&enic->devcmd_lock); + + return err; } /* netif_tx_lock held, BHs disabled */ @@ -910,11 +1043,9 @@ static void enic_set_multicast_list(struct net_device *netdev) if (mc_count > ENIC_MULTICAST_PERFECT_FILTERS) mc_count = ENIC_MULTICAST_PERFECT_FILTERS; - spin_lock(&enic->devcmd_lock); - if (enic->flags != flags) { enic->flags = flags; - vnic_dev_packet_filter(enic->vdev, directed, + enic_dev_packet_filter(enic, directed, multicast, broadcast, promisc, allmulti); } @@ -937,7 +1068,7 @@ static void enic_set_multicast_list(struct net_device *netdev) mc_addr[j]) == 0) break; if (j == mc_count) - enic_del_multicast_addr(enic, enic->mc_addr[i]); + enic_dev_del_multicast_addr(enic, enic->mc_addr[i]); } for (i = 0; i < mc_count; i++) { @@ -946,7 +1077,7 @@ static void enic_set_multicast_list(struct net_device *netdev) enic->mc_addr[j]) == 0) break; if (j == enic->mc_count) - enic_add_multicast_addr(enic, mc_addr[i]); + enic_dev_add_multicast_addr(enic, mc_addr[i]); } /* Save the list to compare against next time @@ -956,8 +1087,6 @@ static void enic_set_multicast_list(struct net_device *netdev) memcpy(enic->mc_addr[i], mc_addr[i], ETH_ALEN); enic->mc_count = mc_count; - - spin_unlock(&enic->devcmd_lock); } /* rtnl lock is held */ @@ -1033,10 +1162,7 @@ static int enic_set_port_profile(struct enic *enic, u8 *mac) { struct vic_provinfo *vp; u8 oui[3] = VIC_PROVINFO_CISCO_OUI; - u8 *uuid; char uuid_str[38]; - static char *uuid_fmt = "%02X%02X%02X%02X-%02X%02X-%02X%02X-" - "%02X%02X-%02X%02X%02X%02X%0X%02X"; int err; err = enic_vnic_dev_deinit(enic); @@ -1067,24 +1193,14 @@ static int enic_set_port_profile(struct enic *enic, u8 *mac) ETH_ALEN, mac); if (enic->pp.set & ENIC_SET_INSTANCE) { - uuid = enic->pp.instance_uuid; - sprintf(uuid_str, uuid_fmt, - uuid[0], uuid[1], uuid[2], uuid[3], - uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], - uuid[12], uuid[13], uuid[14], uuid[15]); + sprintf(uuid_str, "%pUB", enic->pp.instance_uuid); vic_provinfo_add_tlv(vp, VIC_LINUX_PROV_TLV_CLIENT_UUID_STR, sizeof(uuid_str), uuid_str); } if (enic->pp.set & ENIC_SET_HOST) { - uuid = enic->pp.host_uuid; - sprintf(uuid_str, uuid_fmt, - uuid[0], uuid[1], uuid[2], uuid[3], - uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], - uuid[12], uuid[13], uuid[14], uuid[15]); + sprintf(uuid_str, "%pUB", enic->pp.host_uuid); vic_provinfo_add_tlv(vp, VIC_LINUX_PROV_TLV_HOST_UUID_STR, sizeof(uuid_str), uuid_str); @@ -1226,7 +1342,7 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) struct enic *enic = vnic_dev_priv(rq->vdev); struct net_device *netdev = enic->netdev; struct sk_buff *skb; - unsigned int len = netdev->mtu + ETH_HLEN; + unsigned int len = netdev->mtu + VLAN_ETH_HLEN; unsigned int os_buf_index = 0; dma_addr_t dma_addr; @@ -1263,12 +1379,24 @@ static int enic_rq_alloc_buf_a1(struct vnic_rq *rq) return 0; } +static int enic_dev_hw_version(struct enic *enic, + enum vnic_dev_hw_version *hw_ver) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_hw_version(enic->vdev, hw_ver); + spin_unlock(&enic->devcmd_lock); + + return err; +} + static int enic_set_rq_alloc_buf(struct enic *enic) { enum vnic_dev_hw_version hw_ver; int err; - err = vnic_dev_hw_version(enic->vdev, &hw_ver); + err = enic_dev_hw_version(enic, &hw_ver); if (err) return err; @@ -1287,51 +1415,6 @@ static int enic_set_rq_alloc_buf(struct enic *enic) return 0; } -static int enic_get_skb_header(struct sk_buff *skb, void **iphdr, - void **tcph, u64 *hdr_flags, void *priv) -{ - struct cq_enet_rq_desc *cq_desc = priv; - unsigned int ip_len; - struct iphdr *iph; - - u8 type, color, eop, sop, ingress_port, vlan_stripped; - u8 fcoe, fcoe_sof, fcoe_fc_crc_ok, fcoe_enc_error, fcoe_eof; - u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; - u8 ipv6, ipv4, ipv4_fragment, fcs_ok, rss_type, csum_not_calc; - u8 packet_error; - u16 q_number, completed_index, bytes_written, vlan, checksum; - u32 rss_hash; - - cq_enet_rq_desc_dec(cq_desc, - &type, &color, &q_number, &completed_index, - &ingress_port, &fcoe, &eop, &sop, &rss_type, - &csum_not_calc, &rss_hash, &bytes_written, - &packet_error, &vlan_stripped, &vlan, &checksum, - &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, - &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, - &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, - &fcs_ok); - - if (!(ipv4 && tcp && !ipv4_fragment)) - return -1; - - skb_reset_network_header(skb); - iph = ip_hdr(skb); - - ip_len = ip_hdrlen(skb); - skb_set_transport_header(skb, ip_len); - - /* check if ip header and tcp header are complete */ - if (ntohs(iph->tot_len) < ip_len + tcp_hdrlen(skb)) - return -1; - - *hdr_flags = LRO_IPV4 | LRO_TCP; - *tcph = tcp_hdr(skb); - *iphdr = iph; - - return 0; -} - static void enic_rq_indicate_buf(struct vnic_rq *rq, struct cq_desc *cq_desc, struct vnic_rq_buf *buf, int skipped, void *opaque) @@ -1345,7 +1428,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; u8 ipv6, ipv4, ipv4_fragment, fcs_ok, rss_type, csum_not_calc; u8 packet_error; - u16 q_number, completed_index, bytes_written, vlan, checksum; + u16 q_number, completed_index, bytes_written, vlan_tci, checksum; u32 rss_hash; if (skipped) @@ -1360,7 +1443,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, &type, &color, &q_number, &completed_index, &ingress_port, &fcoe, &eop, &sop, &rss_type, &csum_not_calc, &rss_hash, &bytes_written, - &packet_error, &vlan_stripped, &vlan, &checksum, + &packet_error, &vlan_stripped, &vlan_tci, &checksum, &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, @@ -1395,25 +1478,24 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq, skb->dev = netdev; - if (enic->vlan_group && vlan_stripped) { + if (enic->vlan_group && vlan_stripped && + (vlan_tci & CQ_ENET_RQ_DESC_VLAN_TCI_VLAN_MASK)) { - if ((netdev->features & NETIF_F_LRO) && ipv4) - lro_vlan_hwaccel_receive_skb(&enic->lro_mgr, - skb, enic->vlan_group, - vlan, cq_desc); + if (netdev->features & NETIF_F_GRO) + vlan_gro_receive(&enic->napi[q_number], + enic->vlan_group, vlan_tci, skb); else vlan_hwaccel_receive_skb(skb, - enic->vlan_group, vlan); + enic->vlan_group, vlan_tci); } else { - if ((netdev->features & NETIF_F_LRO) && ipv4) - lro_receive_skb(&enic->lro_mgr, skb, cq_desc); + if (netdev->features & NETIF_F_GRO) + napi_gro_receive(&enic->napi[q_number], skb); else netif_receive_skb(skb); } - } else { /* Buffer overflow @@ -1437,8 +1519,11 @@ static int enic_rq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, static int enic_poll(struct napi_struct *napi, int budget) { - struct enic *enic = container_of(napi, struct enic, napi); - struct net_device *netdev = enic->netdev; + struct net_device *netdev = napi->dev; + struct enic *enic = netdev_priv(netdev); + unsigned int cq_rq = enic_cq_rq(enic, 0); + unsigned int cq_wq = enic_cq_wq(enic, 0); + unsigned int intr = enic_legacy_io_intr(); unsigned int rq_work_to_do = budget; unsigned int wq_work_to_do = -1; /* no limit */ unsigned int work_done, rq_work_done, wq_work_done; @@ -1447,10 +1532,10 @@ static int enic_poll(struct napi_struct *napi, int budget) /* Service RQ (first) and WQ */ - rq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_RQ], + rq_work_done = vnic_cq_service(&enic->cq[cq_rq], rq_work_to_do, enic_rq_service, NULL); - wq_work_done = vnic_cq_service(&enic->cq[ENIC_CQ_WQ], + wq_work_done = vnic_cq_service(&enic->cq[cq_wq], wq_work_to_do, enic_wq_service, NULL); /* Accumulate intr event credits for this polling @@ -1461,7 +1546,7 @@ static int enic_poll(struct napi_struct *napi, int budget) work_done = rq_work_done + wq_work_done; if (work_done > 0) - vnic_intr_return_credits(&enic->intr[ENIC_INTX_WQ_RQ], + vnic_intr_return_credits(&enic->intr[intr], work_done, 0 /* don't unmask intr */, 0 /* don't reset intr timer */); @@ -1478,14 +1563,11 @@ static int enic_poll(struct napi_struct *napi, int budget) if (rq_work_done < rq_work_to_do) { /* Some work done, but not enough to stay in polling, - * flush all LROs and exit polling + * exit polling */ - if (netdev->features & NETIF_F_LRO) - lro_flush_all(&enic->lro_mgr); - napi_complete(napi); - vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); + vnic_intr_unmask(&enic->intr[intr]); } return rq_work_done; @@ -1493,8 +1575,11 @@ static int enic_poll(struct napi_struct *napi, int budget) static int enic_poll_msix(struct napi_struct *napi, int budget) { - struct enic *enic = container_of(napi, struct enic, napi); - struct net_device *netdev = enic->netdev; + struct net_device *netdev = napi->dev; + struct enic *enic = netdev_priv(netdev); + unsigned int rq = (napi - &enic->napi[0]); + unsigned int cq = enic_cq_rq(enic, rq); + unsigned int intr = enic_msix_rq_intr(enic, rq); unsigned int work_to_do = budget; unsigned int work_done; int err; @@ -1502,7 +1587,7 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) /* Service RQ */ - work_done = vnic_cq_service(&enic->cq[ENIC_CQ_RQ], + work_done = vnic_cq_service(&enic->cq[cq], work_to_do, enic_rq_service, NULL); /* Return intr event credits for this polling @@ -1511,12 +1596,12 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) */ if (work_done > 0) - vnic_intr_return_credits(&enic->intr[ENIC_MSIX_RQ], + vnic_intr_return_credits(&enic->intr[intr], work_done, 0 /* don't unmask intr */, 0 /* don't reset intr timer */); - err = vnic_rq_fill(&enic->rq[0], enic->rq_alloc_buf); + err = vnic_rq_fill(&enic->rq[rq], enic->rq_alloc_buf); /* Buffer allocation failed. Stay in polling mode * so we can try to fill the ring again. @@ -1528,14 +1613,11 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) if (work_done < work_to_do) { /* Some work done, but not enough to stay in polling, - * flush all LROs and exit polling + * exit polling */ - if (netdev->features & NETIF_F_LRO) - lro_flush_all(&enic->lro_mgr); - napi_complete(napi); - vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]); + vnic_intr_unmask(&enic->intr[intr]); } return work_done; @@ -1577,7 +1659,7 @@ static void enic_free_intr(struct enic *enic) static int enic_request_intr(struct enic *enic) { struct net_device *netdev = enic->netdev; - unsigned int i; + unsigned int i, intr; int err = 0; switch (vnic_dev_get_intr_mode(enic->vdev)) { @@ -1596,27 +1678,38 @@ static int enic_request_intr(struct enic *enic) case VNIC_DEV_INTR_MODE_MSIX: - sprintf(enic->msix[ENIC_MSIX_RQ].devname, - "%.11s-rx-0", netdev->name); - enic->msix[ENIC_MSIX_RQ].isr = enic_isr_msix_rq; - enic->msix[ENIC_MSIX_RQ].devid = enic; + for (i = 0; i < enic->rq_count; i++) { + intr = enic_msix_rq_intr(enic, i); + sprintf(enic->msix[intr].devname, + "%.11s-rx-%d", netdev->name, i); + enic->msix[intr].isr = enic_isr_msix_rq; + enic->msix[intr].devid = &enic->napi[i]; + } - sprintf(enic->msix[ENIC_MSIX_WQ].devname, - "%.11s-tx-0", netdev->name); - enic->msix[ENIC_MSIX_WQ].isr = enic_isr_msix_wq; - enic->msix[ENIC_MSIX_WQ].devid = enic; + for (i = 0; i < enic->wq_count; i++) { + intr = enic_msix_wq_intr(enic, i); + sprintf(enic->msix[intr].devname, + "%.11s-tx-%d", netdev->name, i); + enic->msix[intr].isr = enic_isr_msix_wq; + enic->msix[intr].devid = enic; + } - sprintf(enic->msix[ENIC_MSIX_ERR].devname, + intr = enic_msix_err_intr(enic); + sprintf(enic->msix[intr].devname, "%.11s-err", netdev->name); - enic->msix[ENIC_MSIX_ERR].isr = enic_isr_msix_err; - enic->msix[ENIC_MSIX_ERR].devid = enic; + enic->msix[intr].isr = enic_isr_msix_err; + enic->msix[intr].devid = enic; - sprintf(enic->msix[ENIC_MSIX_NOTIFY].devname, + intr = enic_msix_notify_intr(enic); + sprintf(enic->msix[intr].devname, "%.11s-notify", netdev->name); - enic->msix[ENIC_MSIX_NOTIFY].isr = enic_isr_msix_notify; - enic->msix[ENIC_MSIX_NOTIFY].devid = enic; + enic->msix[intr].isr = enic_isr_msix_notify; + enic->msix[intr].devid = enic; + + for (i = 0; i < ARRAY_SIZE(enic->msix); i++) + enic->msix[i].requested = 0; - for (i = 0; i < ARRAY_SIZE(enic->msix); i++) { + for (i = 0; i < enic->intr_count; i++) { err = request_irq(enic->msix_entry[i].vector, enic->msix[i].isr, 0, enic->msix[i].devname, @@ -1655,17 +1748,19 @@ static void enic_synchronize_irqs(struct enic *enic) } } -static int enic_notify_set(struct enic *enic) +static int enic_dev_notify_set(struct enic *enic) { int err; spin_lock(&enic->devcmd_lock); switch (vnic_dev_get_intr_mode(enic->vdev)) { case VNIC_DEV_INTR_MODE_INTX: - err = vnic_dev_notify_set(enic->vdev, ENIC_INTX_NOTIFY); + err = vnic_dev_notify_set(enic->vdev, + enic_legacy_notify_intr()); break; case VNIC_DEV_INTR_MODE_MSIX: - err = vnic_dev_notify_set(enic->vdev, ENIC_MSIX_NOTIFY); + err = vnic_dev_notify_set(enic->vdev, + enic_msix_notify_intr(enic)); break; default: err = vnic_dev_notify_set(enic->vdev, -1 /* no intr */); @@ -1676,6 +1771,39 @@ static int enic_notify_set(struct enic *enic) return err; } +static int enic_dev_notify_unset(struct enic *enic) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_notify_unset(enic->vdev); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_dev_enable(struct enic *enic) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_enable_wait(enic->vdev); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_dev_disable(struct enic *enic) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_disable(enic->vdev); + spin_unlock(&enic->devcmd_lock); + + return err; +} + static void enic_notify_timer_start(struct enic *enic) { switch (vnic_dev_get_intr_mode(enic->vdev)) { @@ -1697,16 +1825,14 @@ static int enic_open(struct net_device *netdev) err = enic_request_intr(enic); if (err) { - printk(KERN_ERR PFX "%s: Unable to request irq.\n", - netdev->name); + netdev_err(netdev, "Unable to request irq.\n"); return err; } - err = enic_notify_set(enic); + err = enic_dev_notify_set(enic); if (err) { - printk(KERN_ERR PFX - "%s: Failed to alloc notify buffer, aborting.\n", - netdev->name); + netdev_err(netdev, + "Failed to alloc notify buffer, aborting.\n"); goto err_out_free_intr; } @@ -1714,9 +1840,7 @@ static int enic_open(struct net_device *netdev) vnic_rq_fill(&enic->rq[i], enic->rq_alloc_buf); /* Need at least one buffer on ring to get going */ if (vnic_rq_desc_used(&enic->rq[i]) == 0) { - printk(KERN_ERR PFX - "%s: Unable to alloc receive buffers.\n", - netdev->name); + netdev_err(netdev, "Unable to alloc receive buffers\n"); err = -ENOMEM; goto err_out_notify_unset; } @@ -1731,10 +1855,11 @@ static int enic_open(struct net_device *netdev) enic_set_multicast_list(netdev); netif_wake_queue(netdev); - napi_enable(&enic->napi); - spin_lock(&enic->devcmd_lock); - vnic_dev_enable(enic->vdev); - spin_unlock(&enic->devcmd_lock); + + for (i = 0; i < enic->rq_count; i++) + napi_enable(&enic->napi[i]); + + enic_dev_enable(enic); for (i = 0; i < enic->intr_count; i++) vnic_intr_unmask(&enic->intr[i]); @@ -1744,9 +1869,7 @@ static int enic_open(struct net_device *netdev) return 0; err_out_notify_unset: - spin_lock(&enic->devcmd_lock); - vnic_dev_notify_unset(enic->vdev); - spin_unlock(&enic->devcmd_lock); + enic_dev_notify_unset(enic); err_out_free_intr: enic_free_intr(enic); @@ -1760,20 +1883,22 @@ static int enic_stop(struct net_device *netdev) unsigned int i; int err; - for (i = 0; i < enic->intr_count; i++) + for (i = 0; i < enic->intr_count; i++) { vnic_intr_mask(&enic->intr[i]); + (void)vnic_intr_masked(&enic->intr[i]); /* flush write */ + } enic_synchronize_irqs(enic); del_timer_sync(&enic->notify_timer); - spin_lock(&enic->devcmd_lock); - vnic_dev_disable(enic->vdev); - spin_unlock(&enic->devcmd_lock); - napi_disable(&enic->napi); + enic_dev_disable(enic); + + for (i = 0; i < enic->rq_count; i++) + napi_disable(&enic->napi[i]); + netif_carrier_off(netdev); netif_tx_disable(netdev); - enic_dev_del_station_addr(enic); for (i = 0; i < enic->wq_count; i++) { @@ -1787,9 +1912,7 @@ static int enic_stop(struct net_device *netdev) return err; } - spin_lock(&enic->devcmd_lock); - vnic_dev_notify_unset(enic->vdev); - spin_unlock(&enic->devcmd_lock); + enic_dev_notify_unset(enic); enic_free_intr(enic); for (i = 0; i < enic->wq_count; i++) @@ -1818,10 +1941,9 @@ static int enic_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; if (netdev->mtu > enic->port_mtu) - printk(KERN_WARNING PFX - "%s: interface MTU (%d) set higher " - "than port MTU (%d)\n", - netdev->name, netdev->mtu, enic->port_mtu); + netdev_warn(netdev, + "interface MTU (%d) set higher than port MTU (%d)\n", + netdev->mtu, enic->port_mtu); if (running) enic_open(netdev); @@ -1834,11 +1956,16 @@ static void enic_poll_controller(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); struct vnic_dev *vdev = enic->vdev; + unsigned int i, intr; switch (vnic_dev_get_intr_mode(vdev)) { case VNIC_DEV_INTR_MODE_MSIX: - enic_isr_msix_rq(enic->pdev->irq, enic); - enic_isr_msix_wq(enic->pdev->irq, enic); + for (i = 0; i < enic->rq_count; i++) { + intr = enic_msix_rq_intr(enic, i); + enic_isr_msix_rq(enic->msix_entry[intr].vector, enic); + } + intr = enic_msix_wq_intr(enic, i); + enic_isr_msix_wq(enic->msix_entry[intr].vector, enic); break; case VNIC_DEV_INTR_MODE_MSI: enic_isr_msi(enic->pdev->irq, enic); @@ -1894,43 +2021,154 @@ static int enic_dev_open(struct enic *enic) err = enic_dev_wait(enic->vdev, vnic_dev_open, vnic_dev_open_done, 0); if (err) - printk(KERN_ERR PFX - "vNIC device open failed, err %d.\n", err); + dev_err(enic_get_dev(enic), "vNIC device open failed, err %d\n", + err); return err; } -static int enic_dev_soft_reset(struct enic *enic) +static int enic_dev_hang_reset(struct enic *enic) { int err; - err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, - vnic_dev_soft_reset_done, 0); + err = enic_dev_wait(enic->vdev, vnic_dev_hang_reset, + vnic_dev_hang_reset_done, 0); if (err) - printk(KERN_ERR PFX - "vNIC soft reset failed, err %d.\n", err); + netdev_err(enic->netdev, "vNIC hang reset failed, err %d\n", + err); return err; } -static int enic_set_niccfg(struct enic *enic) +static int enic_set_rsskey(struct enic *enic) +{ + u64 rss_key_buf_pa; + union vnic_rss_key *rss_key_buf_va = NULL; + union vnic_rss_key rss_key = { + .key[0].b = {85, 67, 83, 97, 119, 101, 115, 111, 109, 101}, + .key[1].b = {80, 65, 76, 79, 117, 110, 105, 113, 117, 101}, + .key[2].b = {76, 73, 78, 85, 88, 114, 111, 99, 107, 115}, + .key[3].b = {69, 78, 73, 67, 105, 115, 99, 111, 111, 108}, + }; + int err; + + rss_key_buf_va = pci_alloc_consistent(enic->pdev, + sizeof(union vnic_rss_key), &rss_key_buf_pa); + if (!rss_key_buf_va) + return -ENOMEM; + + memcpy(rss_key_buf_va, &rss_key, sizeof(union vnic_rss_key)); + + spin_lock(&enic->devcmd_lock); + err = enic_set_rss_key(enic, + rss_key_buf_pa, + sizeof(union vnic_rss_key)); + spin_unlock(&enic->devcmd_lock); + + pci_free_consistent(enic->pdev, sizeof(union vnic_rss_key), + rss_key_buf_va, rss_key_buf_pa); + + return err; +} + +static int enic_set_rsscpu(struct enic *enic, u8 rss_hash_bits) +{ + u64 rss_cpu_buf_pa; + union vnic_rss_cpu *rss_cpu_buf_va = NULL; + unsigned int i; + int err; + + rss_cpu_buf_va = pci_alloc_consistent(enic->pdev, + sizeof(union vnic_rss_cpu), &rss_cpu_buf_pa); + if (!rss_cpu_buf_va) + return -ENOMEM; + + for (i = 0; i < (1 << rss_hash_bits); i++) + (*rss_cpu_buf_va).cpu[i/4].b[i%4] = i % enic->rq_count; + + spin_lock(&enic->devcmd_lock); + err = enic_set_rss_cpu(enic, + rss_cpu_buf_pa, + sizeof(union vnic_rss_cpu)); + spin_unlock(&enic->devcmd_lock); + + pci_free_consistent(enic->pdev, sizeof(union vnic_rss_cpu), + rss_cpu_buf_va, rss_cpu_buf_pa); + + return err; +} + +static int enic_set_niccfg(struct enic *enic, u8 rss_default_cpu, + u8 rss_hash_type, u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable) { - const u8 rss_default_cpu = 0; - const u8 rss_hash_type = 0; - const u8 rss_hash_bits = 0; - const u8 rss_base_cpu = 0; - const u8 rss_enable = 0; const u8 tso_ipid_split_en = 0; const u8 ig_vlan_strip_en = 1; + int err; - /* Enable VLAN tag stripping. RSS not enabled (yet). - */ + /* Enable VLAN tag stripping. + */ - return enic_set_nic_cfg(enic, + spin_lock(&enic->devcmd_lock); + err = enic_set_nic_cfg(enic, rss_default_cpu, rss_hash_type, rss_hash_bits, rss_base_cpu, rss_enable, tso_ipid_split_en, ig_vlan_strip_en); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_set_rss_nic_cfg(struct enic *enic) +{ + struct device *dev = enic_get_dev(enic); + const u8 rss_default_cpu = 0; + const u8 rss_hash_type = NIC_CFG_RSS_HASH_TYPE_IPV4 | + NIC_CFG_RSS_HASH_TYPE_TCP_IPV4 | + NIC_CFG_RSS_HASH_TYPE_IPV6 | + NIC_CFG_RSS_HASH_TYPE_TCP_IPV6; + const u8 rss_hash_bits = 7; + const u8 rss_base_cpu = 0; + u8 rss_enable = ENIC_SETTING(enic, RSS) && (enic->rq_count > 1); + + if (rss_enable) { + if (!enic_set_rsskey(enic)) { + if (enic_set_rsscpu(enic, rss_hash_bits)) { + rss_enable = 0; + dev_warn(dev, "RSS disabled, " + "Failed to set RSS cpu indirection table."); + } + } else { + rss_enable = 0; + dev_warn(dev, "RSS disabled, Failed to set RSS key.\n"); + } + } + + return enic_set_niccfg(enic, rss_default_cpu, rss_hash_type, + rss_hash_bits, rss_base_cpu, rss_enable); +} + +static int enic_dev_hang_notify(struct enic *enic) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_hang_notify(enic->vdev); + spin_unlock(&enic->devcmd_lock); + + return err; +} + +static int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic) +{ + int err; + + spin_lock(&enic->devcmd_lock); + err = vnic_dev_set_ig_vlan_rewrite_mode(enic->vdev, + IG_VLAN_REWRITE_MODE_PRIORITY_TAG_DEFAULT_VLAN); + spin_unlock(&enic->devcmd_lock); + + return err; } static void enic_reset(struct work_struct *work) @@ -1942,16 +2180,13 @@ static void enic_reset(struct work_struct *work) rtnl_lock(); - spin_lock(&enic->devcmd_lock); - vnic_dev_hang_notify(enic->vdev); - spin_unlock(&enic->devcmd_lock); - + enic_dev_hang_notify(enic); enic_stop(enic->netdev); - enic_dev_soft_reset(enic); - vnic_dev_init(enic->vdev, 0); - enic_reset_mcaddrs(enic); + enic_dev_hang_reset(enic); + enic_reset_multicast_list(enic); enic_init_vnic_resources(enic); - enic_set_niccfg(enic); + enic_set_rss_nic_cfg(enic); + enic_dev_set_ig_vlan_rewrite_mode(enic); enic_open(enic->netdev); rtnl_unlock(); @@ -1959,12 +2194,12 @@ static void enic_reset(struct work_struct *work) static int enic_set_intr_mode(struct enic *enic) { - unsigned int n = 1; + unsigned int n = min_t(unsigned int, enic->rq_count, ENIC_RQ_MAX); unsigned int m = 1; unsigned int i; /* Set interrupt mode (INTx, MSI, MSI-X) depending - * system capabilities. + * on system capabilities. * * Try MSI-X first * @@ -1977,21 +2212,47 @@ static int enic_set_intr_mode(struct enic *enic) for (i = 0; i < n + m + 2; i++) enic->msix_entry[i].entry = i; - if (enic->config.intr_mode < 1 && + /* Use multiple RQs if RSS is enabled + */ + + if (ENIC_SETTING(enic, RSS) && + enic->config.intr_mode < 1 && enic->rq_count >= n && enic->wq_count >= m && enic->cq_count >= n + m && - enic->intr_count >= n + m + 2 && - !pci_enable_msix(enic->pdev, enic->msix_entry, n + m + 2)) { + enic->intr_count >= n + m + 2) { - enic->rq_count = n; - enic->wq_count = m; - enic->cq_count = n + m; - enic->intr_count = n + m + 2; + if (!pci_enable_msix(enic->pdev, enic->msix_entry, n + m + 2)) { - vnic_dev_set_intr_mode(enic->vdev, VNIC_DEV_INTR_MODE_MSIX); + enic->rq_count = n; + enic->wq_count = m; + enic->cq_count = n + m; + enic->intr_count = n + m + 2; - return 0; + vnic_dev_set_intr_mode(enic->vdev, + VNIC_DEV_INTR_MODE_MSIX); + + return 0; + } + } + + if (enic->config.intr_mode < 1 && + enic->rq_count >= 1 && + enic->wq_count >= m && + enic->cq_count >= 1 + m && + enic->intr_count >= 1 + m + 2) { + if (!pci_enable_msix(enic->pdev, enic->msix_entry, 1 + m + 2)) { + + enic->rq_count = 1; + enic->wq_count = m; + enic->cq_count = 1 + m; + enic->intr_count = 1 + m + 2; + + vnic_dev_set_intr_mode(enic->vdev, + VNIC_DEV_INTR_MODE_MSIX); + + return 0; + } } /* Next try MSI @@ -2087,8 +2348,8 @@ static const struct net_device_ops enic_netdev_ops = { .ndo_start_xmit = enic_hard_start_xmit, .ndo_get_stats = enic_get_stats, .ndo_validate_addr = eth_validate_addr, - .ndo_set_multicast_list = enic_set_multicast_list, .ndo_set_mac_address = enic_set_mac_address, + .ndo_set_multicast_list = enic_set_multicast_list, .ndo_change_mtu = enic_change_mtu, .ndo_vlan_rx_register = enic_vlan_rx_register, .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid, @@ -2099,16 +2360,22 @@ static const struct net_device_ops enic_netdev_ops = { #endif }; -void enic_dev_deinit(struct enic *enic) +static void enic_dev_deinit(struct enic *enic) { - netif_napi_del(&enic->napi); + unsigned int i; + + for (i = 0; i < enic->rq_count; i++) + netif_napi_del(&enic->napi[i]); + enic_free_vnic_resources(enic); enic_clear_intr_mode(enic); } -int enic_dev_init(struct enic *enic) +static int enic_dev_init(struct enic *enic) { + struct device *dev = enic_get_dev(enic); struct net_device *netdev = enic->netdev; + unsigned int i; int err; /* Get vNIC configuration @@ -2116,8 +2383,7 @@ int enic_dev_init(struct enic *enic) err = enic_get_vnic_config(enic); if (err) { - printk(KERN_ERR PFX - "Get vNIC configuration failed, aborting.\n"); + dev_err(dev, "Get vNIC configuration failed, aborting\n"); return err; } @@ -2132,9 +2398,8 @@ int enic_dev_init(struct enic *enic) err = enic_set_intr_mode(enic); if (err) { - printk(KERN_ERR PFX - "Failed to set intr mode based on resource " - "counts and system capabilities, aborting.\n"); + dev_err(dev, "Failed to set intr mode based on resource " + "counts and system capabilities, aborting\n"); return err; } @@ -2143,8 +2408,7 @@ int enic_dev_init(struct enic *enic) err = enic_alloc_vnic_resources(enic); if (err) { - printk(KERN_ERR PFX - "Failed to alloc vNIC resources, aborting.\n"); + dev_err(dev, "Failed to alloc vNIC resources, aborting\n"); goto err_out_free_vnic_resources; } @@ -2152,24 +2416,31 @@ int enic_dev_init(struct enic *enic) err = enic_set_rq_alloc_buf(enic); if (err) { - printk(KERN_ERR PFX - "Failed to set RQ buffer allocator, aborting.\n"); + dev_err(dev, "Failed to set RQ buffer allocator, aborting\n"); + goto err_out_free_vnic_resources; + } + + err = enic_set_rss_nic_cfg(enic); + if (err) { + dev_err(dev, "Failed to config nic, aborting\n"); goto err_out_free_vnic_resources; } - err = enic_set_niccfg(enic); + err = enic_dev_set_ig_vlan_rewrite_mode(enic); if (err) { - printk(KERN_ERR PFX - "Failed to config nic, aborting.\n"); + dev_err(dev, + "Failed to set ingress vlan rewrite mode, aborting.\n"); goto err_out_free_vnic_resources; } switch (vnic_dev_get_intr_mode(enic->vdev)) { default: - netif_napi_add(netdev, &enic->napi, enic_poll, 64); + netif_napi_add(netdev, &enic->napi[0], enic_poll, 64); break; case VNIC_DEV_INTR_MODE_MSIX: - netif_napi_add(netdev, &enic->napi, enic_poll_msix, 64); + for (i = 0; i < enic->rq_count; i++) + netif_napi_add(netdev, &enic->napi[i], + enic_poll_msix, 64); break; } @@ -2194,6 +2465,7 @@ static void enic_iounmap(struct enic *enic) static int __devinit enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct device *dev = &pdev->dev; struct net_device *netdev; struct enic *enic; int using_dac = 0; @@ -2206,7 +2478,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, netdev = alloc_etherdev(sizeof(struct enic)); if (!netdev) { - printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + pr_err("Etherdev alloc failed, aborting\n"); return -ENOMEM; } @@ -2221,17 +2493,15 @@ static int __devinit enic_probe(struct pci_dev *pdev, /* Setup PCI resources */ - err = pci_enable_device(pdev); + err = pci_enable_device_mem(pdev); if (err) { - printk(KERN_ERR PFX - "Cannot enable PCI device, aborting.\n"); + dev_err(dev, "Cannot enable PCI device, aborting\n"); goto err_out_free_netdev; } err = pci_request_regions(pdev, DRV_NAME); if (err) { - printk(KERN_ERR PFX - "Cannot request PCI regions, aborting.\n"); + dev_err(dev, "Cannot request PCI regions, aborting\n"); goto err_out_disable_device; } @@ -2246,23 +2516,20 @@ static int __devinit enic_probe(struct pci_dev *pdev, if (err) { err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { - printk(KERN_ERR PFX - "No usable DMA configuration, aborting.\n"); + dev_err(dev, "No usable DMA configuration, aborting\n"); goto err_out_release_regions; } err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { - printk(KERN_ERR PFX - "Unable to obtain 32-bit DMA " - "for consistent allocations, aborting.\n"); + dev_err(dev, "Unable to obtain %u-bit DMA " + "for consistent allocations, aborting\n", 32); goto err_out_release_regions; } } else { err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40)); if (err) { - printk(KERN_ERR PFX - "Unable to obtain 40-bit DMA " - "for consistent allocations, aborting.\n"); + dev_err(dev, "Unable to obtain %u-bit DMA " + "for consistent allocations, aborting\n", 40); goto err_out_release_regions; } using_dac = 1; @@ -2277,8 +2544,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, enic->bar[i].len = pci_resource_len(pdev, i); enic->bar[i].vaddr = pci_iomap(pdev, i, enic->bar[i].len); if (!enic->bar[i].vaddr) { - printk(KERN_ERR PFX - "Cannot memory-map BAR %d, aborting.\n", i); + dev_err(dev, "Cannot memory-map BAR %d, aborting\n", i); err = -ENODEV; goto err_out_iounmap; } @@ -2291,8 +2557,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, enic->vdev = vnic_dev_register(NULL, enic, pdev, enic->bar, ARRAY_SIZE(enic->bar)); if (!enic->vdev) { - printk(KERN_ERR PFX - "vNIC registration failed, aborting.\n"); + dev_err(dev, "vNIC registration failed, aborting\n"); err = -ENODEV; goto err_out_iounmap; } @@ -2302,8 +2567,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, err = enic_dev_open(enic); if (err) { - printk(KERN_ERR PFX - "vNIC dev open failed, aborting.\n"); + dev_err(dev, "vNIC dev open failed, aborting\n"); goto err_out_vnic_unregister; } @@ -2317,23 +2581,31 @@ static int __devinit enic_probe(struct pci_dev *pdev, netif_carrier_off(netdev); + /* Do not call dev_init for a dynamic vnic. + * For a dynamic vnic, init_prov_info will be + * called later by an upper layer. + */ + if (!enic_is_dynamic(enic)) { err = vnic_dev_init(enic->vdev, 0); if (err) { - printk(KERN_ERR PFX - "vNIC dev init failed, aborting.\n"); + dev_err(dev, "vNIC dev init failed, aborting\n"); goto err_out_dev_close; } } + /* Setup devcmd lock + */ + + spin_lock_init(&enic->devcmd_lock); + err = enic_dev_init(enic); if (err) { - printk(KERN_ERR PFX - "Device initialization failed, aborting.\n"); + dev_err(dev, "Device initialization failed, aborting\n"); goto err_out_dev_close; } - /* Setup notification timer, HW reset task, and locks + /* Setup notification timer, HW reset task, and wq locks */ init_timer(&enic->notify_timer); @@ -2345,8 +2617,6 @@ static int __devinit enic_probe(struct pci_dev *pdev, for (i = 0; i < enic->wq_count; i++) spin_lock_init(&enic->wq_lock[i]); - spin_lock_init(&enic->devcmd_lock); - /* Register net device */ @@ -2355,8 +2625,7 @@ static int __devinit enic_probe(struct pci_dev *pdev, err = enic_set_mac_addr(netdev, enic->mac_addr); if (err) { - printk(KERN_ERR PFX - "Invalid MAC address, aborting.\n"); + dev_err(dev, "Invalid MAC address, aborting\n"); goto err_out_dev_deinit; } @@ -2372,31 +2641,27 @@ static int __devinit enic_probe(struct pci_dev *pdev, netdev->ethtool_ops = &enic_ethtool_ops; netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + if (ENIC_SETTING(enic, LOOP)) { + netdev->features &= ~NETIF_F_HW_VLAN_TX; + enic->loop_enable = 1; + enic->loop_tag = enic->config.loop_tag; + dev_info(dev, "loopback tag=0x%04x\n", enic->loop_tag); + } if (ENIC_SETTING(enic, TXCSUM)) netdev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; if (ENIC_SETTING(enic, TSO)) netdev->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN; if (ENIC_SETTING(enic, LRO)) - netdev->features |= NETIF_F_LRO; + netdev->features |= NETIF_F_GRO; if (using_dac) netdev->features |= NETIF_F_HIGHDMA; enic->csum_rx_enabled = ENIC_SETTING(enic, RXCSUM); - enic->lro_mgr.max_aggr = ENIC_LRO_MAX_AGGR; - enic->lro_mgr.max_desc = ENIC_LRO_MAX_DESC; - enic->lro_mgr.lro_arr = enic->lro_desc; - enic->lro_mgr.get_skb_header = enic_get_skb_header; - enic->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID; - enic->lro_mgr.dev = netdev; - enic->lro_mgr.ip_summed = CHECKSUM_COMPLETE; - enic->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY; - err = register_netdev(netdev); if (err) { - printk(KERN_ERR PFX - "Cannot register net device, aborting.\n"); + dev_err(dev, "Cannot register net device, aborting\n"); goto err_out_dev_deinit; } @@ -2450,7 +2715,7 @@ static struct pci_driver enic_driver = { static int __init enic_init_module(void) { - printk(KERN_INFO PFX "%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION); + pr_info("%s, ver %s\n", DRV_DESCRIPTION, DRV_VERSION); return pci_register_driver(&enic_driver); } diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c index 9b18840cba96..f111a37419ce 100644 --- a/drivers/net/enic/enic_res.c +++ b/drivers/net/enic/enic_res.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -46,7 +46,8 @@ int enic_get_vnic_config(struct enic *enic) err = vnic_dev_mac_addr(enic->vdev, enic->mac_addr); if (err) { - printk(KERN_ERR PFX "Error getting MAC addr, %d\n", err); + dev_err(enic_get_dev(enic), + "Error getting MAC addr, %d\n", err); return err; } @@ -56,7 +57,7 @@ int enic_get_vnic_config(struct enic *enic) offsetof(struct vnic_enet_config, m), \ sizeof(c->m), &c->m); \ if (err) { \ - printk(KERN_ERR PFX \ + dev_err(enic_get_dev(enic), \ "Error getting %s, %d\n", #m, err); \ return err; \ } \ @@ -69,6 +70,7 @@ int enic_get_vnic_config(struct enic *enic) GET_CONFIG(intr_timer_type); GET_CONFIG(intr_mode); GET_CONFIG(intr_timer_usec); + GET_CONFIG(loop_tag); c->wq_desc_count = min_t(u32, ENIC_MAX_WQ_DESCS, @@ -92,28 +94,19 @@ int enic_get_vnic_config(struct enic *enic) INTR_COALESCE_HW_TO_USEC(VNIC_INTR_TIMER_MAX), c->intr_timer_usec); - printk(KERN_INFO PFX "vNIC MAC addr %pM wq/rq %d/%d\n", - enic->mac_addr, c->wq_desc_count, c->rq_desc_count); - printk(KERN_INFO PFX "vNIC mtu %d csum tx/rx %d/%d tso/lro %d/%d " - "intr timer %d usec\n", - c->mtu, ENIC_SETTING(enic, TXCSUM), - ENIC_SETTING(enic, RXCSUM), ENIC_SETTING(enic, TSO), - ENIC_SETTING(enic, LRO), c->intr_timer_usec); + dev_info(enic_get_dev(enic), + "vNIC MAC addr %pM wq/rq %d/%d mtu %d\n", + enic->mac_addr, c->wq_desc_count, c->rq_desc_count, c->mtu); + dev_info(enic_get_dev(enic), "vNIC csum tx/rx %d/%d " + "tso/lro %d/%d intr timer %d usec rss %d\n", + ENIC_SETTING(enic, TXCSUM), ENIC_SETTING(enic, RXCSUM), + ENIC_SETTING(enic, TSO), ENIC_SETTING(enic, LRO), + c->intr_timer_usec, ENIC_SETTING(enic, RSS)); return 0; } -void enic_add_multicast_addr(struct enic *enic, u8 *addr) -{ - vnic_dev_add_addr(enic->vdev, addr); -} - -void enic_del_multicast_addr(struct enic *enic, u8 *addr) -{ - vnic_dev_del_addr(enic->vdev, addr); -} - -void enic_add_vlan(struct enic *enic, u16 vlanid) +int enic_add_vlan(struct enic *enic, u16 vlanid) { u64 a0 = vlanid, a1 = 0; int wait = 1000; @@ -121,10 +114,12 @@ void enic_add_vlan(struct enic *enic, u16 vlanid) err = vnic_dev_cmd(enic->vdev, CMD_VLAN_ADD, &a0, &a1, wait); if (err) - printk(KERN_ERR PFX "Can't add vlan id, %d\n", err); + dev_err(enic_get_dev(enic), "Can't add vlan id, %d\n", err); + + return err; } -void enic_del_vlan(struct enic *enic, u16 vlanid) +int enic_del_vlan(struct enic *enic, u16 vlanid) { u64 a0 = vlanid, a1 = 0; int wait = 1000; @@ -132,7 +127,9 @@ void enic_del_vlan(struct enic *enic, u16 vlanid) err = vnic_dev_cmd(enic->vdev, CMD_VLAN_DEL, &a0, &a1, wait); if (err) - printk(KERN_ERR PFX "Can't delete vlan id, %d\n", err); + dev_err(enic_get_dev(enic), "Can't delete vlan id, %d\n", err); + + return err; } int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, @@ -185,21 +182,14 @@ void enic_free_vnic_resources(struct enic *enic) void enic_get_res_counts(struct enic *enic) { - enic->wq_count = min_t(int, - vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ), - ENIC_WQ_MAX); - enic->rq_count = min_t(int, - vnic_dev_get_res_count(enic->vdev, RES_TYPE_RQ), - ENIC_RQ_MAX); - enic->cq_count = min_t(int, - vnic_dev_get_res_count(enic->vdev, RES_TYPE_CQ), - ENIC_CQ_MAX); - enic->intr_count = min_t(int, - vnic_dev_get_res_count(enic->vdev, RES_TYPE_INTR_CTRL), - ENIC_INTR_MAX); - - printk(KERN_INFO PFX "vNIC resources avail: " - "wq %d rq %d cq %d intr %d\n", + enic->wq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_WQ); + enic->rq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_RQ); + enic->cq_count = vnic_dev_get_res_count(enic->vdev, RES_TYPE_CQ); + enic->intr_count = vnic_dev_get_res_count(enic->vdev, + RES_TYPE_INTR_CTRL); + + dev_info(enic_get_dev(enic), + "vNIC resources avail: wq %d rq %d cq %d intr %d\n", enic->wq_count, enic->rq_count, enic->cq_count, enic->intr_count); } @@ -304,11 +294,6 @@ void enic_init_vnic_resources(struct enic *enic) enic->config.intr_timer_type, mask_on_assertion); } - - /* Clear LIF stats - */ - - vnic_dev_stats_clear(enic->vdev); } int enic_alloc_vnic_resources(struct enic *enic) @@ -319,15 +304,14 @@ int enic_alloc_vnic_resources(struct enic *enic) intr_mode = vnic_dev_get_intr_mode(enic->vdev); - printk(KERN_INFO PFX "vNIC resources used: " + dev_info(enic_get_dev(enic), "vNIC resources used: " "wq %d rq %d cq %d intr %d intr mode %s\n", enic->wq_count, enic->rq_count, enic->cq_count, enic->intr_count, intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" : intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" : intr_mode == VNIC_DEV_INTR_MODE_MSIX ? "MSI-X" : - "unknown" - ); + "unknown"); /* Allocate queue resources */ @@ -373,7 +357,8 @@ int enic_alloc_vnic_resources(struct enic *enic) enic->legacy_pba = vnic_dev_get_res(enic->vdev, RES_TYPE_INTR_PBA_LEGACY, 0); if (!enic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) { - printk(KERN_ERR PFX "Failed to hook legacy pba resource\n"); + dev_err(enic_get_dev(enic), + "Failed to hook legacy pba resource\n"); err = -ENODEV; goto err_out_cleanup; } diff --git a/drivers/net/enic/enic_res.h b/drivers/net/enic/enic_res.h index 494664f7fccc..9a103d9ef9e2 100644 --- a/drivers/net/enic/enic_res.h +++ b/drivers/net/enic/enic_res.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -30,7 +30,7 @@ #define ENIC_MIN_RQ_DESCS 64 #define ENIC_MAX_RQ_DESCS 4096 -#define ENIC_MIN_MTU 576 /* minimum for IPv4 */ +#define ENIC_MIN_MTU 68 #define ENIC_MAX_MTU 9000 #define ENIC_MULTICAST_PERFECT_FILTERS 32 @@ -43,7 +43,7 @@ static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq, void *os_buf, dma_addr_t dma_addr, unsigned int len, unsigned int mss_or_csum_offset, unsigned int hdr_len, int vlan_tag_insert, unsigned int vlan_tag, - int offload_mode, int cq_entry, int sop, int eop) + int offload_mode, int cq_entry, int sop, int eop, int loopback) { struct wq_enet_desc *desc = vnic_wq_next_desc(wq); @@ -56,61 +56,62 @@ static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq, 0, /* fcoe_encap */ (u8)vlan_tag_insert, (u16)vlan_tag, - 0 /* loopback */); + (u8)loopback); vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop); } static inline void enic_queue_wq_desc_cont(struct vnic_wq *wq, - void *os_buf, dma_addr_t dma_addr, unsigned int len, int eop) + void *os_buf, dma_addr_t dma_addr, unsigned int len, + int eop, int loopback) { enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, 0, 0, 0, 0, 0, - eop, 0 /* !SOP */, eop); + eop, 0 /* !SOP */, eop, loopback); } static inline void enic_queue_wq_desc(struct vnic_wq *wq, void *os_buf, dma_addr_t dma_addr, unsigned int len, int vlan_tag_insert, - unsigned int vlan_tag, int eop) + unsigned int vlan_tag, int eop, int loopback) { enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, 0, 0, vlan_tag_insert, vlan_tag, WQ_ENET_OFFLOAD_MODE_CSUM, - eop, 1 /* SOP */, eop); + eop, 1 /* SOP */, eop, loopback); } static inline void enic_queue_wq_desc_csum(struct vnic_wq *wq, void *os_buf, dma_addr_t dma_addr, unsigned int len, int ip_csum, int tcpudp_csum, int vlan_tag_insert, - unsigned int vlan_tag, int eop) + unsigned int vlan_tag, int eop, int loopback) { enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, (ip_csum ? 1 : 0) + (tcpudp_csum ? 2 : 0), 0, vlan_tag_insert, vlan_tag, WQ_ENET_OFFLOAD_MODE_CSUM, - eop, 1 /* SOP */, eop); + eop, 1 /* SOP */, eop, loopback); } static inline void enic_queue_wq_desc_csum_l4(struct vnic_wq *wq, void *os_buf, dma_addr_t dma_addr, unsigned int len, unsigned int csum_offset, unsigned int hdr_len, - int vlan_tag_insert, unsigned int vlan_tag, int eop) + int vlan_tag_insert, unsigned int vlan_tag, int eop, int loopback) { enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, csum_offset, hdr_len, vlan_tag_insert, vlan_tag, WQ_ENET_OFFLOAD_MODE_CSUM_L4, - eop, 1 /* SOP */, eop); + eop, 1 /* SOP */, eop, loopback); } static inline void enic_queue_wq_desc_tso(struct vnic_wq *wq, void *os_buf, dma_addr_t dma_addr, unsigned int len, unsigned int mss, unsigned int hdr_len, int vlan_tag_insert, - unsigned int vlan_tag, int eop) + unsigned int vlan_tag, int eop, int loopback) { enic_queue_wq_desc_ex(wq, os_buf, dma_addr, len, mss, hdr_len, vlan_tag_insert, vlan_tag, WQ_ENET_OFFLOAD_MODE_TSO, - eop, 1 /* SOP */, eop); + eop, 1 /* SOP */, eop, loopback); } static inline void enic_queue_rq_desc(struct vnic_rq *rq, @@ -131,10 +132,8 @@ static inline void enic_queue_rq_desc(struct vnic_rq *rq, struct enic; int enic_get_vnic_config(struct enic *); -void enic_add_multicast_addr(struct enic *enic, u8 *addr); -void enic_del_multicast_addr(struct enic *enic, u8 *addr); -void enic_add_vlan(struct enic *enic, u16 vlanid); -void enic_del_vlan(struct enic *enic, u16 vlanid); +int enic_add_vlan(struct enic *enic, u16 vlanid); +int enic_del_vlan(struct enic *enic, u16 vlanid); int enic_set_nic_cfg(struct enic *enic, u8 rss_default_cpu, u8 rss_hash_type, u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en, u8 ig_vlan_strip_en); diff --git a/drivers/net/enic/rq_enet_desc.h b/drivers/net/enic/rq_enet_desc.h index a06e649010ce..e6dd30988d6f 100644 --- a/drivers/net/enic/rq_enet_desc.h +++ b/drivers/net/enic/rq_enet_desc.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify diff --git a/drivers/net/enic/vnic_cq.c b/drivers/net/enic/vnic_cq.c index 020ae6c3f3d9..b86d6ef8dad3 100644 --- a/drivers/net/enic/vnic_cq.c +++ b/drivers/net/enic/vnic_cq.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -42,7 +42,7 @@ int vnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, unsigned int index, cq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_CQ, index); if (!cq->ctrl) { - printk(KERN_ERR "Failed to hook CQ[%d] resource\n", index); + pr_err("Failed to hook CQ[%d] resource\n", index); return -EINVAL; } diff --git a/drivers/net/enic/vnic_cq.h b/drivers/net/enic/vnic_cq.h index 114763cbc2f8..552d3daf2508 100644 --- a/drivers/net/enic/vnic_cq.h +++ b/drivers/net/enic/vnic_cq.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/enic/vnic_dev.c index e0d33281ec98..fb35d8b17668 100644 --- a/drivers/net/enic/vnic_dev.c +++ b/drivers/net/enic/vnic_dev.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -23,21 +23,23 @@ #include <linux/pci.h> #include <linux/delay.h> #include <linux/if_ether.h> -#include <linux/slab.h> #include "vnic_resource.h" #include "vnic_devcmd.h" #include "vnic_dev.h" #include "vnic_stats.h" +enum vnic_proxy_type { + PROXY_NONE, + PROXY_BY_BDF, +}; + struct vnic_res { void __iomem *vaddr; dma_addr_t bus_addr; unsigned int count; }; -#define VNIC_DEV_CAP_INIT 0x0001 - struct vnic_dev { void *priv; struct pci_dev *pdev; @@ -48,13 +50,14 @@ struct vnic_dev { struct vnic_devcmd_notify notify_copy; dma_addr_t notify_pa; u32 notify_sz; - u32 *linkstatus; dma_addr_t linkstatus_pa; struct vnic_stats *stats; dma_addr_t stats_pa; struct vnic_devcmd_fw_info *fw_info; dma_addr_t fw_info_pa; - u32 cap_flags; + enum vnic_proxy_type proxy; + u32 proxy_index; + u64 args[VNIC_DEVCMD_NARGS]; }; #define VNIC_MAX_RES_HDR_SIZE \ @@ -71,6 +74,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev, struct vnic_dev_bar *bar, unsigned int num_bars) { struct vnic_resource_header __iomem *rh; + struct mgmt_barmap_hdr __iomem *mrh; struct vnic_resource __iomem *r; u8 type; @@ -78,26 +82,36 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev, return -EINVAL; if (bar->len < VNIC_MAX_RES_HDR_SIZE) { - printk(KERN_ERR "vNIC BAR0 res hdr length error\n"); + pr_err("vNIC BAR0 res hdr length error\n"); return -EINVAL; } - rh = bar->vaddr; + rh = bar->vaddr; + mrh = bar->vaddr; if (!rh) { - printk(KERN_ERR "vNIC BAR0 res hdr not mem-mapped\n"); + pr_err("vNIC BAR0 res hdr not mem-mapped\n"); return -EINVAL; } - if (ioread32(&rh->magic) != VNIC_RES_MAGIC || - ioread32(&rh->version) != VNIC_RES_VERSION) { - printk(KERN_ERR "vNIC BAR0 res magic/version error " - "exp (%lx/%lx) curr (%x/%x)\n", + /* Check for mgmt vnic in addition to normal vnic */ + if ((ioread32(&rh->magic) != VNIC_RES_MAGIC) || + (ioread32(&rh->version) != VNIC_RES_VERSION)) { + if ((ioread32(&mrh->magic) != MGMTVNIC_MAGIC) || + (ioread32(&mrh->version) != MGMTVNIC_VERSION)) { + pr_err("vNIC BAR0 res magic/version error " + "exp (%lx/%lx) or (%lx/%lx), curr (%x/%x)\n", VNIC_RES_MAGIC, VNIC_RES_VERSION, + MGMTVNIC_MAGIC, MGMTVNIC_VERSION, ioread32(&rh->magic), ioread32(&rh->version)); - return -EINVAL; + return -EINVAL; + } } - r = (struct vnic_resource __iomem *)(rh + 1); + if (ioread32(&mrh->magic) == MGMTVNIC_MAGIC) + r = (struct vnic_resource __iomem *)(mrh + 1); + else + r = (struct vnic_resource __iomem *)(rh + 1); + while ((type = ioread8(&r->type)) != RES_TYPE_EOL) { @@ -122,7 +136,7 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev, /* each count is stride bytes long */ len = count * VNIC_RES_STRIDE; if (len + bar_offset > bar[bar_num].len) { - printk(KERN_ERR "vNIC BAR0 resource %d " + pr_err("vNIC BAR0 resource %d " "out-of-bounds, offset 0x%x + " "size 0x%x > bar len 0x%lx\n", type, bar_offset, @@ -172,22 +186,7 @@ void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, } } -dma_addr_t vnic_dev_get_res_bus_addr(struct vnic_dev *vdev, - enum vnic_res_type type, unsigned int index) -{ - switch (type) { - case RES_TYPE_WQ: - case RES_TYPE_RQ: - case RES_TYPE_CQ: - case RES_TYPE_INTR_CTRL: - return vdev->res[type].bus_addr + - index * VNIC_RES_STRIDE; - default: - return vdev->res[type].bus_addr; - } -} - -unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, +static unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, unsigned int desc_count, unsigned int desc_size) { /* The base address of the desc rings must be 512 byte aligned. @@ -229,8 +228,7 @@ int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, &ring->base_addr_unaligned); if (!ring->descs_unaligned) { - printk(KERN_ERR - "Failed to allocate ring (size=%d), aborting\n", + pr_err("Failed to allocate ring (size=%d), aborting\n", (int)ring->size); return -ENOMEM; } @@ -258,23 +256,28 @@ void vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring) } } -int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, - u64 *a0, u64 *a1, int wait) +static int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + int wait) { struct vnic_devcmd __iomem *devcmd = vdev->devcmd; + unsigned int i; int delay; u32 status; int err; status = ioread32(&devcmd->status); + if (status == 0xFFFFFFFF) { + /* PCI-e target device is gone */ + return -ENODEV; + } if (status & STAT_BUSY) { - printk(KERN_ERR "Busy devcmd %d\n", _CMD_N(cmd)); + pr_err("Busy devcmd %d\n", _CMD_N(cmd)); return -EBUSY; } if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) { - writeq(*a0, &devcmd->args[0]); - writeq(*a1, &devcmd->args[1]); + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + writeq(vdev->args[i], &devcmd->args[i]); wmb(); } @@ -288,31 +291,98 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, udelay(100); status = ioread32(&devcmd->status); + if (status == 0xFFFFFFFF) { + /* PCI-e target device is gone */ + return -ENODEV; + } + if (!(status & STAT_BUSY)) { if (status & STAT_ERROR) { err = (int)readq(&devcmd->args[0]); if (err != ERR_ECMDUNKNOWN || cmd != CMD_CAPABILITY) - printk(KERN_ERR "Error %d devcmd %d\n", + pr_err("Error %d devcmd %d\n", err, _CMD_N(cmd)); return err; } if (_CMD_DIR(cmd) & _CMD_DIR_READ) { rmb(); - *a0 = readq(&devcmd->args[0]); - *a1 = readq(&devcmd->args[1]); + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + vdev->args[i] = readq(&devcmd->args[i]); } return 0; } } - printk(KERN_ERR "Timedout devcmd %d\n", _CMD_N(cmd)); + pr_err("Timedout devcmd %d\n", _CMD_N(cmd)); return -ETIMEDOUT; } +static int vnic_dev_cmd_proxy_by_bdf(struct vnic_dev *vdev, + enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait) +{ + u32 status; + int err; + + memset(vdev->args, 0, sizeof(vdev->args)); + + vdev->args[0] = vdev->proxy_index; /* bdf */ + vdev->args[1] = cmd; + vdev->args[2] = *a0; + vdev->args[3] = *a1; + + err = _vnic_dev_cmd(vdev, CMD_PROXY_BY_BDF, wait); + if (err) + return err; + + status = (u32)vdev->args[0]; + if (status & STAT_ERROR) { + err = (int)vdev->args[1]; + if (err != ERR_ECMDUNKNOWN || + cmd != CMD_CAPABILITY) + pr_err("Error %d proxy devcmd %d\n", err, _CMD_N(cmd)); + return err; + } + + *a0 = vdev->args[1]; + *a1 = vdev->args[2]; + + return 0; +} + +static int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev, + enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait) +{ + int err; + + vdev->args[0] = *a0; + vdev->args[1] = *a1; + + err = _vnic_dev_cmd(vdev, cmd, wait); + + *a0 = vdev->args[0]; + *a1 = vdev->args[1]; + + return err; +} + +int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait) +{ + memset(vdev->args, 0, sizeof(vdev->args)); + + switch (vdev->proxy) { + case PROXY_BY_BDF: + return vnic_dev_cmd_proxy_by_bdf(vdev, cmd, a0, a1, wait); + case PROXY_NONE: + default: + return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait); + } +} + static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd) { u64 a0 = (u32)cmd, a1 = 0; @@ -391,13 +461,6 @@ int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, return err; } -int vnic_dev_stats_clear(struct vnic_dev *vdev) -{ - u64 a0 = 0, a1 = 0; - int wait = 1000; - return vnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait); -} - int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats) { u64 a0, a1; @@ -424,11 +487,17 @@ int vnic_dev_close(struct vnic_dev *vdev) return vnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait); } -int vnic_dev_enable(struct vnic_dev *vdev) +int vnic_dev_enable_wait(struct vnic_dev *vdev) { u64 a0 = 0, a1 = 0; int wait = 1000; - return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait); + int err; + + err = vnic_dev_cmd(vdev, CMD_ENABLE_WAIT, &a0, &a1, wait); + if (err == ERR_ECMDUNKNOWN) + return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait); + + return err; } int vnic_dev_disable(struct vnic_dev *vdev) @@ -462,14 +531,14 @@ int vnic_dev_open_done(struct vnic_dev *vdev, int *done) return 0; } -int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) +static int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) { u64 a0 = (u32)arg, a1 = 0; int wait = 1000; return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); } -int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) +static int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) { u64 a0 = 0, a1 = 0; int wait = 1000; @@ -486,6 +555,44 @@ int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) return 0; } +int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = 1000; + int err; + + err = vnic_dev_cmd(vdev, CMD_HANG_RESET, &a0, &a1, wait); + if (err == ERR_ECMDUNKNOWN) { + err = vnic_dev_soft_reset(vdev, arg); + if (err) + return err; + + return vnic_dev_init(vdev, 0); + } + + return err; +} + +int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done) +{ + u64 a0 = 0, a1 = 0; + int wait = 1000; + int err; + + *done = 0; + + err = vnic_dev_cmd(vdev, CMD_HANG_RESET_STATUS, &a0, &a1, wait); + if (err) { + if (err == ERR_ECMDUNKNOWN) + return vnic_dev_soft_reset_done(vdev, done); + return err; + } + + *done = (a0 == 0); + + return 0; +} + int vnic_dev_hang_notify(struct vnic_dev *vdev) { u64 a0, a1; @@ -512,7 +619,7 @@ int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr) return 0; } -void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, +int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, int broadcast, int promisc, int allmulti) { u64 a0, a1 = 0; @@ -527,7 +634,9 @@ void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait); if (err) - printk(KERN_ERR "Can't set packet filter\n"); + pr_err("Can't set packet filter\n"); + + return err; } int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) @@ -542,7 +651,7 @@ int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr) err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait); if (err) - printk(KERN_ERR "Can't add addr [%pM], %d\n", addr, err); + pr_err("Can't add addr [%pM], %d\n", addr, err); return err; } @@ -559,26 +668,26 @@ int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr) err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait); if (err) - printk(KERN_ERR "Can't del addr [%pM], %d\n", addr, err); + pr_err("Can't del addr [%pM], %d\n", addr, err); return err; } -int vnic_dev_raise_intr(struct vnic_dev *vdev, u16 intr) +int vnic_dev_set_ig_vlan_rewrite_mode(struct vnic_dev *vdev, + u8 ig_vlan_rewrite_mode) { - u64 a0 = intr, a1 = 0; + u64 a0 = ig_vlan_rewrite_mode, a1 = 0; int wait = 1000; int err; - err = vnic_dev_cmd(vdev, CMD_IAR, &a0, &a1, wait); - if (err) - printk(KERN_ERR "Failed to raise INTR[%d], err %d\n", - intr, err); + err = vnic_dev_cmd(vdev, CMD_IG_VLAN_REWRITE_MODE, &a0, &a1, wait); + if (err == ERR_ECMDUNKNOWN) + return 0; return err; } -int vnic_dev_notify_setcmd(struct vnic_dev *vdev, +static int vnic_dev_notify_setcmd(struct vnic_dev *vdev, void *notify_addr, dma_addr_t notify_pa, u16 intr) { u64 a0, a1; @@ -604,8 +713,7 @@ int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr) dma_addr_t notify_pa; if (vdev->notify || vdev->notify_pa) { - printk(KERN_ERR "notify block %p still allocated", - vdev->notify); + pr_err("notify block %p still allocated", vdev->notify); return -EINVAL; } @@ -618,22 +726,25 @@ int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr) return vnic_dev_notify_setcmd(vdev, notify_addr, notify_pa, intr); } -void vnic_dev_notify_unsetcmd(struct vnic_dev *vdev) +static int vnic_dev_notify_unsetcmd(struct vnic_dev *vdev) { u64 a0, a1; int wait = 1000; + int err; a0 = 0; /* paddr = 0 to unset notify buffer */ a1 = 0x0000ffff00000000ULL; /* intr num = -1 to unreg for intr */ a1 += sizeof(struct vnic_devcmd_notify); - vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); + err = vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); vdev->notify = NULL; vdev->notify_pa = 0; vdev->notify_sz = 0; + + return err; } -void vnic_dev_notify_unset(struct vnic_dev *vdev) +int vnic_dev_notify_unset(struct vnic_dev *vdev) { if (vdev->notify) { pci_free_consistent(vdev->pdev, @@ -642,7 +753,7 @@ void vnic_dev_notify_unset(struct vnic_dev *vdev) vdev->notify_pa); } - vnic_dev_notify_unsetcmd(vdev); + return vnic_dev_notify_unsetcmd(vdev); } static int vnic_dev_notify_ready(struct vnic_dev *vdev) @@ -672,13 +783,14 @@ int vnic_dev_init(struct vnic_dev *vdev, int arg) int wait = 1000; int r = 0; - if (vdev->cap_flags & VNIC_DEV_CAP_INIT) + if (vnic_dev_capable(vdev, CMD_INIT)) r = vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait); else { vnic_dev_cmd(vdev, CMD_INIT_v1, &a0, &a1, wait); if (a0 & CMD_INITF_DEFAULT_MAC) { - // Emulate these for old CMD_INIT_v1 which - // didn't pass a0 so no CMD_INITF_*. + /* Emulate these for old CMD_INIT_v1 which + * didn't pass a0 so no CMD_INITF_*. + */ vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait); vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait); } @@ -700,7 +812,7 @@ int vnic_dev_init_done(struct vnic_dev *vdev, int *done, int *err) *done = (a0 == 0); - *err = (a0 == 0) ? a1 : 0; + *err = (a0 == 0) ? (int)a1:0; return 0; } @@ -738,9 +850,6 @@ int vnic_dev_deinit(struct vnic_dev *vdev) int vnic_dev_link_status(struct vnic_dev *vdev) { - if (vdev->linkstatus) - return *vdev->linkstatus; - if (!vnic_dev_notify_ready(vdev)) return 0; @@ -771,22 +880,6 @@ u32 vnic_dev_mtu(struct vnic_dev *vdev) return vdev->notify_copy.mtu; } -u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev) -{ - if (!vnic_dev_notify_ready(vdev)) - return 0; - - return vdev->notify_copy.link_down_cnt; -} - -u32 vnic_dev_notify_status(struct vnic_dev *vdev) -{ - if (!vnic_dev_notify_ready(vdev)) - return 0; - - return vdev->notify_copy.status; -} - void vnic_dev_set_intr_mode(struct vnic_dev *vdev, enum vnic_dev_intr_mode intr_mode) { @@ -807,14 +900,9 @@ void vnic_dev_unregister(struct vnic_dev *vdev) sizeof(struct vnic_devcmd_notify), vdev->notify, vdev->notify_pa); - if (vdev->linkstatus) - pci_free_consistent(vdev->pdev, - sizeof(u32), - vdev->linkstatus, - vdev->linkstatus_pa); if (vdev->stats) pci_free_consistent(vdev->pdev, - sizeof(struct vnic_dev), + sizeof(struct vnic_stats), vdev->stats, vdev->stats_pa); if (vdev->fw_info) pci_free_consistent(vdev->pdev, @@ -844,11 +932,6 @@ struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, if (!vdev->devcmd) goto err_out; - vdev->cap_flags = 0; - - if (vnic_dev_capable(vdev, CMD_INIT)) - vdev->cap_flags |= VNIC_DEV_CAP_INIT; - return vdev; err_out: diff --git a/drivers/net/enic/vnic_dev.h b/drivers/net/enic/vnic_dev.h index caccce36957b..05f9a24cd459 100644 --- a/drivers/net/enic/vnic_dev.h +++ b/drivers/net/enic/vnic_dev.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -41,6 +41,9 @@ static inline void writeq(u64 val, void __iomem *reg) } #endif +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + enum vnic_dev_hw_version { VNIC_DEV_HW_VER_UNKNOWN, VNIC_DEV_HW_VER_A1, @@ -81,10 +84,6 @@ unsigned int vnic_dev_get_res_count(struct vnic_dev *vdev, enum vnic_res_type type); void __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, unsigned int index); -dma_addr_t vnic_dev_get_res_bus_addr(struct vnic_dev *vdev, - enum vnic_res_type type, unsigned int index); -unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring, - unsigned int desc_count, unsigned int desc_size); void vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring); int vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, unsigned int desc_count, unsigned int desc_size); @@ -98,28 +97,21 @@ int vnic_dev_hw_version(struct vnic_dev *vdev, enum vnic_dev_hw_version *hw_ver); int vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size, void *value); -int vnic_dev_stats_clear(struct vnic_dev *vdev); int vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats); int vnic_dev_hang_notify(struct vnic_dev *vdev); -void vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, +int vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast, int broadcast, int promisc, int allmulti); int vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr); int vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr); int vnic_dev_mac_addr(struct vnic_dev *vdev, u8 *mac_addr); -int vnic_dev_raise_intr(struct vnic_dev *vdev, u16 intr); -int vnic_dev_notify_setcmd(struct vnic_dev *vdev, - void *notify_addr, dma_addr_t notify_pa, u16 intr); int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr); -void vnic_dev_notify_unsetcmd(struct vnic_dev *vdev); -void vnic_dev_notify_unset(struct vnic_dev *vdev); +int vnic_dev_notify_unset(struct vnic_dev *vdev); int vnic_dev_link_status(struct vnic_dev *vdev); u32 vnic_dev_port_speed(struct vnic_dev *vdev); u32 vnic_dev_msg_lvl(struct vnic_dev *vdev); u32 vnic_dev_mtu(struct vnic_dev *vdev); -u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev); -u32 vnic_dev_notify_status(struct vnic_dev *vdev); int vnic_dev_close(struct vnic_dev *vdev); -int vnic_dev_enable(struct vnic_dev *vdev); +int vnic_dev_enable_wait(struct vnic_dev *vdev); int vnic_dev_disable(struct vnic_dev *vdev); int vnic_dev_open(struct vnic_dev *vdev, int arg); int vnic_dev_open_done(struct vnic_dev *vdev, int *done); @@ -127,12 +119,14 @@ int vnic_dev_init(struct vnic_dev *vdev, int arg); int vnic_dev_init_done(struct vnic_dev *vdev, int *done, int *err); int vnic_dev_init_prov(struct vnic_dev *vdev, u8 *buf, u32 len); int vnic_dev_deinit(struct vnic_dev *vdev); -int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg); -int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done); +int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg); +int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done); void vnic_dev_set_intr_mode(struct vnic_dev *vdev, enum vnic_dev_intr_mode intr_mode); enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev); void vnic_dev_unregister(struct vnic_dev *vdev); +int vnic_dev_set_ig_vlan_rewrite_mode(struct vnic_dev *vdev, + u8 ig_vlan_rewrite_mode); struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev, void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar, unsigned int num_bars); diff --git a/drivers/net/enic/vnic_devcmd.h b/drivers/net/enic/vnic_devcmd.h index d78bbcc1fdf9..9abb3d51dea1 100644 --- a/drivers/net/enic/vnic_devcmd.h +++ b/drivers/net/enic/vnic_devcmd.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -98,6 +98,9 @@ enum vnic_devcmd_cmd { /* set Rx packet filter: (u32)a0=filters (see CMD_PFILTER_*) */ CMD_PACKET_FILTER = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ENET, 7), + /* set Rx packet filter for all: (u32)a0=filters (see CMD_PFILTER_*) */ + CMD_PACKET_FILTER_ALL = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 7), + /* hang detection notification */ CMD_HANG_NOTIFY = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 8), @@ -171,6 +174,9 @@ enum vnic_devcmd_cmd { /* enable virtual link */ CMD_ENABLE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28), + /* enable virtual link, waiting variant. */ + CMD_ENABLE_WAIT = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28), + /* disable virtual link */ CMD_DISABLE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29), @@ -211,6 +217,39 @@ enum vnic_devcmd_cmd { * in: (u16)a0=interrupt number to assert */ CMD_IAR = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 38), + + /* initiate hangreset, like softreset after hang detected */ + CMD_HANG_RESET = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 39), + + /* hangreset status: + * out: a0=0 reset complete, a0=1 reset in progress */ + CMD_HANG_RESET_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 40), + + /* + * Set hw ingress packet vlan rewrite mode: + * in: (u32)a0=new vlan rewrite mode + * out: (u32)a0=old vlan rewrite mode */ + CMD_IG_VLAN_REWRITE_MODE = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ENET, 41), + + /* + * in: (u16)a0=bdf of target vnic + * (u32)a1=cmd to proxy + * a2-a15=args to cmd in a1 + * out: (u32)a0=status of proxied cmd + * a1-a15=out args of proxied cmd */ + CMD_PROXY_BY_BDF = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 42), + + /* + * As for BY_BDF except a0 is index of hvnlink subordinate vnic + * or SR-IOV virtual vnic */ + CMD_PROXY_BY_INDEX = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 43), + + /* + * in: (u64)a0=paddr of buffer to put latest VIC VIF-CONFIG-INFO TLV in + * (u32)a1=length of buffer in a0 + * out: (u64)a0=paddr of buffer with latest VIC VIF-CONFIG-INFO TLV + * (u32)a1=actual length of latest VIC VIF-CONFIG-INFO TLV */ + CMD_CONFIG_INFO_GET = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 44), }; /* flags for CMD_OPEN */ @@ -226,6 +265,12 @@ enum vnic_devcmd_cmd { #define CMD_PFILTER_PROMISCUOUS 0x08 #define CMD_PFILTER_ALL_MULTICAST 0x10 +/* rewrite modes for CMD_IG_VLAN_REWRITE_MODE */ +#define IG_VLAN_REWRITE_MODE_DEFAULT_TRUNK 0 +#define IG_VLAN_REWRITE_MODE_UNTAG_DEFAULT_VLAN 1 +#define IG_VLAN_REWRITE_MODE_PRIORITY_TAG_DEFAULT_VLAN 2 +#define IG_VLAN_REWRITE_MODE_PASS_THRU 3 + enum vnic_devcmd_status { STAT_NONE = 0, STAT_BUSY = 1 << 0, /* cmd in progress */ diff --git a/drivers/net/enic/vnic_enet.h b/drivers/net/enic/vnic_enet.h index 8eeb6758491b..e8740e3704e4 100644 --- a/drivers/net/enic/vnic_enet.h +++ b/drivers/net/enic/vnic_enet.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -30,11 +30,12 @@ struct vnic_enet_config { u32 wq_desc_count; u32 rq_desc_count; u16 mtu; - u16 intr_timer; + u16 intr_timer_deprecated; u8 intr_timer_type; u8 intr_mode; char devname[16]; u32 intr_timer_usec; + u16 loop_tag; }; #define VENETF_TSO 0x1 /* TSO enabled */ @@ -48,5 +49,6 @@ struct vnic_enet_config { #define VENETF_RSSHASH_TCPIPV6 0x100 /* Hash on TCP + IPv6 fields */ #define VENETF_RSSHASH_IPV6_EX 0x200 /* Hash on IPv6 extended fields */ #define VENETF_RSSHASH_TCPIPV6_EX 0x400 /* Hash on TCP + IPv6 ext. fields */ +#define VENETF_LOOP 0x800 /* Loopback enabled */ #endif /* _VNIC_ENIC_H_ */ diff --git a/drivers/net/enic/vnic_intr.c b/drivers/net/enic/vnic_intr.c index 3934309a9498..3873771d75cc 100644 --- a/drivers/net/enic/vnic_intr.c +++ b/drivers/net/enic/vnic_intr.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -39,8 +39,7 @@ int vnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr, intr->ctrl = vnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index); if (!intr->ctrl) { - printk(KERN_ERR "Failed to hook INTR[%d].ctrl resource\n", - index); + pr_err("Failed to hook INTR[%d].ctrl resource\n", index); return -EINVAL; } @@ -66,8 +65,3 @@ void vnic_intr_clean(struct vnic_intr *intr) { iowrite32(0, &intr->ctrl->int_credits); } - -void vnic_intr_raise(struct vnic_intr *intr) -{ - vnic_dev_raise_intr(intr->vdev, (u16)intr->index); -} diff --git a/drivers/net/enic/vnic_intr.h b/drivers/net/enic/vnic_intr.h index 2fe6c6339e3c..09dc0b73ff46 100644 --- a/drivers/net/enic/vnic_intr.h +++ b/drivers/net/enic/vnic_intr.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -61,7 +61,11 @@ static inline void vnic_intr_unmask(struct vnic_intr *intr) static inline void vnic_intr_mask(struct vnic_intr *intr) { iowrite32(1, &intr->ctrl->mask); - (void)ioread32(&intr->ctrl->mask); +} + +static inline int vnic_intr_masked(struct vnic_intr *intr) +{ + return ioread32(&intr->ctrl->mask); } static inline void vnic_intr_return_credits(struct vnic_intr *intr, diff --git a/drivers/net/enic/vnic_nic.h b/drivers/net/enic/vnic_nic.h index cf80ab46d582..995a50dd4c99 100644 --- a/drivers/net/enic/vnic_nic.h +++ b/drivers/net/enic/vnic_nic.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify diff --git a/drivers/net/enic/vnic_resource.h b/drivers/net/enic/vnic_resource.h index b61c22aec41a..e0a73f1ca6f4 100644 --- a/drivers/net/enic/vnic_resource.h +++ b/drivers/net/enic/vnic_resource.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -22,6 +22,11 @@ #define VNIC_RES_MAGIC 0x766E6963L /* 'vnic' */ #define VNIC_RES_VERSION 0x00000000L +#define MGMTVNIC_MAGIC 0x544d474dL /* 'MGMT' */ +#define MGMTVNIC_VERSION 0x00000000L + +/* The MAC address assigned to the CFG vNIC is fixed. */ +#define MGMTVNIC_MAC { 0x02, 0x00, 0x54, 0x4d, 0x47, 0x4d } /* vNIC resource types */ enum vnic_res_type { @@ -52,6 +57,14 @@ struct vnic_resource_header { u32 version; }; +struct mgmt_barmap_hdr { + u32 magic; /* magic number */ + u32 version; /* header format version */ + u16 lif; /* loopback lif for mgmt frames */ + u16 pci_slot; /* installed pci slot */ + char serial[16]; /* card serial number */ +}; + struct vnic_resource { u8 type; u8 bar; diff --git a/drivers/net/enic/vnic_rq.c b/drivers/net/enic/vnic_rq.c index cc580cfec41d..34105e0951a5 100644 --- a/drivers/net/enic/vnic_rq.c +++ b/drivers/net/enic/vnic_rq.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -37,23 +37,23 @@ static int vnic_rq_alloc_bufs(struct vnic_rq *rq) vdev = rq->vdev; for (i = 0; i < blks; i++) { - rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ, GFP_ATOMIC); + rq->bufs[i] = kzalloc(VNIC_RQ_BUF_BLK_SZ(count), GFP_ATOMIC); if (!rq->bufs[i]) { - printk(KERN_ERR "Failed to alloc rq_bufs\n"); + pr_err("Failed to alloc rq_bufs\n"); return -ENOMEM; } } for (i = 0; i < blks; i++) { buf = rq->bufs[i]; - for (j = 0; j < VNIC_RQ_BUF_BLK_ENTRIES; j++) { - buf->index = i * VNIC_RQ_BUF_BLK_ENTRIES + j; + for (j = 0; j < VNIC_RQ_BUF_BLK_ENTRIES(count); j++) { + buf->index = i * VNIC_RQ_BUF_BLK_ENTRIES(count) + j; buf->desc = (u8 *)rq->ring.descs + rq->ring.desc_size * buf->index; if (buf->index + 1 == count) { buf->next = rq->bufs[0]; break; - } else if (j + 1 == VNIC_RQ_BUF_BLK_ENTRIES) { + } else if (j + 1 == VNIC_RQ_BUF_BLK_ENTRIES(count)) { buf->next = rq->bufs[i + 1]; } else { buf->next = buf + 1; @@ -77,8 +77,10 @@ void vnic_rq_free(struct vnic_rq *rq) vnic_dev_free_desc_ring(vdev, &rq->ring); for (i = 0; i < VNIC_RQ_BUF_BLKS_MAX; i++) { - kfree(rq->bufs[i]); - rq->bufs[i] = NULL; + if (rq->bufs[i]) { + kfree(rq->bufs[i]); + rq->bufs[i] = NULL; + } } rq->ctrl = NULL; @@ -94,7 +96,7 @@ int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index, rq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_RQ, index); if (!rq->ctrl) { - printk(KERN_ERR "Failed to hook RQ[%d] resource\n", index); + pr_err("Failed to hook RQ[%d] resource\n", index); return -EINVAL; } @@ -113,16 +115,17 @@ int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index, return 0; } -void vnic_rq_init_start(struct vnic_rq *rq, unsigned int cq_index, +static void vnic_rq_init_start(struct vnic_rq *rq, unsigned int cq_index, unsigned int fetch_index, unsigned int posted_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset) { u64 paddr; + unsigned int count = rq->ring.desc_count; paddr = (u64)rq->ring.base_addr | VNIC_PADDR_TARGET; writeq(paddr, &rq->ctrl->ring_base); - iowrite32(rq->ring.desc_count, &rq->ctrl->ring_size); + iowrite32(count, &rq->ctrl->ring_size); iowrite32(cq_index, &rq->ctrl->cq_index); iowrite32(error_interrupt_enable, &rq->ctrl->error_interrupt_enable); iowrite32(error_interrupt_offset, &rq->ctrl->error_interrupt_offset); @@ -132,8 +135,8 @@ void vnic_rq_init_start(struct vnic_rq *rq, unsigned int cq_index, iowrite32(posted_index, &rq->ctrl->posted_index); rq->to_use = rq->to_clean = - &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES] - [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES]; + &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES(count)] + [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES(count)]; } void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index, @@ -145,6 +148,11 @@ void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index, /* Use current fetch_index as the ring starting point */ fetch_index = ioread32(&rq->ctrl->fetch_index); + if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */ + /* Hardware surprise removal: reset fetch_index */ + fetch_index = 0; + } + vnic_rq_init_start(rq, cq_index, fetch_index, fetch_index, error_interrupt_enable, @@ -174,7 +182,7 @@ int vnic_rq_disable(struct vnic_rq *rq) udelay(10); } - printk(KERN_ERR "Failed to disable RQ[%d]\n", rq->index); + pr_err("Failed to disable RQ[%d]\n", rq->index); return -ETIMEDOUT; } @@ -184,8 +192,7 @@ void vnic_rq_clean(struct vnic_rq *rq, { struct vnic_rq_buf *buf; u32 fetch_index; - - BUG_ON(ioread32(&rq->ctrl->enable)); + unsigned int count = rq->ring.desc_count; buf = rq->to_clean; @@ -199,9 +206,14 @@ void vnic_rq_clean(struct vnic_rq *rq, /* Use current fetch_index as the ring starting point */ fetch_index = ioread32(&rq->ctrl->fetch_index); + + if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone */ + /* Hardware surprise removal: reset fetch_index */ + fetch_index = 0; + } rq->to_use = rq->to_clean = - &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES] - [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES]; + &rq->bufs[fetch_index / VNIC_RQ_BUF_BLK_ENTRIES(count)] + [fetch_index % VNIC_RQ_BUF_BLK_ENTRIES(count)]; iowrite32(fetch_index, &rq->ctrl->posted_index); vnic_dev_clear_desc_ring(&rq->ring); diff --git a/drivers/net/enic/vnic_rq.h b/drivers/net/enic/vnic_rq.h index 35e736cc2d88..37f08de2454a 100644 --- a/drivers/net/enic/vnic_rq.h +++ b/drivers/net/enic/vnic_rq.h @@ -1,5 +1,5 @@ /* - * Copyright 2008, 2009 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -52,12 +52,16 @@ struct vnic_rq_ctrl { u32 pad10; }; -/* Break the vnic_rq_buf allocations into blocks of 64 entries */ -#define VNIC_RQ_BUF_BLK_ENTRIES 64 -#define VNIC_RQ_BUF_BLK_SZ \ - (VNIC_RQ_BUF_BLK_ENTRIES * sizeof(struct vnic_rq_buf)) +/* Break the vnic_rq_buf allocations into blocks of 32/64 entries */ +#define VNIC_RQ_BUF_MIN_BLK_ENTRIES 32 +#define VNIC_RQ_BUF_DFLT_BLK_ENTRIES 64 +#define VNIC_RQ_BUF_BLK_ENTRIES(entries) \ + ((unsigned int)((entries < VNIC_RQ_BUF_DFLT_BLK_ENTRIES) ? \ + VNIC_RQ_BUF_MIN_BLK_ENTRIES : VNIC_RQ_BUF_DFLT_BLK_ENTRIES)) +#define VNIC_RQ_BUF_BLK_SZ(entries) \ + (VNIC_RQ_BUF_BLK_ENTRIES(entries) * sizeof(struct vnic_rq_buf)) #define VNIC_RQ_BUF_BLKS_NEEDED(entries) \ - DIV_ROUND_UP(entries, VNIC_RQ_BUF_BLK_ENTRIES) + DIV_ROUND_UP(entries, VNIC_RQ_BUF_BLK_ENTRIES(entries)) #define VNIC_RQ_BUF_BLKS_MAX VNIC_RQ_BUF_BLKS_NEEDED(4096) struct vnic_rq_buf { @@ -139,7 +143,7 @@ static inline void vnic_rq_post(struct vnic_rq *rq, static inline int vnic_rq_posting_soon(struct vnic_rq *rq) { - return ((rq->to_use->index & VNIC_RQ_RETURN_RATE) == 0); + return (rq->to_use->index & VNIC_RQ_RETURN_RATE) == 0; } static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count) @@ -198,10 +202,6 @@ static inline int vnic_rq_fill(struct vnic_rq *rq, void vnic_rq_free(struct vnic_rq *rq); int vnic_rq_alloc(struct vnic_dev *vdev, struct vnic_rq *rq, unsigned int index, unsigned int desc_count, unsigned int desc_size); -void vnic_rq_init_start(struct vnic_rq *rq, unsigned int cq_index, - unsigned int fetch_index, unsigned int posted_index, - unsigned int error_interrupt_enable, - unsigned int error_interrupt_offset); void vnic_rq_init(struct vnic_rq *rq, unsigned int cq_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset); diff --git a/drivers/net/enic/vnic_rss.h b/drivers/net/enic/vnic_rss.h index 5fbb3c923bcd..fa421baf45b8 100644 --- a/drivers/net/enic/vnic_rss.h +++ b/drivers/net/enic/vnic_rss.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -37,9 +37,4 @@ union vnic_rss_cpu { u64 raw[32]; }; -void vnic_set_rss_key(union vnic_rss_key *rss_key, u8 *key); -void vnic_set_rss_cpu(union vnic_rss_cpu *rss_cpu, u8 *cpu); -void vnic_get_rss_key(union vnic_rss_key *rss_key, u8 *key); -void vnic_get_rss_cpu(union vnic_rss_cpu *rss_cpu, u8 *cpu); - #endif /* _VNIC_RSS_H_ */ diff --git a/drivers/net/enic/vnic_stats.h b/drivers/net/enic/vnic_stats.h index 9ff9614d89b1..77750ec93954 100644 --- a/drivers/net/enic/vnic_stats.h +++ b/drivers/net/enic/vnic_stats.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify diff --git a/drivers/net/enic/vnic_vic.c b/drivers/net/enic/vnic_vic.c index d769772998c6..4725b79de0ef 100644 --- a/drivers/net/enic/vnic_vic.c +++ b/drivers/net/enic/vnic_vic.c @@ -25,9 +25,13 @@ struct vic_provinfo *vic_provinfo_alloc(gfp_t flags, u8 *oui, u8 type) { - struct vic_provinfo *vp = kzalloc(VIC_PROVINFO_MAX_DATA, flags); + struct vic_provinfo *vp; - if (!vp || !oui) + if (!oui) + return NULL; + + vp = kzalloc(VIC_PROVINFO_MAX_DATA, flags); + if (!vp) return NULL; memcpy(vp->oui, oui, sizeof(vp->oui)); @@ -50,8 +54,8 @@ int vic_provinfo_add_tlv(struct vic_provinfo *vp, u16 type, u16 length, if (!vp || !value) return -EINVAL; - if (ntohl(vp->length) + sizeof(*tlv) + length > - VIC_PROVINFO_MAX_TLV_DATA) + if (ntohl(vp->length) + offsetof(struct vic_provinfo_tlv, value) + + length > VIC_PROVINFO_MAX_TLV_DATA) return -ENOMEM; tlv = (struct vic_provinfo_tlv *)((u8 *)vp->tlv + @@ -62,7 +66,8 @@ int vic_provinfo_add_tlv(struct vic_provinfo *vp, u16 type, u16 length, memcpy(tlv->value, value, length); vp->num_tlvs = htonl(ntohl(vp->num_tlvs) + 1); - vp->length = htonl(ntohl(vp->length) + sizeof(*tlv) + length); + vp->length = htonl(ntohl(vp->length) + + offsetof(struct vic_provinfo_tlv, value) + length); return 0; } diff --git a/drivers/net/enic/vnic_vic.h b/drivers/net/enic/vnic_vic.h index 085c2a274cb1..7e46e5e8600f 100644 --- a/drivers/net/enic/vnic_vic.h +++ b/drivers/net/enic/vnic_vic.h @@ -44,7 +44,7 @@ struct vic_provinfo { u16 length; u8 value[0]; } tlv[0]; -} __attribute__ ((packed)); +} __packed; #define VIC_PROVINFO_MAX_DATA 1385 #define VIC_PROVINFO_MAX_TLV_DATA (VIC_PROVINFO_MAX_DATA - \ diff --git a/drivers/net/enic/vnic_wq.c b/drivers/net/enic/vnic_wq.c index 1378afbdfe67..df61bd932ea6 100644 --- a/drivers/net/enic/vnic_wq.c +++ b/drivers/net/enic/vnic_wq.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -37,23 +37,23 @@ static int vnic_wq_alloc_bufs(struct vnic_wq *wq) vdev = wq->vdev; for (i = 0; i < blks; i++) { - wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC); + wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ(count), GFP_ATOMIC); if (!wq->bufs[i]) { - printk(KERN_ERR "Failed to alloc wq_bufs\n"); + pr_err("Failed to alloc wq_bufs\n"); return -ENOMEM; } } for (i = 0; i < blks; i++) { buf = wq->bufs[i]; - for (j = 0; j < VNIC_WQ_BUF_BLK_ENTRIES; j++) { - buf->index = i * VNIC_WQ_BUF_BLK_ENTRIES + j; + for (j = 0; j < VNIC_WQ_BUF_BLK_ENTRIES(count); j++) { + buf->index = i * VNIC_WQ_BUF_BLK_ENTRIES(count) + j; buf->desc = (u8 *)wq->ring.descs + wq->ring.desc_size * buf->index; if (buf->index + 1 == count) { buf->next = wq->bufs[0]; break; - } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES) { + } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES(count)) { buf->next = wq->bufs[i + 1]; } else { buf->next = buf + 1; @@ -77,8 +77,10 @@ void vnic_wq_free(struct vnic_wq *wq) vnic_dev_free_desc_ring(vdev, &wq->ring); for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) { - kfree(wq->bufs[i]); - wq->bufs[i] = NULL; + if (wq->bufs[i]) { + kfree(wq->bufs[i]); + wq->bufs[i] = NULL; + } } wq->ctrl = NULL; @@ -94,7 +96,7 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, wq->ctrl = vnic_dev_get_res(vdev, RES_TYPE_WQ, index); if (!wq->ctrl) { - printk(KERN_ERR "Failed to hook WQ[%d] resource\n", index); + pr_err("Failed to hook WQ[%d] resource\n", index); return -EINVAL; } @@ -113,16 +115,17 @@ int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, return 0; } -void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, +static void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, unsigned int fetch_index, unsigned int posted_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset) { u64 paddr; + unsigned int count = wq->ring.desc_count; paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET; writeq(paddr, &wq->ctrl->ring_base); - iowrite32(wq->ring.desc_count, &wq->ctrl->ring_size); + iowrite32(count, &wq->ctrl->ring_size); iowrite32(fetch_index, &wq->ctrl->fetch_index); iowrite32(posted_index, &wq->ctrl->posted_index); iowrite32(cq_index, &wq->ctrl->cq_index); @@ -131,8 +134,8 @@ void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, iowrite32(0, &wq->ctrl->error_status); wq->to_use = wq->to_clean = - &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES] - [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES]; + &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES(count)] + [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES(count)]; } void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, @@ -167,7 +170,7 @@ int vnic_wq_disable(struct vnic_wq *wq) udelay(10); } - printk(KERN_ERR "Failed to disable WQ[%d]\n", wq->index); + pr_err("Failed to disable WQ[%d]\n", wq->index); return -ETIMEDOUT; } @@ -177,8 +180,6 @@ void vnic_wq_clean(struct vnic_wq *wq, { struct vnic_wq_buf *buf; - BUG_ON(ioread32(&wq->ctrl->enable)); - buf = wq->to_clean; while (vnic_wq_desc_used(wq) > 0) { diff --git a/drivers/net/enic/vnic_wq.h b/drivers/net/enic/vnic_wq.h index 9c34d41a887e..7dd937ac11c2 100644 --- a/drivers/net/enic/vnic_wq.h +++ b/drivers/net/enic/vnic_wq.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify @@ -60,12 +60,16 @@ struct vnic_wq_buf { void *desc; }; -/* Break the vnic_wq_buf allocations into blocks of 64 entries */ -#define VNIC_WQ_BUF_BLK_ENTRIES 64 -#define VNIC_WQ_BUF_BLK_SZ \ - (VNIC_WQ_BUF_BLK_ENTRIES * sizeof(struct vnic_wq_buf)) +/* Break the vnic_wq_buf allocations into blocks of 32/64 entries */ +#define VNIC_WQ_BUF_MIN_BLK_ENTRIES 32 +#define VNIC_WQ_BUF_DFLT_BLK_ENTRIES 64 +#define VNIC_WQ_BUF_BLK_ENTRIES(entries) \ + ((unsigned int)((entries < VNIC_WQ_BUF_DFLT_BLK_ENTRIES) ? \ + VNIC_WQ_BUF_MIN_BLK_ENTRIES : VNIC_WQ_BUF_DFLT_BLK_ENTRIES)) +#define VNIC_WQ_BUF_BLK_SZ(entries) \ + (VNIC_WQ_BUF_BLK_ENTRIES(entries) * sizeof(struct vnic_wq_buf)) #define VNIC_WQ_BUF_BLKS_NEEDED(entries) \ - DIV_ROUND_UP(entries, VNIC_WQ_BUF_BLK_ENTRIES) + DIV_ROUND_UP(entries, VNIC_WQ_BUF_BLK_ENTRIES(entries)) #define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096) struct vnic_wq { @@ -149,10 +153,6 @@ static inline void vnic_wq_service(struct vnic_wq *wq, void vnic_wq_free(struct vnic_wq *wq); int vnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, unsigned int index, unsigned int desc_count, unsigned int desc_size); -void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, - unsigned int fetch_index, unsigned int posted_index, - unsigned int error_interrupt_enable, - unsigned int error_interrupt_offset); void vnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, unsigned int error_interrupt_enable, unsigned int error_interrupt_offset); diff --git a/drivers/net/enic/wq_enet_desc.h b/drivers/net/enic/wq_enet_desc.h index 483596c2d8bf..c7021e3a631f 100644 --- a/drivers/net/enic/wq_enet_desc.h +++ b/drivers/net/enic/wq_enet_desc.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. * * This program is free software; you may redistribute it and/or modify |