lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Fri, 03 Aug 2007 21:04:59 -0700 From: "Michael Chan" <mchan@...adcom.com> To: davem@...emloft.net, mchristi@...hat.com, netdev@...r.kernel.org, open-iscsi@...glegroups.com cc: anilgv@...adcom.com, talm@...adcom.com, lusinsky@...adcom.com, uri@...adcom.com Subject: [RFC 1/2][BNX2]: Add iSCSI support to BNX2 devices. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 81ef81c..b23c142 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2237,6 +2237,16 @@ config BNX2 To compile this driver as a module, choose M here: the module will be called bnx2. This is recommended. +config CNIC + tristate "Broadcom CNIC support" + depends on BNX2 + help + This driver supports offload features of Broadcom NetXtremeII + gigabit Ethernet cards. + + To compile this driver as a module, choose M here: the module + will be called cnic. This is recommended. + config SPIDER_NET tristate "Spider Gigabit Ethernet driver" depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e684212..3b5f057 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_STNIC) += stnic.o 8390.o obj-$(CONFIG_FEALNX) += fealnx.o obj-$(CONFIG_TIGON3) += tg3.o obj-$(CONFIG_BNX2) += bnx2.o +obj-$(CONFIG_CNIC) += cnic.o spidernet-y += spider_net.o spider_net_ethtool.o obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o obj-$(CONFIG_GELIC_NET) += ps3_gelic.o diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index d53dfc5..a0a0a83 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -242,7 +242,7 @@ static inline u32 bnx2_tx_avail(struct bnx2 *bp) return (bp->tx_ring_size - diff); } -static u32 +u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset) { u32 val; @@ -254,7 +254,7 @@ bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset) return val; } -static void +void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val) { spin_lock_bh(&bp->indirect_lock); @@ -263,7 +263,7 @@ bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val) spin_unlock_bh(&bp->indirect_lock); } -static void +void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) { offset += cid_addr; @@ -447,6 +447,26 @@ bnx2_netif_start(struct bnx2 *bp) } static void +bnx2_cnic_stop(struct bnx2 *bp) +{ + struct cnic_ops *c_ops; + + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + c_ops->cnic_stop(bp->cnic_data); +} + +static void +bnx2_cnic_start(struct bnx2 *bp) +{ + struct cnic_ops *c_ops; + + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + c_ops->cnic_start(bp->cnic_data); +} + +static void bnx2_free_mem(struct bnx2 *bp) { int i; @@ -2628,6 +2648,9 @@ bnx2_has_work(struct bnx2 *bp) (sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS)) return 1; + if (rcu_dereference(bp->cnic_ops) && (bp->cnic_tag != sblk->status_idx)) + return 1; + return 0; } @@ -2638,6 +2661,7 @@ bnx2_poll(struct net_device *dev, int *budget) struct status_block *sblk = bp->status_blk; u32 status_attn_bits = sblk->status_attn_bits; u32 status_attn_bits_ack = sblk->status_attn_bits_ack; + struct cnic_ops *c_ops; if ((status_attn_bits & STATUS_ATTN_EVENTS) != (status_attn_bits_ack & STATUS_ATTN_EVENTS)) { @@ -2667,6 +2691,13 @@ bnx2_poll(struct net_device *dev, int *budget) dev->quota -= work_done; } + rcu_read_lock(); + c_ops = rcu_dereference(bp->cnic_ops); + if (c_ops) + bp->cnic_tag = c_ops->cnic_handler(bp->cnic_data, + bp->status_blk); + rcu_read_unlock(); + bp->last_status_idx = bp->status_blk->status_idx; rmb(); @@ -2692,6 +2723,53 @@ bnx2_poll(struct net_device *dev, int *budget) return 1; } +int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops, void *data) +{ + struct bnx2 *bp = netdev_priv(dev); + + if (ops == NULL) + return -EINVAL; + + if (!try_module_get(ops->cnic_owner)) + return -EBUSY; + + bp->cnic_data = data; + rcu_assign_pointer(bp->cnic_ops, ops); + + if (netif_running(bp->dev)) { + struct status_block *status_blk = bp->status_blk; + int i = 0; + + while (status_blk->status_completion_producer_index && i < 10) { + REG_WR(bp, BNX2_HC_COMMAND, + bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); + REG_RD(bp, BNX2_HC_COMMAND); + udelay(10); + i++; + /* Tell compiler status_blk fields can change. */ + barrier(); + } + if (status_blk->status_completion_producer_index) { + printk(KERN_ERR PFX "%s: " + "KCQ index not resetting to 0.\n", + bp->dev->name); + return -EBUSY; + } + } + + return 0; +} + +int bnx2_unregister_cnic(struct net_device *dev) +{ + struct bnx2 *bp = netdev_priv(dev); + + module_put(bp->cnic_ops->cnic_owner); + rcu_assign_pointer(bp->cnic_ops, NULL); + synchronize_rcu(); + return 0; +} + /* Called with rtnl_lock from vlan functions and also netif_tx_lock * from set_multicast. */ @@ -2767,7 +2845,7 @@ bnx2_set_rx_mode(struct net_device *dev) spin_unlock_bh(&bp->phy_lock); } -#define FW_BUF_SIZE 0x8000 +#define FW_BUF_SIZE 0x10000 static int bnx2_gunzip_init(struct bnx2 *bp) @@ -3107,13 +3185,13 @@ bnx2_init_cpus(struct bnx2 *bp) cpu_reg.spad_base = BNX2_CP_SCRATCH; cpu_reg.mips_view_base = 0x8000000; - if (CHIP_NUM(bp) == CHIP_NUM_5709) { + if (CHIP_NUM(bp) == CHIP_NUM_5709) fw = &bnx2_cp_fw_09; + else + fw = &bnx2_cp_fw_06; + + rc = load_cpu_fw(bp, &cpu_reg, fw); - rc = load_cpu_fw(bp, &cpu_reg, fw); - if (rc) - goto init_cpu_err; - } init_cpu_err: bnx2_gunzip_end(bp); return rc; @@ -5112,12 +5190,14 @@ bnx2_reset_task(struct work_struct *work) return; bp->in_reset_task = 1; + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); bnx2_init_nic(bp); atomic_set(&bp->intr_sem, 1); bnx2_netif_start(bp); + bnx2_cnic_start(bp); bp->in_reset_task = 0; } @@ -5803,9 +5883,11 @@ bnx2_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) bp->stats_ticks &= BNX2_HC_STATS_TICKS_HC_STAT_TICKS; if (netif_running(bp->dev)) { + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); bnx2_init_nic(bp); bnx2_netif_start(bp); + bnx2_cnic_start(bp); } return 0; @@ -5839,7 +5921,9 @@ bnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) return -EINVAL; } + if (netif_running(bp->dev)) { + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_RESET); bnx2_free_skbs(bp); @@ -5857,6 +5941,7 @@ bnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) return rc; bnx2_init_nic(bp); bnx2_netif_start(bp); + bnx2_cnic_start(bp); } return 0; @@ -6082,6 +6167,7 @@ bnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) if (etest->flags & ETH_TEST_FL_OFFLINE) { int i; + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_DIAG); bnx2_free_skbs(bp); @@ -6103,6 +6189,7 @@ bnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) else { bnx2_init_nic(bp); bnx2_netif_start(bp); + bnx2_cnic_start(bp); } /* wait for link up */ @@ -6354,11 +6441,13 @@ bnx2_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; if (netif_running(dev)) { + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); bnx2_init_nic(bp); bnx2_netif_start(bp); + bnx2_cnic_start(bp); } return 0; } @@ -6941,6 +7030,7 @@ bnx2_suspend(struct pci_dev *pdev, pm_message_t state) return 0; flush_scheduled_work(); + bnx2_cnic_stop(bp); bnx2_netif_stop(bp); netif_device_detach(dev); del_timer_sync(&bp->timer); @@ -6971,6 +7061,7 @@ bnx2_resume(struct pci_dev *pdev) netif_device_attach(dev); bnx2_init_nic(bp); bnx2_netif_start(bp); + bnx2_cnic_start(bp); return 0; } @@ -6996,5 +7087,8 @@ static void __exit bnx2_cleanup(void) module_init(bnx2_init); module_exit(bnx2_cleanup); - - +EXPORT_SYMBOL(bnx2_register_cnic); +EXPORT_SYMBOL(bnx2_unregister_cnic); +EXPORT_SYMBOL(bnx2_reg_rd_ind); +EXPORT_SYMBOL(bnx2_reg_wr_ind); +EXPORT_SYMBOL(bnx2_ctx_wr); diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index 102adfe..75929c7 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -6465,6 +6465,16 @@ struct flash_spec { u8 *name; }; +struct cnic_ops { + struct module *cnic_owner; + void (*cnic_stop)(void *); + void (*cnic_start)(void *); + int (*cnic_handler)(void *, struct status_block *); +}; + +extern int bnx2_register_cnic(struct net_device *, struct cnic_ops *, void *); +extern int bnx2_unregister_cnic(struct net_device *); + struct bnx2 { /* Fields used in the tx and intr/napi performance paths are grouped */ /* together in the beginning of the structure. */ @@ -6524,6 +6534,10 @@ struct bnx2 { int tx_ring_size; u32 tx_wake_thresh; + struct cnic_ops *cnic_ops; + void *cnic_data; + int cnic_tag; + /* End of fields used in the performance code paths. */ char *name; @@ -6635,6 +6649,7 @@ struct bnx2 { u16 req_line_speed; u8 req_duplex; + u8 req_port; u8 phy_port; u8 link_up; @@ -6684,8 +6699,10 @@ struct bnx2 { void *gunzip_buf; }; -static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset); -static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val); +extern u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset); +extern void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val); +extern u32 bnx2_reg_rd_ind_locked(struct bnx2 *bp, u32 offset); +extern void bnx2_reg_wr_ind_locked(struct bnx2 *bp, u32 offset, u32 val); #define REG_RD(bp, offset) \ readl(bp->regview + offset) @@ -6704,7 +6721,9 @@ static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val); /* Indirect context access. Unlike the MBQ_WR, these macros will not * trigger a chip event. */ -static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val); +extern void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val); +extern void bnx2_ctx_wr_locked(struct bnx2 *bp, u32 cid_addr, u32 offset, + u32 val); #define CTX_WR(bp, cid_addr, offset, val) \ bnx2_ctx_wr(bp, cid_addr, offset, val) diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c new file mode 100644 index 0000000..207fd19 --- /dev/null +++ b/drivers/net/cnic.c @@ -0,0 +1,1885 @@ +/* cnic.c: Broadcom CNIC core network driver. + * + * Copyright (c) 2006-2007 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: John(Zongxi) Chen (zongxi@...adcom.com) + * Modified and maintained by: Michael Chan <mchan@...adcom.com> + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/in.h> +#include <linux/dma-mapping.h> +#include <asm/byteorder.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/if_vlan.h> +#define BCM_VLAN 1 +#include <net/ip.h> +#include <net/tcp.h> +#include <linux/workqueue.h> +#include <net/arp.h> +#include <net/neighbour.h> +#include <net/route.h> +#include <net/netevent.h> + +#include "bnx2.h" +#include "cnic_if.h" +#include "cnic.h" +#include "cnic_cm.h" + +#define DRV_MODULE_NAME "cnic" +#define PFX DRV_MODULE_NAME ": " + +static char version[] __devinitdata = + "Broadcom NetXtreme II CNIC Driver " DRV_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n"; + +MODULE_AUTHOR("John(Zongxi) Chen <zongxic@...adcom.com>"); +MODULE_DESCRIPTION("Broadcom NetXtreme II CNIC Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CNIC_MODULE_VERSION); + +static LIST_HEAD(cnic_dev_list); +static DEFINE_RWLOCK(cnic_dev_lock); +static DEFINE_MUTEX(cnic_lock); + +struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE]; + +static int cnic_service_kq(void *, struct status_block *); +static void cnic_service_stop(void *); +static void cnic_service_start(void *); + +static struct cnic_ops my_cnic_ops = { + .cnic_owner = THIS_MODULE, + .cnic_handler = cnic_service_kq, + .cnic_stop = cnic_service_stop, + .cnic_start = cnic_service_start, +}; + +static inline void cnic_hold(struct cnic_dev *dev) +{ + atomic_inc(&dev->ref_count); +} + +static inline void cnic_put(struct cnic_dev *dev) +{ + atomic_dec(&dev->ref_count); +} + +int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) +{ + struct cnic_dev *dev; + + if (ulp_type >= MAX_CNIC_ULP_TYPE) { + printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n", + ulp_type); + return -EINVAL; + } + mutex_lock(&cnic_lock); + if (cnic_ulp_tbl[ulp_type]) { + printk(KERN_ERR PFX "cnic_register_driver: Type %d has already " + "been registered\n", ulp_type); + mutex_unlock(&cnic_lock); + return -EBUSY; + } + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + struct cnic_local *cp = dev->cnic_priv; + + clear_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]); + } + read_unlock(&cnic_dev_lock); + + rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops); + mutex_unlock(&cnic_lock); + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + struct cnic_local *cp = dev->cnic_priv; + + /* Prevent race conditions with netdev_event */ + rtnl_lock(); + if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type])) + ulp_ops->cnic_init(dev); + rtnl_unlock(); + } + read_unlock(&cnic_dev_lock); + + return 0; +} + +int cnic_unregister_driver(int ulp_type) +{ + struct cnic_dev *dev; + + if (ulp_type >= MAX_CNIC_ULP_TYPE) { + printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n", + ulp_type); + return -EINVAL; + } + mutex_lock(&cnic_lock); + if (!cnic_ulp_tbl[ulp_type]) { + printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not " + "been registered\n", ulp_type); + goto out_unlock; + } + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + struct cnic_local *cp = dev->cnic_priv; + + if (rcu_dereference(cp->ulp_ops[ulp_type])) { + printk(KERN_ERR PFX "cnic_unregister_driver: Type %d " + "still has devices registered\n", ulp_type); + read_unlock(&cnic_dev_lock); + goto out_unlock; + } + } + read_unlock(&cnic_dev_lock); + + rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL); + + mutex_unlock(&cnic_lock); + synchronize_rcu(); + return 0; + +out_unlock: + mutex_unlock(&cnic_lock); + return -EINVAL; +} + +EXPORT_SYMBOL(cnic_register_driver); +EXPORT_SYMBOL(cnic_unregister_driver); + +static int cnic_start_hw(struct cnic_dev *); +static void cnic_stop_hw(struct cnic_dev *); + +static int cnic_register_device(struct cnic_dev *dev, int ulp_type, + void *ulp_ctx) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_ulp_ops *ulp_ops; + + if (ulp_type >= MAX_CNIC_ULP_TYPE) { + printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n", + ulp_type); + return -EINVAL; + } + mutex_lock(&cnic_lock); + if (cnic_ulp_tbl[ulp_type] == NULL) { + printk(KERN_ERR PFX "cnic_register_device: Driver with type %d " + "has not been registered\n", ulp_type); + mutex_unlock(&cnic_lock); + return -EAGAIN; + } + if (rcu_dereference(cp->ulp_ops[ulp_type])) { + printk(KERN_ERR PFX "cnic_register_device: Type %d has already " + "been registered to this device\n", ulp_type); + mutex_unlock(&cnic_lock); + return -EBUSY; + } + if (!try_module_get(cnic_ulp_tbl[ulp_type]->owner)) { + mutex_unlock(&cnic_lock); + return -EBUSY; + } + + clear_bit(ULP_F_START, &cp->ulp_flags[ulp_type]); + cp->ulp_handle[ulp_type] = ulp_ctx; + ulp_ops = cnic_ulp_tbl[ulp_type]; + rcu_assign_pointer(cp->ulp_ops[ulp_type], ulp_ops); + cnic_hold(dev); + dev->use_count++; + + if (dev->use_count == 1) { + if (test_bit(CNIC_F_IF_UP, &dev->flags)) + cnic_start_hw(dev); + } + + if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) + if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[ulp_type])) + ulp_ops->cnic_start(cp->ulp_handle[ulp_type]); + + mutex_unlock(&cnic_lock); + + return 0; + +} + +static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) +{ + struct cnic_local *cp = dev->cnic_priv; + + if (ulp_type >= MAX_CNIC_ULP_TYPE) { + printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n", + ulp_type); + return -EINVAL; + } + mutex_lock(&cnic_lock); + if (rcu_dereference(cp->ulp_ops[ulp_type])) { + dev->use_count--; + module_put(cp->ulp_ops[ulp_type]->owner); + rcu_assign_pointer(cp->ulp_ops[ulp_type], NULL); + if (dev->use_count == 0) + cnic_stop_hw(dev); + cnic_put(dev); + } else { + printk(KERN_ERR PFX "cnic_unregister_device: device not " + "registered to this ulp type %d\n", ulp_type); + mutex_unlock(&cnic_lock); + return -EINVAL; + } + mutex_unlock(&cnic_lock); + + synchronize_rcu(); + + return 0; +} + +#ifdef CNIC_SYSFS +static ssize_t show_pci_bar(struct class_device *cdev, char *buf) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + + return sprintf(buf, "0x%.8x\n", (u32)pci_resource_start(dev->pcidev, 0)); +} + +static ssize_t show_kq_intr_coal(struct class_device *cdev, char *buf) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + + return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_COMP_PROD_TRIP)); +} + +static ssize_t set_kq_intr_coal(struct class_device *cdev, const char *buf, + size_t count) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + u32 val; + + if (sscanf(buf, " 0x%x ", &val) > 0) + REG_WR(dev, BNX2_HC_COMP_PROD_TRIP, val); + return count; +} + +static ssize_t show_kq_com_ticks(struct class_device *cdev, char *buf) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + + return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_COM_TICKS)); +} + +static ssize_t set_kq_com_ticks(struct class_device *cdev, const char *buf, + size_t count) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + u32 val; + + if (sscanf(buf, " 0x%x ", &val) > 0) + REG_WR(dev, BNX2_HC_COM_TICKS, val); + return count; +} + +static ssize_t show_kq_cmd_ticks(struct class_device *cdev, char *buf) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + + return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_CMD_TICKS)); +} + +static ssize_t set_kq_cmd_ticks(struct class_device *cdev, const char *buf, + size_t count) +{ + struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev); + u32 val; + + if (sscanf(buf, " 0x%x ", &val) > 0) + REG_WR(dev, BNX2_HC_CMD_TICKS, val); + return count; +} + +static CLASS_DEVICE_ATTR(pci_bar, S_IRUGO, show_pci_bar, NULL); +static CLASS_DEVICE_ATTR(kq_intr_coal, S_IRUGO | S_IWUSR, show_kq_intr_coal, + set_kq_intr_coal); +static CLASS_DEVICE_ATTR(kq_com_ticks, S_IRUGO | S_IWUSR, show_kq_com_ticks, + set_kq_com_ticks); +static CLASS_DEVICE_ATTR(kq_cmd_ticks, S_IRUGO | S_IWUSR, show_kq_cmd_ticks, + set_kq_cmd_ticks); + +static struct class_device_attribute *cnic_class_attributes[] = { + &class_device_attr_pci_bar, + &class_device_attr_kq_intr_coal, + &class_device_attr_kq_com_ticks, + &class_device_attr_kq_cmd_ticks +}; + +static void cnic_sysfs_release(struct class_device *class_dev) +{ +} + +static struct class cnic_class = { + .name = "cnic", + .release = cnic_sysfs_release, +}; + +static int cnic_register_sysfs(struct cnic_dev *device) +{ + struct class_device *class_dev = &device->class_dev; + char dev_name[BUS_ID_SIZE]; + int ret; + int i; + + class_dev->class = &cnic_class; + class_dev->class_data = device; + snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x", + device->pcidev->bus->number, PCI_SLOT(device->pcidev->devfn), + PCI_FUNC(device->pcidev->devfn)); + strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE); + + ret = class_device_register(class_dev); + if (ret) + goto err; + + for (i = 0; i < ARRAY_SIZE(cnic_class_attributes); ++i) { + ret = class_device_create_file(class_dev, + cnic_class_attributes[i]); + if (ret) + goto err_unregister; + } + + return 0; + +err_unregister: + class_device_unregister(class_dev); +err: + return ret; +} + +static void cnic_unregister_sysfs(struct cnic_dev *device) +{ + class_device_unregister(&device->class_dev); +} + +static int cnic_sysfs_setup(void) +{ + return class_register(&cnic_class); +} + +static void cnic_sysfs_cleanup(void) +{ + class_unregister(&cnic_class); +} +#else +static void cnic_unregister_sysfs(struct cnic_dev *device) +{ +} + +static int cnic_register_sysfs(struct cnic_dev *device) +{ + return 0; +} + +static int cnic_sysfs_setup(void) +{ + return 0; +} + +static void cnic_sysfs_cleanup(void) +{ +} + +#endif + +static void cnic_free_resc(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + if (cp->kwq_pgtbl) { + pci_free_consistent(dev->pcidev, cp->kwq_pgtbl_size, + cp->kwq_pgtbl, cp->kwq_pgtbl_mapping); + cp->kwq_pgtbl = NULL; + } + for (i = 0; i < KWQ_PAGE_CNT; i++) { + if (cp->kwq[i]) { + pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE, + cp->kwq[i], cp->kwq_mapping[i]); + cp->kwq[i] = NULL; + } + } + if (cp->kcq_pgtbl) { + pci_free_consistent(dev->pcidev, cp->kcq_pgtbl_size, + cp->kcq_pgtbl, cp->kcq_pgtbl_mapping); + cp->kcq_pgtbl = NULL; + } + for (i = 0; i < KCQ_PAGE_CNT; i++) { + if (cp->kcq[i]) { + pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE, + cp->kcq[i], cp->kcq_mapping[i]); + cp->kcq[i] = NULL; + } + } + + return; +} + +static int cnic_alloc_resc(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + cp->kwq_pgtbl_size = ((KWQ_PAGE_CNT * 8) + BCM_PAGE_SIZE - 1) & + (BCM_PAGE_SIZE - 1); + cp->kwq_pgtbl = pci_alloc_consistent(dev->pcidev, cp->kwq_pgtbl_size, + &cp->kwq_pgtbl_mapping); + if (cp->kwq_pgtbl == NULL) + goto error; + + for (i = 0; i < KWQ_PAGE_CNT; i++) { + cp->kwq[i] = pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE, + &cp->kwq_mapping[i]); + if (cp->kwq[i] == NULL) + goto error; + } + + cp->kcq_pgtbl_size = ((KCQ_PAGE_CNT * 8) + BCM_PAGE_SIZE - 1) & + (BCM_PAGE_SIZE - 1); + cp->kcq_pgtbl = pci_alloc_consistent(dev->pcidev, cp->kcq_pgtbl_size, + &cp->kcq_pgtbl_mapping); + if (cp->kcq_pgtbl == NULL) + goto error; + + for (i = 0; i < KCQ_PAGE_CNT; i++) { + cp->kcq[i] = pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE, + &cp->kcq_mapping[i]); + if (cp->kcq[i] == NULL) + goto error; + } + + return 0; + +error: + cnic_free_resc(dev); + return -ENOMEM; +} + +static void cnic_setup_page_tbl(u32 *page_table, u32 page_cnt, + dma_addr_t base_mapping[]) +{ + int i; + + for (i = 0; i < page_cnt; i++) { + /* Each entry needs to be in big endian format. */ + *page_table = (u32) ((u64) base_mapping[i] >> 32); + page_table++; + *page_table = (u32) base_mapping[i]; + page_table++; + } +} + +static inline u32 cnic_kwq_avail(struct cnic_local *cp) +{ + return (MAX_KWQ_IDX - + ((cp->kwq_prod_idx - cp->kwq_con_idx) & MAX_KWQ_IDX)); +} + +static int cnic_submit_kernel_wqes(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num_wqes) +{ + struct cnic_local *cp = dev->cnic_priv; + struct kwqe *prod_qe; + u16 prod, sw_prod, i; + + if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) + return -EAGAIN; /* bnx2 is down */ + + spin_lock_bh(&cp->cnic_ulp_lock); + if (num_wqes > cnic_kwq_avail(cp)) { + spin_unlock_bh(&cp->cnic_ulp_lock); + return -EAGAIN; + } + + prod = cp->kwq_prod_idx; + sw_prod = prod & MAX_KWQ_IDX; + for (i = 0; i < num_wqes; i++) { + prod_qe = &cp->kwq[KWQ_PG(sw_prod)][KWQ_IDX(sw_prod)]; + memcpy(prod_qe, wqes[i], sizeof(struct kwqe)); + prod++; + sw_prod = prod & MAX_KWQ_IDX; + } + cp->kwq_prod_idx = prod; + + REG_WR16(dev, cp->kwq_io_addr, cp->kwq_prod_idx); + /* Proper ordering of REG_WR16 from different CPUs. */ + mmiowb(); + + spin_unlock_bh(&cp->cnic_ulp_lock); + return 0; +} + +static void service_kcqes(struct cnic_dev *dev, int num_cqes) +{ + struct cnic_local *cp = dev->cnic_priv; + int i, j; + + i = 0; + j = 1; + while (num_cqes) { + struct cnic_ulp_ops *ulp_ops; + int ulp_type; + u32 kcqe_op_flag = cp->completed_kcq[i]->kcqe_op_flag & + KCQE_FLAGS_LAYER_MASK; + + while (j < num_cqes) { + if ((cp->completed_kcq[i + j]->kcqe_op_flag & + KCQE_FLAGS_LAYER_MASK) != kcqe_op_flag) { + break; + } + j++; + } + + if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L5_RDMA) + ulp_type = CNIC_ULP_RDMA; + else if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L5_ISCSI) + ulp_type = CNIC_ULP_ISCSI; + else if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L4) + ulp_type = CNIC_ULP_L4; + else { + printk(KERN_ERR PFX "%s: Unknown type of KCQE(0x%x)\n", + dev->netdev->name, kcqe_op_flag); + goto end; + } + + rcu_read_lock(); + ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]); + if (likely(ulp_ops)) { + ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type], + cp->completed_kcq + i, j); + } + rcu_read_unlock(); +end: + num_cqes -= j; + i += j; + j = 1; + } + return; +} + +static int cnic_service_kq(void *data, struct status_block *status_blk) +{ + struct cnic_dev *dev = data; + struct cnic_local *cp = dev->cnic_priv; + u32 status_idx = status_blk->status_idx; + u16 hw_prod, sw_prod; + + cp->kwq_con_idx = status_blk->status_rx_quick_consumer_index15; + + hw_prod = status_blk->status_completion_producer_index; + sw_prod = cp->kcq_prod_idx; + while (sw_prod != hw_prod) { + u16 i, max; + struct kcqe *kcqe; + int kcqe_cnt = 0; + + i = sw_prod & MAX_KCQ_IDX; + max = hw_prod & MAX_KCQ_IDX; + while ((i != max) && (kcqe_cnt < MAX_COMPLETED_KCQE)) { + cp->completed_kcq[kcqe_cnt++] = + &cp->kcq[KCQ_PG(i)][KCQ_IDX(i)]; + i = (i + 1) & MAX_KCQ_IDX; + } + + kcqe = cp->completed_kcq[kcqe_cnt - 1]; + while (kcqe->kcqe_op_flag & KCQE_FLAGS_NEXT) { + kcqe_cnt--; + if (kcqe_cnt == 0) + goto done; + kcqe = cp->completed_kcq[kcqe_cnt - 1]; + } + sw_prod += kcqe_cnt; + + service_kcqes(dev, kcqe_cnt); + + REG_WR16(dev, cp->kcq_io_addr, sw_prod); + + if (hw_prod != status_blk->status_completion_producer_index) + hw_prod = status_blk->status_completion_producer_index; + else + break; + } +done: + /* Proper ordering of REG_WR16 from different CPUs. */ + mmiowb(); + cp->kcq_prod_idx = sw_prod; + return status_idx; +} + +static void cnic_ulp_stop(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int if_type; + + rcu_read_lock(); + for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { + struct cnic_ulp_ops *ulp_ops; + + ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); + if (!ulp_ops) + continue; + + if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type])) + ulp_ops->cnic_stop(cp->ulp_handle[if_type]); + } + rcu_read_unlock(); +} + +static void cnic_ulp_start(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int if_type; + + rcu_read_lock(); + for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { + struct cnic_ulp_ops *ulp_ops; + + ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); + if (!ulp_ops) + continue; + + if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type])) + ulp_ops->cnic_start(cp->ulp_handle[if_type]); + } + rcu_read_unlock(); +} + +static void cnic_service_start(void *data) +{ + struct cnic_dev *dev = data; + + cnic_hold(dev); + mutex_lock(&cnic_lock); + set_bit(CNIC_F_IF_UP, &dev->flags); + if (dev->use_count) { + if (!cnic_start_hw(dev)) + cnic_ulp_start(dev); + } + mutex_unlock(&cnic_lock); + cnic_put(dev); +} + +static void cnic_service_stop(void *data) +{ + struct cnic_dev *dev = data; + + cnic_hold(dev); + mutex_lock(&cnic_lock); + clear_bit(CNIC_F_IF_UP, &dev->flags); + cnic_ulp_stop(dev); + cnic_stop_hw(dev); + mutex_unlock(&cnic_lock); + cnic_put(dev); +} + +static void cnic_ulp_init(struct cnic_dev *dev) +{ + int i; + struct cnic_local *cp = dev->cnic_priv; + + rcu_read_lock(); + for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) { + struct cnic_ulp_ops *ulp_ops; + + ulp_ops = rcu_dereference(cnic_ulp_tbl[i]); + if (!ulp_ops || !try_module_get(ulp_ops->owner)) + continue; + + if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i])) + ulp_ops->cnic_init(dev); + + module_put(ulp_ops->owner); + } + rcu_read_unlock(); +} + +static void cnic_ulp_exit(struct cnic_dev *dev) +{ + int i; + struct cnic_local *cp = dev->cnic_priv; + + rcu_read_lock(); + for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) { + struct cnic_ulp_ops *ulp_ops; + + ulp_ops = rcu_dereference(cnic_ulp_tbl[i]); + if (!ulp_ops || !try_module_get(ulp_ops->owner)) + continue; + + if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i])) + ulp_ops->cnic_exit(dev); + + module_put(ulp_ops->owner); + } + rcu_read_unlock(); +} + +static int cnic_queue_work(struct cnic_local *cp, u32 work_type, void *data) +{ + struct cnic_work_node *node; + int bytes = sizeof(u32 *); + + spin_lock_bh(&cp->cm_lock); + + node = &cp->cnic_work_ring[cp->cnic_wr_prod]; + node->work_type = work_type; + if (work_type == WORK_TYPE_KCQE) + bytes = sizeof(struct kcqe); + if (work_type == WORK_TYPE_REDIRECT) + bytes = sizeof(struct cnic_redirect_entry); + memcpy(&node->work_data, data, bytes); + cp->cnic_wr_prod++; + cp->cnic_wr_prod &= WORK_RING_SIZE_MASK; + + spin_unlock_bh(&cp->cm_lock); + return 0; +} + +static int cnic_cm_offload_pg(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_offload_pg *l4kwqe; + struct kwqe *wqes[1]; + struct neighbour *neigh = csk->dst->neighbour; + struct net_device *netdev = neigh->dev; + + l4kwqe = (struct l4_kwq_offload_pg *) &csk->kwqe1; + memset(l4kwqe, 0, sizeof(*l4kwqe)); + wqes[0] = (struct kwqe *) l4kwqe; + + l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_OFFLOAD_PG; + l4kwqe->flags = + L4_LAYER_CODE << L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT; + l4kwqe->l2hdr_nbytes = ETH_HLEN; + l4kwqe->da0 = neigh->ha[0]; + l4kwqe->da1 = neigh->ha[1]; + l4kwqe->da2 = neigh->ha[2]; + l4kwqe->da3 = neigh->ha[3]; + l4kwqe->da4 = neigh->ha[4]; + l4kwqe->da5 = neigh->ha[5]; + + l4kwqe->sa0 = netdev->dev_addr[0]; + l4kwqe->sa1 = netdev->dev_addr[1]; + l4kwqe->sa2 = netdev->dev_addr[2]; + l4kwqe->sa3 = netdev->dev_addr[3]; + l4kwqe->sa4 = netdev->dev_addr[4]; + l4kwqe->sa5 = netdev->dev_addr[5]; + + l4kwqe->etype = ETH_P_IP; + l4kwqe->ipid_count = DEF_IPID_COUNT; + l4kwqe->host_opaque = csk->l5_cid; + + if (csk->vlan_id) { + l4kwqe->pg_flags |= L4_KWQ_OFFLOAD_PG_VLAN_TAGGING; + l4kwqe->vlan_tag = csk->vlan_id; + l4kwqe->l2hdr_nbytes += 4; + } + + return (dev->submit_kwqes(dev, wqes, 1)); +} + +static int cnic_cm_update_pg(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_update_pg *l4kwqe; + struct kwqe *wqes[1]; + struct neighbour *neigh = csk->dst->neighbour; + + l4kwqe = (struct l4_kwq_update_pg *) &csk->kwqe1; + memset(l4kwqe, 0, sizeof(*l4kwqe)); + wqes[0] = (struct kwqe *) l4kwqe; + + l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPDATE_PG; + l4kwqe->flags = + L4_LAYER_CODE << L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT; + l4kwqe->pg_cid = csk->pg_cid; + l4kwqe->da0 = neigh->ha[0]; + l4kwqe->da1 = neigh->ha[1]; + l4kwqe->da2 = neigh->ha[2]; + l4kwqe->da3 = neigh->ha[3]; + l4kwqe->da4 = neigh->ha[4]; + l4kwqe->da5 = neigh->ha[5]; + + l4kwqe->pg_host_opaque = 0; + l4kwqe->pg_valids = L4_KWQ_UPDATE_PG_VALIDS_DA; + + return (dev->submit_kwqes(dev, wqes, 1)); +} + +static int cnic_cm_upload_pg(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_upload *l4kwqe; + struct kwqe *wqes[1]; + + l4kwqe = (struct l4_kwq_upload *) &csk->kwqe1; + memset(l4kwqe, 0, sizeof(*l4kwqe)); + wqes[0] = (struct kwqe *) l4kwqe; + + l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPLOAD_PG; + l4kwqe->flags = + L4_LAYER_CODE << L4_KWQ_UPLOAD_LAYER_CODE_SHIFT; + l4kwqe->cid = csk->pg_cid; + + return (dev->submit_kwqes(dev, wqes, 1)); +} + +static void cnic_redirect(struct cnic_local *cp, struct dst_entry *new, + struct dst_entry *old) +{ + int i, found = 0; + + spin_lock_bh(&cp->cm_lock); + for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) { + struct cnic_sock *csk; + + csk = &cp->csk_tbl[i]; + if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst == old) + found = 1; + } + spin_unlock_bh(&cp->cm_lock); + + if (found) { + struct cnic_redirect_entry cnic_redir; + + dst_hold(new); + cnic_redir.old_dst = old; + cnic_redir.new_dst = new; + cnic_queue_work(cp, WORK_TYPE_REDIRECT, &cnic_redir); + schedule_work(&cp->cnic_task); + } +} + +static void cnic_update_neigh(struct cnic_local *cp, struct neighbour *neigh) +{ + int i, found = 0; + + spin_lock_bh(&cp->cm_lock); + for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) { + struct cnic_sock *csk; + + csk = &cp->csk_tbl[i]; + if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst) { + if (csk->dst->neighbour == neigh) + found = 1; + } + } + spin_unlock_bh(&cp->cm_lock); + + if (!found) + return; + + neigh_hold(neigh); + + cnic_queue_work(cp, WORK_TYPE_NEIGH_UPDATE, &neigh); + schedule_work(&cp->cnic_task); +} + +static int cnic_net_callback(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct cnic_local *cp = container_of(this, struct cnic_local, cm_nb); + + if (event == NETEVENT_NEIGH_UPDATE) { + struct neighbour *neigh = ptr; + + cnic_update_neigh(cp, neigh); + + } else if (event == NETEVENT_REDIRECT) { + struct netevent_redirect *netevent = ptr; + struct dst_entry *old_dst = netevent->old; + struct dst_entry *new_dst = netevent->new; + + cnic_redirect(cp, new_dst, old_dst); + } + return 0; +} + +static int cnic_ok_to_connect(struct cnic_sock *csk) +{ + if (test_bit(SK_F_INUSE, &csk->flags) && + !test_bit(SK_F_OFFLD_PENDING, &csk->flags) && + !test_bit(SK_F_OFFLD_COMPLETE, &csk->flags) && + test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags)) + return 1; + return 0; +} + +static int cnic_cm_conn_req(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_connect_req1 *l4kwqe1; + struct l4_kwq_connect_req3 *l4kwqe3; + struct kwqe *wqes[2]; + u8 tcp_flags = 0; + + l4kwqe1 = (struct l4_kwq_connect_req1 *) &csk->kwqe2; + l4kwqe3 = (struct l4_kwq_connect_req3 *) &csk->kwqe3; + memset(l4kwqe1, 0, sizeof(*l4kwqe1)); + memset(l4kwqe3, 0, sizeof(*l4kwqe3)); + wqes[0] = (struct kwqe *) l4kwqe1; + wqes[1] = (struct kwqe *) l4kwqe3; + + l4kwqe1->op_code = L4_KWQE_OPCODE_VALUE_CONNECT1; + l4kwqe1->flags = + (L4_LAYER_CODE << L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT) | + L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT; + l4kwqe1->cid = csk->cid; + l4kwqe1->pg_cid = csk->pg_cid; + l4kwqe1->src_ip = be32_to_cpu(csk->src_ip); + l4kwqe1->dst_ip = be32_to_cpu(csk->dst_ip); + l4kwqe1->src_port = be16_to_cpu(csk->src_port); + l4kwqe1->dst_port = be16_to_cpu(csk->dst_port); + if (test_bit(SK_TCP_NO_DELAY_ACK, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK; + if (test_bit(SK_TCP_KEEP_ALIVE, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_KEEP_ALIVE; + if (test_bit(SK_TCP_NAGLE, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE; + if (test_bit(SK_TCP_TIMESTAMP, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_TIME_STAMP; + if (test_bit(SK_TCP_SACK, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_SACK; + if (test_bit(SK_TCP_SEG_SCALING, &csk->tcp_flags)) + tcp_flags |= L4_KWQ_CONNECT_REQ1_SEG_SCALING; + + l4kwqe1->tcp_flags = tcp_flags; + + l4kwqe3->op_code = L4_KWQE_OPCODE_VALUE_CONNECT3; + l4kwqe3->flags = + L4_LAYER_CODE << L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT; + l4kwqe3->ka_timeout = csk->ka_timeout; + l4kwqe3->ka_interval = csk->ka_interval; + l4kwqe3->ka_max_probe_count = csk->ka_max_probe_count; + l4kwqe3->tos = csk->tos; + l4kwqe3->ttl = csk->ttl; + l4kwqe3->snd_seq_scale = csk->snd_seq_scale; + l4kwqe3->pmtu = dst_mtu(csk->dst); + l4kwqe3->mss = l4kwqe3->pmtu - 40; + l4kwqe3->rcv_buf = csk->rcv_buf; + l4kwqe3->snd_buf = csk->snd_buf; + l4kwqe3->seed = csk->seed; + + return (dev->submit_kwqes(dev, wqes, 2)); +} + +static int cnic_cm_close_req(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_close_req *l4kwqe; + struct kwqe *wqes[1]; + + l4kwqe = (struct l4_kwq_close_req *) &csk->kwqe2; + memset(l4kwqe, 0, sizeof(*l4kwqe)); + wqes[0] = (struct kwqe *) l4kwqe; + + l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_CLOSE; + l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT; + l4kwqe->cid = csk->cid; + + return (dev->submit_kwqes(dev, wqes, 1)); +} + +static int cnic_cm_abort_req(struct cnic_sock *csk) +{ + struct cnic_dev *dev = csk->dev; + struct l4_kwq_reset_req *l4kwqe; + struct kwqe *wqes[1]; + + l4kwqe = (struct l4_kwq_reset_req *) &csk->kwqe2; + memset(l4kwqe, 0, sizeof(*l4kwqe)); + wqes[0] = (struct kwqe *) l4kwqe; + + l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_RESET; + l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT; + l4kwqe->cid = csk->cid; + + return (dev->submit_kwqes(dev, wqes, 1)); +} + +static int cnic_cm_create(struct cnic_dev *dev, int ulp_type, u32 cid, + u32 l5_cid, struct cnic_sock **csk, void *context) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_sock *csk1; + + if (l5_cid >= MAX_CM_SK_TBL_SZ) + return -EINVAL; + + csk1 = &cp->csk_tbl[l5_cid]; + if (test_and_set_bit(SK_F_INUSE, &csk1->flags)) + return -EINVAL; + + csk1->dev = dev; + csk1->cid = cid; + csk1->l5_cid = l5_cid; + csk1->ulp_type = ulp_type; + csk1->context = context; + + csk1->ka_timeout = DEF_KA_TIMEOUT; + csk1->ka_interval = DEF_KA_INTERVAL; + csk1->ka_max_probe_count = DEF_KA_MAX_PROBE_COUNT; + csk1->tos = DEF_TOS; + csk1->ttl = DEF_TTL; + csk1->snd_seq_scale = DEF_SND_SEQ_SCALE; + csk1->rcv_buf = DEF_RCV_BUF; + csk1->snd_buf = DEF_SND_BUF; + csk1->seed = DEF_SEED; + + *csk = csk1; + + return 0; +} + +static int cnic_cm_destroy(struct cnic_sock *csk) +{ + struct cnic_local *cp = csk->dev->cnic_priv; + + if (!test_bit(SK_F_INUSE, &csk->flags)) + return -EINVAL; + + spin_lock_bh(&cp->cm_lock); + if (csk->dst) { + if (csk->dst->neighbour) + neigh_release(csk->dst->neighbour); + dst_release(csk->dst); + csk->dst = NULL; + } + csk->flags = 0; + spin_unlock_bh(&cp->cm_lock); + return 0; +} + +static inline struct net_device *get_real_netdev(struct net_device *netdev) +{ + return netdev->priv_flags & IFF_802_1Q_VLAN ? + VLAN_DEV_INFO(netdev)->real_dev : netdev; +} + +static struct cnic_dev *cnic_cm_select_dev(struct sockaddr_in *dst_addr, + int ulp_type) +{ + u32 dst_ip = dst_addr->sin_addr.s_addr; + struct flowi fl; + struct rtable *rt; + struct net_device *netdev; + struct cnic_dev *dev; + int err, found; + + memset(&fl, 0, sizeof(fl)); + fl.nl_u.ip4_u.daddr = dst_ip; + + err = ip_route_output_key(&rt, &fl); + if (err) + return NULL; + + netdev = get_real_netdev(rt->idev->dev); + + found = 0; + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + if (netdev == dev->netdev) { + found = 1; + cnic_hold(dev); + break; + } + } + read_unlock(&cnic_dev_lock); + + ip_rt_put(rt); + + if (found) { + struct cnic_local *cp = dev->cnic_priv; + + if (rcu_dereference(cp->ulp_ops[ulp_type])) { + cnic_put(dev); + return dev; + } + cnic_put(dev); + } + + return NULL; +} + +static int cnic_cm_connect(struct cnic_sock *csk, struct cnic_sockaddr *saddr) +{ + struct cnic_dev *dev = csk->dev; + struct net_device *realdev; + u32 dst_ip = saddr->remote_addr.sin_addr.s_addr; + u32 src_ip = saddr->local_addr.sin_addr.s_addr; + struct flowi fl; + struct rtable *rt; + struct neighbour *neigh; + int err = 0, retry = 0; + + if (!test_bit(SK_F_INUSE, &csk->flags)) + return -EINVAL; + + memset(&fl, 0, sizeof(fl)); + fl.nl_u.ip4_u.daddr = dst_ip; + fl.nl_u.ip4_u.saddr = src_ip; + err = ip_route_output_key(&rt, &fl); + if (err) + return err; + + realdev = get_real_netdev(rt->idev->dev); + if (realdev != dev->netdev) + goto err; + + if (src_ip == 0) + src_ip = inet_select_addr(rt->idev->dev, dst_ip, RT_SCOPE_LINK); + + csk->dst = &rt->u.dst; + csk->src_ip = src_ip; + csk->dst_ip = dst_ip; + csk->src_port = saddr->local_addr.sin_port; + csk->dst_port = saddr->remote_addr.sin_port; + + neigh = csk->dst->neighbour; + if (!csk->dst->neighbour) + goto err; + + neigh_hold(neigh); + + if (realdev != rt->idev->dev) + csk->vlan_id = VLAN_DEV_INFO(rt->idev->dev)->vlan_id; + else + csk->vlan_id = 0; + + + if (neigh->nud_state & NUD_VALID) + err = cnic_cm_offload_pg(csk); + + while (!(neigh->nud_state & NUD_VALID) && (retry < 3)) { + arp_send(ARPOP_REQUEST, ETH_P_ARP, rt->rt_gateway, + rt->idev->dev, rt->rt_src, NULL, + rt->idev->dev->dev_addr, NULL); + msleep(1000); + retry++; + } + if (!(neigh->nud_state & NUD_VALID)) + err = -ENODATA; + + if (!err) + return 0; + + neigh_release(neigh); + +err: + csk->dst = NULL; + + ip_rt_put(rt); + return err; +} + +static int cnic_cm_abort(struct cnic_sock *csk) +{ + if (!test_bit(SK_F_INUSE, &csk->flags)) + return -EINVAL; + + return (cnic_cm_abort_req(csk)); +} + +static int cnic_cm_close(struct cnic_sock *csk) +{ + if (!test_bit(SK_F_INUSE, &csk->flags)) + return -EINVAL; + + return (cnic_cm_close_req(csk)); +} + +static void cnic_cm_process_neigh(struct cnic_dev *dev, struct neighbour *neigh) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) { + struct cnic_sock *csk; + + csk = &cp->csk_tbl[i]; + spin_lock_bh(&cp->cm_lock); + if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst && + csk->dst->neighbour == neigh) { + if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags)) + cnic_cm_update_pg(csk); + else + cnic_cm_offload_pg(csk); + } + spin_unlock_bh(&cp->cm_lock); + } + neigh_release(neigh); +} + +static void cnic_cm_process_redirect(struct cnic_dev *dev, + struct cnic_redirect_entry *redir) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) { + struct cnic_sock *csk; + + spin_lock_bh(&cp->cm_lock); + csk = &cp->csk_tbl[i]; + if (test_bit(SK_F_INUSE, &csk->flags) && + csk->dst == redir->old_dst) { + csk->dst = redir->new_dst; + dst_hold(csk->dst); + neigh_hold(csk->dst->neighbour); + if (redir->old_dst->neighbour); + neigh_release(redir->old_dst->neighbour); + dst_release(redir->old_dst); + if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags)) + cnic_cm_update_pg(csk); + else + cnic_cm_offload_pg(csk); + } + spin_unlock_bh(&cp->cm_lock); + } + + dst_release(redir->new_dst); +} + +static void cnic_cm_upcall(struct cnic_local *cp, struct cnic_sock *csk, + u8 opcode) +{ + struct cnic_ulp_ops *ulp_ops; + int ulp_type = csk->ulp_type; + + rcu_read_lock(); + ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]); + if (ulp_ops) { + if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE) + ulp_ops->cm_connect_complete(csk); + else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE) + ulp_ops->cm_close_complete(csk); + else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED) + ulp_ops->cm_remote_abort(csk); + else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP) + ulp_ops->cm_abort_complete(csk); + else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED) + ulp_ops->cm_remote_close(csk); + } + rcu_read_unlock(); +} + +static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) +{ + struct cnic_local *cp = dev->cnic_priv; + struct l4_kcq *l4kcqe = (struct l4_kcq *) kcqe; + u8 opcode = l4kcqe->op_code; + + if (opcode == L4_KCQE_OPCODE_VALUE_OFFLOAD_PG) { + u32 l5_cid = l4kcqe->pg_host_opaque; + struct cnic_sock *csk = &cp->csk_tbl[l5_cid]; + + if (!test_bit(SK_F_INUSE, &csk->flags)) + return; + + csk->pg_cid = l4kcqe->pg_cid; + set_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags); + if (cnic_ok_to_connect(csk)) { + set_bit(SK_F_OFFLD_PENDING, &csk->flags); + cnic_cm_conn_req(csk); + } + + } else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_RESP) { + + } else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE) { + u32 l5_cid = l4kcqe->conn_id; + struct cnic_sock *csk = &cp->csk_tbl[l5_cid]; + + if (test_bit(SK_F_INUSE, &csk->flags)) { + if (l4kcqe->status == 0) + set_bit(SK_F_OFFLD_COMPLETE, &csk->flags); + clear_bit(SK_F_OFFLD_PENDING, &csk->flags); + cnic_cm_upcall(cp, csk, opcode); + } + + } else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE || + opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED || + opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP) { + u32 l5_cid = l4kcqe->conn_id; + struct cnic_sock *csk = &cp->csk_tbl[l5_cid]; + + if (test_bit(SK_F_INUSE, &csk->flags)) { + clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags); + clear_bit(SK_F_OFFLD_PENDING, &csk->flags); + cnic_cm_upload_pg(csk); + clear_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags); + cnic_cm_upcall(cp, csk, opcode); + } + } else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED) { + u32 l5_cid = l4kcqe->conn_id; + struct cnic_sock *csk = &cp->csk_tbl[l5_cid]; + + if (test_bit(SK_F_INUSE, &csk->flags)) + cnic_cm_upcall(cp, csk, opcode); + } +} + +static void cnic_cm_indicate_kcqe(void *data, struct kcqe *kcqe[], u32 num_cqe) +{ + struct cnic_dev *dev = data; + int i; + struct cnic_local *cp = dev->cnic_priv; + + for (i = 0; i < num_cqe; i++) + cnic_queue_work(cp, WORK_TYPE_KCQE, kcqe[i]); + + schedule_work(&cp->cnic_task); +} + +static void cnic_cm_indicate_event(void *data, unsigned long event) +{ +} + +static void cnic_cm_dummy(void *data) +{ +} + +static struct cnic_ulp_ops cm_ulp_ops = { + .cnic_start = cnic_cm_dummy, + .cnic_stop = cnic_cm_dummy, + .indicate_kcqes = cnic_cm_indicate_kcqe, + .indicate_netevent = cnic_cm_indicate_event, + .indicate_inetevent = cnic_cm_indicate_event, +}; + +static void cnic_task(struct work_struct *work) +{ + struct cnic_local *cp = + container_of(work, struct cnic_local, cnic_task); + struct cnic_dev *dev = cp->dev; + u32 cons = cp->cnic_wr_cons; + u32 prod = cp->cnic_wr_prod; + + while (cons != prod) { + struct cnic_work_node *node; + + node = &cp->cnic_work_ring[cons]; + if (node->work_type == WORK_TYPE_KCQE) + cnic_cm_process_kcqe(dev, &node->work_data.kcqe); + else if (node->work_type == WORK_TYPE_NEIGH_UPDATE) + cnic_cm_process_neigh(dev, node->work_data.neigh); + else if (node->work_type == WORK_TYPE_REDIRECT) + cnic_cm_process_redirect(dev, + &node->work_data.cnic_redir); + cons++; + cons &= WORK_RING_SIZE_MASK; + } + cp->cnic_wr_cons = cons; +} + +static void cnic_free_dev(struct cnic_dev *dev) +{ + cnic_free_resc(dev); + cnic_unregister_sysfs(dev); + pci_dev_put(dev->pcidev); + dev_put(dev->netdev); + kfree(dev); +} + +static void cnic_cm_free_mem(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + + kfree(cp->csk_tbl); + cp->csk_tbl = NULL; +} + +static int cnic_cm_alloc_mem(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + + cp->csk_tbl = kmalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ, + GFP_KERNEL); + if (!cp->csk_tbl) + return -ENOMEM; + memset(cp->csk_tbl, 0, sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ); + return 0; +} + +static int cnic_cm_open(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + u32 seed; + int err; + + get_random_bytes(&seed, 4); + bnx2_ctx_wr(dev->bp, 45, 0, seed); + + err = cnic_cm_alloc_mem(dev); + if (err) { + cnic_cm_free_mem(dev); + return err; + } + + spin_lock_init(&cp->cm_lock); + + INIT_WORK(&cp->cnic_task, cnic_task); + + cp->cm_nb.notifier_call = cnic_net_callback; + register_netevent_notifier(&cp->cm_nb); + + dev->cm_create = cnic_cm_create; + dev->cm_destroy = cnic_cm_destroy; + dev->cm_connect = cnic_cm_connect; + dev->cm_abort = cnic_cm_abort; + dev->cm_close = cnic_cm_close; + dev->cm_select_dev = cnic_cm_select_dev; + + cp->ulp_handle[CNIC_ULP_L4] = dev; + rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], &cm_ulp_ops); + return 0; +} + +static void cnic_cm_cleanup(struct cnic_sock *csk) +{ + clear_bit(SK_F_INUSE, &csk->flags); + if (csk->dst) { + if (csk->dst->neighbour) + neigh_release(csk->dst->neighbour); + dst_release(csk->dst); + csk->dst = NULL; + } +} + +static int cnic_cm_shutdown(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + int i; + + unregister_netevent_notifier(&cp->cm_nb); + + cancel_work_sync(&cp->cnic_task); + + if (!cp->csk_tbl) + return 0; + + for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) { + struct cnic_sock *csk = &cp->csk_tbl[i]; + + cnic_cm_cleanup(csk); + } + cnic_cm_free_mem(dev); + + return 0; +} + +static void cnic_init_context(struct cnic_dev *dev, u32 cid) +{ + u32 cid_addr; + int i; + + cid_addr = GET_CID_ADDR(cid); + + for (i = 0; i < CTX_SIZE; i += 4) + bnx2_ctx_wr(dev->bp, cid_addr, i, 0); +} + +static int cnic_start_hw(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + u32 val; + int err; + + if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) + return -EALREADY; + + val = REG_RD(dev, BNX2_MQ_CONFIG); + val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE; + if (BCM_PAGE_BITS > 12) + val |= (12 - 8) << 4; + else + val |= (BCM_PAGE_BITS - 8) << 4; + + REG_WR(dev, BNX2_MQ_CONFIG, val); + + REG_WR(dev, BNX2_HC_COMP_PROD_TRIP, (2 << 16) | 8); + REG_WR(dev, BNX2_HC_COM_TICKS, (64 << 16) | 220); + REG_WR(dev, BNX2_HC_CMD_TICKS, (64 << 16) | 220); + + cnic_init_context(dev, KWQ_CID); + cnic_init_context(dev, KCQ_CID); + + cp->kwq_cid_addr = GET_CID_ADDR(KWQ_CID); + cp->kwq_io_addr = MB_GET_CID_ADDR(KWQ_CID) + L5_KRNLQ_HOST_QIDX; + + cnic_setup_page_tbl(cp->kwq_pgtbl, KWQ_PAGE_CNT, cp->kwq_mapping); + + cp->kwq_prod_idx = 0; + cp->kwq_con_idx = 0; + + /* Initialize the kernel work queue context. */ + val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE | + (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; + bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_TYPE, val); + + val = (PAGE_SIZE / sizeof(struct kwqe) - 1) << 16; + bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val); + + val = ((PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT; + bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val); + + val = (u32) ((u64) cp->kwq_pgtbl_mapping >> 32); + bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val); + + val = (u32) cp->kwq_pgtbl_mapping; + bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val); + + cp->kcq_cid_addr = GET_CID_ADDR(KCQ_CID); + cp->kcq_io_addr = MB_GET_CID_ADDR(KCQ_CID) + L5_KRNLQ_HOST_QIDX; + + cnic_setup_page_tbl(cp->kcq_pgtbl, KCQ_PAGE_CNT, cp->kcq_mapping); + + cp->kcq_prod_idx = 0; + + /* Initialize the kernel complete queue context. */ + val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE | + (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; + bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_TYPE, val); + + val = (BCM_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16; + bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val); + + val = ((BCM_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT; + bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val); + + val = (u32) ((u64) cp->kcq_pgtbl_mapping >> 32); + bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val); + + val = (u32) cp->kcq_pgtbl_mapping; + bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val); + + /* Enable Commnad Scheduler notification when we write to the + * host producer index of the kernel contexts. */ + REG_WR(dev, BNX2_MQ_KNL_CMD_MASK1, 2); + + /* Enable Command Scheduler notification when we write to either + * the Send Queue or Receive Queue producer indexes of the kernel + * bypass contexts. */ + REG_WR(dev, BNX2_MQ_KNL_BYP_CMD_MASK1, 7); + REG_WR(dev, BNX2_MQ_KNL_BYP_WRITE_MASK1, 7); + + /* Notify COM when the driver post an application buffer. */ + REG_WR(dev, BNX2_MQ_KNL_RX_V2P_MASK2, 0x2000); + + /* Set the CP and COM doorbells. These two processors polls the + * doorbell for a non zero value before running. This must be done + * after setting up the kernel queue contexts. */ + bnx2_reg_wr_ind(dev->bp, BNX2_CP_SCRATCH + 0x20, 1); + bnx2_reg_wr_ind(dev->bp, BNX2_COM_SCRATCH + 0x20, 1); + + err = bnx2_register_cnic(dev->netdev, &my_cnic_ops, dev); + if (err) { + printk(KERN_ERR PFX "%s: bnx2_register_cnic failed\n", + dev->netdev->name); + return -EBUSY; + } + + set_bit(CNIC_F_CNIC_UP, &dev->flags); + cnic_cm_open(dev); + + return 0; +} + +static void cnic_stop_hw(struct cnic_dev *dev) +{ + if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) { + struct cnic_local *cp = dev->cnic_priv; + + cnic_cm_shutdown(dev); + bnx2_unregister_cnic(dev->netdev); + rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], NULL); + clear_bit(CNIC_F_CNIC_UP, &dev->flags); + synchronize_rcu(); + + bnx2_reg_wr_ind(dev->bp, BNX2_CP_SCRATCH + 0x20, 0); + bnx2_reg_wr_ind(dev->bp, BNX2_COM_SCRATCH + 0x20, 0); + + cnic_init_context(dev, KWQ_CID); + cnic_init_context(dev, KCQ_CID); + } +} + +static struct cnic_dev *init_cnic(struct net_device *dev) +{ + struct cnic_dev *cdev; + struct cnic_local *bp; + struct bnx2 *bnx2_bp = netdev_priv(dev); + int alloc_size; + + alloc_size = sizeof(struct cnic_dev) + sizeof(struct cnic_local); + + cdev = kmalloc(alloc_size , GFP_KERNEL); + if (cdev == NULL) { + printk(KERN_ERR PFX "%s: allocate dev struct failure\n", + dev->name); + return cdev; + } + memset(cdev, 0, alloc_size); + + cdev->netdev = dev; + cdev->bp = bnx2_bp; + cdev->pcidev = bnx2_bp->pdev; + cdev->regview = bnx2_bp->regview; + cdev->cnic_priv = (char *)cdev + sizeof(struct cnic_dev); + cdev->submit_kwqes = cnic_submit_kernel_wqes; + cdev->register_device = cnic_register_device; + cdev->unregister_device = cnic_unregister_device; + + if (cnic_alloc_resc(cdev)) { + printk(KERN_ERR PFX "%s: allocate resource failure\n", + dev->name); + kfree(cdev); + return NULL; + } + bp = cdev->cnic_priv; + bp->dev = cdev; + spin_lock_init(&bp->cnic_ulp_lock); + dev_hold(cdev->netdev); + pci_dev_get(cdev->pcidev); + + return cdev; +} + +static struct cnic_dev *is_cnic_dev(struct net_device *dev) +{ + struct ethtool_drvinfo drvinfo; + struct cnic_dev *cdev = NULL; + + if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) { + memset(&drvinfo, 0, sizeof(drvinfo)); + dev->ethtool_ops->get_drvinfo(dev, &drvinfo); + + if (!strcmp(drvinfo.driver, "bnx2")) { + cdev = init_cnic(dev); + if (cdev) { + cnic_register_sysfs(cdev); + write_lock(&cnic_dev_lock); + list_add(&cdev->list, &cnic_dev_list); + write_unlock(&cnic_dev_lock); + } + } + } + return cdev; +} + +/** + * IP event handler + */ +static int cnic_ip_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; + struct net_device *netdev = (struct net_device *) ifa->ifa_dev->dev; + struct cnic_dev *dev; + int if_type; + u32 my_dev = 0; + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + if (netdev == dev->netdev) { + my_dev = 1; + cnic_hold(dev); + break; + } + } + read_unlock(&cnic_dev_lock); + + if (my_dev) { + struct cnic_local *cp = dev->cnic_priv; + + rcu_read_lock(); + for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { + struct cnic_ulp_ops *ulp_ops; + + ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); + if (ulp_ops) { + void *ctx = cp->ulp_handle[if_type]; + + ulp_ops->indicate_inetevent(ctx, event); + } + } + rcu_read_unlock(); + + cnic_put(dev); + } + + return NOTIFY_DONE; +} + +/** + * netdev event handler + */ +static int cnic_netdev_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct net_device *netdev = ptr; + struct cnic_dev *dev; + int if_type; + u32 my_dev = 0; + + read_lock(&cnic_dev_lock); + list_for_each_entry(dev, &cnic_dev_list, list) { + if (netdev == dev->netdev) { + my_dev = 1; + cnic_hold(dev); + break; + } + } + read_unlock(&cnic_dev_lock); + + if (!my_dev && event == NETDEV_REGISTER) { + /* Check for the hot-plug device */ + dev = is_cnic_dev(netdev); + if (dev) { + my_dev = 1; + cnic_hold(dev); + } + } + if (my_dev) { + struct cnic_local *cp = dev->cnic_priv; + + if (event == NETDEV_REGISTER) + cnic_ulp_init(dev); + else if (event == NETDEV_UNREGISTER) + cnic_ulp_exit(dev); + else if (event == NETDEV_UP) { + mutex_lock(&cnic_lock); + set_bit(CNIC_F_IF_UP, &dev->flags); + if (dev->use_count) { + if (!cnic_start_hw(dev)) + cnic_ulp_start(dev); + } + mutex_unlock(&cnic_lock); + } + + rcu_read_lock(); + for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { + struct cnic_ulp_ops *ulp_ops; + void *ctx; + + ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); + if (!ulp_ops) + continue; + + ctx = cp->ulp_handle[if_type]; + + ulp_ops->indicate_netevent(ctx, event); + } + rcu_read_unlock(); + + if (event == NETDEV_GOING_DOWN) { + mutex_lock(&cnic_lock); + clear_bit(CNIC_F_IF_UP, &dev->flags); + cnic_ulp_stop(dev); + cnic_stop_hw(dev); + mutex_unlock(&cnic_lock); + } else if (event == NETDEV_UNREGISTER) { + int i = 0; + + write_lock(&cnic_dev_lock); + list_del_init(&dev->list); + write_unlock(&cnic_dev_lock); + while ((atomic_read(&dev->ref_count) != 1) && + i < 10) { + msleep(100); + i++; + } + if (atomic_read(&dev->ref_count) != 1) + printk(KERN_ERR PFX "%s: Failed waiting" + " for ref count to go zero\n", + dev->netdev->name); + cnic_free_dev(dev); + goto done; + } + cnic_put(dev); + } +done: + return NOTIFY_DONE; +} + +static struct notifier_block cnic_ip_notifier = { + cnic_ip_event, + 0 +}; + +static struct notifier_block cnic_netdev_notifier = { + cnic_netdev_event, + 0 +}; + +static void cnic_release(void) +{ + struct cnic_dev *dev; + + while (!list_empty(&cnic_dev_list)) { + dev = list_entry(cnic_dev_list.next, struct cnic_dev, list); + if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) + cnic_stop_hw(dev); + + list_del_init(&dev->list); + cnic_free_dev(dev); + } +} + +static int __init cnic_init(void) +{ + int rc = 0; + struct net_device *dev; + + printk(KERN_INFO "%s", version); + + cnic_sysfs_setup(); + + read_lock(&dev_base_lock); + /* Find Teton devices */ + for_each_netdev(dev) + is_cnic_dev(dev); + + read_unlock(&dev_base_lock); + + rc = register_inetaddr_notifier(&cnic_ip_notifier); + if (rc) + cnic_release(); + rc = register_netdevice_notifier(&cnic_netdev_notifier); + if (rc) { + unregister_inetaddr_notifier(&cnic_ip_notifier); + cnic_release(); + } + return rc; +} + +static void __exit cnic_exit(void) +{ + unregister_inetaddr_notifier(&cnic_ip_notifier); + unregister_netdevice_notifier(&cnic_netdev_notifier); + cnic_release(); + cnic_sysfs_cleanup(); + + return; +} + +module_init(cnic_init); +module_exit(cnic_exit); diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h new file mode 100644 index 0000000..6ba1f4e --- /dev/null +++ b/drivers/net/cnic.h @@ -0,0 +1,163 @@ +/* cnic.h: Broadcom CNIC core network driver. + * + * Copyright (c) 2006 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: John(Zongxi) Chen (zongxic@...adcom.com) + */ + + +#ifndef CNIC_H +#define CNIC_H + +#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) + #error "Missing either LITTLE_ENDIAN or BIG_ENDIAN definition." +#endif + +#define KWQ_PAGE_CNT 4 +#define KCQ_PAGE_CNT 16 + +#define KWQ_CID 24 +#define KCQ_CID 25 + +/* + * krnlq_context definition + */ +#define L5_KRNLQ_FLAGS 0x00000000 +#define L5_KRNLQ_SIZE 0x00000000 +#define L5_KRNLQ_TYPE 0x00000000 +#define KRNLQ_FLAGS_PG_SZ (0xf<<0) +#define KRNLQ_FLAGS_PG_SZ_256 (0<<0) +#define KRNLQ_FLAGS_PG_SZ_512 (1<<0) +#define KRNLQ_FLAGS_PG_SZ_1K (2<<0) +#define KRNLQ_FLAGS_PG_SZ_2K (3<<0) +#define KRNLQ_FLAGS_PG_SZ_4K (4<<0) +#define KRNLQ_FLAGS_PG_SZ_8K (5<<0) +#define KRNLQ_FLAGS_PG_SZ_16K (6<<0) +#define KRNLQ_FLAGS_PG_SZ_32K (7<<0) +#define KRNLQ_FLAGS_PG_SZ_64K (8<<0) +#define KRNLQ_FLAGS_PG_SZ_128K (9<<0) +#define KRNLQ_FLAGS_PG_SZ_256K (10<<0) +#define KRNLQ_FLAGS_PG_SZ_512K (11<<0) +#define KRNLQ_FLAGS_PG_SZ_1M (12<<0) +#define KRNLQ_FLAGS_PG_SZ_2M (13<<0) +#define KRNLQ_FLAGS_QE_SELF_SEQ (1<<15) +#define KRNLQ_SIZE_TYPE_SIZE ((((0x28 + 0x1f) & ~0x1f) / 0x20) << 16) +#define KRNLQ_TYPE_TYPE (0xf<<28) +#define KRNLQ_TYPE_TYPE_EMPTY (0<<28) +#define KRNLQ_TYPE_TYPE_KRNLQ (6<<28) + +#define L5_KRNLQ_HOST_QIDX 0x00000004 +#define L5_KRNLQ_HOST_FW_QIDX 0x00000008 +#define L5_KRNLQ_NX_QE_SELF_SEQ 0x0000000c +#define L5_KRNLQ_QE_SELF_SEQ_MAX 0x0000000c +#define L5_KRNLQ_NX_QE_HADDR_HI 0x00000010 +#define L5_KRNLQ_NX_QE_HADDR_LO 0x00000014 +#define L5_KRNLQ_PGTBL_PGIDX 0x00000018 +#define L5_KRNLQ_NX_PG_QIDX 0x00000018 +#define L5_KRNLQ_PGTBL_NPAGES 0x0000001c +#define L5_KRNLQ_QIDX_INCR 0x0000001c +#define L5_KRNLQ_PGTBL_HADDR_HI 0x00000020 +#define L5_KRNLQ_PGTBL_HADDR_LO 0x00000024 + +struct cnic_redirect_entry { + struct dst_entry *old_dst; + struct dst_entry *new_dst; +}; + +struct cnic_work_node { + u32 work_type; +#define WORK_TYPE_KCQE 1 +#define WORK_TYPE_NEIGH_UPDATE 2 +#define WORK_TYPE_REDIRECT 3 + union { + struct kcqe kcqe; + struct neighbour *neigh; + struct cnic_redirect_entry cnic_redir; + } work_data; +}; + +#define WORK_RING_SIZE 128 +#define WORK_RING_SIZE_MASK 127 +#define MAX_CM_SK_TBL_SZ 128 +#define MAX_COMPLETED_KCQE 64 + +#define KWQE_CNT (BCM_PAGE_SIZE / sizeof(struct kwqe)) +#define KCQE_CNT (BCM_PAGE_SIZE / sizeof(struct kcqe)) +#define MAX_KWQE_CNT (KWQE_CNT - 1) +#define MAX_KCQE_CNT (KCQE_CNT - 1) + +#define MAX_KWQ_IDX ((KWQ_PAGE_CNT * KWQE_CNT) - 1) +#define MAX_KCQ_IDX ((KCQ_PAGE_CNT * KCQE_CNT) - 1) + +#define KWQ_PG(x) (((x) & ~MAX_KWQE_CNT) >> (BCM_PAGE_BITS - 5)) +#define KWQ_IDX(x) ((x) & MAX_KWQE_CNT) + +#define KCQ_PG(x) (((x) & ~MAX_KCQE_CNT) >> (BCM_PAGE_BITS - 5)) +#define KCQ_IDX(x) ((x) & MAX_KCQE_CNT) + +#define DEF_IPID_COUNT 0xc001 + +#define DEF_KA_TIMEOUT 10000 +#define DEF_KA_INTERVAL 300000 +#define DEF_KA_MAX_PROBE_COUNT 3 +#define DEF_TOS 0 +#define DEF_TTL 0xfe +#define DEF_SND_SEQ_SCALE 0 +#define DEF_RCV_BUF 0xffff +#define DEF_SND_BUF 0xffff +#define DEF_SEED 0 + +struct cnic_local { + + spinlock_t cnic_ulp_lock; + void *ulp_handle[MAX_CNIC_ULP_TYPE]; + unsigned long ulp_flags[MAX_CNIC_ULP_TYPE]; +#define ULP_F_INIT 0 +#define ULP_F_START 1 + struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE]; + + struct cnic_dev *dev; + + u32 kwq_cid_addr; + u32 kcq_cid_addr; + + struct kwqe *kwq[KWQ_PAGE_CNT]; + dma_addr_t kwq_mapping[KWQ_PAGE_CNT]; + u16 kwq_prod_idx; + u32 kwq_io_addr; + + u16 kwq_con_idx; + + void *kwq_pgtbl; + dma_addr_t kwq_pgtbl_mapping; + int kwq_pgtbl_size; + + struct kcqe *kcq[KCQ_PAGE_CNT]; + dma_addr_t kcq_mapping[KCQ_PAGE_CNT]; + u16 kcq_prod_idx; + u32 kcq_io_addr; + + void *kcq_pgtbl; + dma_addr_t kcq_pgtbl_mapping; + int kcq_pgtbl_size; + + struct kcqe *completed_kcq[MAX_COMPLETED_KCQE]; + + struct cnic_sock *csk_tbl; + spinlock_t cm_lock; + + struct notifier_block cm_nb; + + struct cnic_work_node cnic_work_ring[WORK_RING_SIZE]; + int cnic_wr_cons; + int cnic_wr_prod; + + struct work_struct cnic_task; + +}; + +#endif diff --git a/drivers/net/cnic_cm.h b/drivers/net/cnic_cm.h new file mode 100644 index 0000000..d6ea01d --- /dev/null +++ b/drivers/net/cnic_cm.h @@ -0,0 +1,555 @@ +#ifndef __57XX_L5CM_HSI_LINUX_LE__ +#define __57XX_L5CM_HSI_LINUX_LE__ + +/* KWQ (kernel work queue) request op codes */ +#define L4_KWQE_OPCODE_VALUE_CONNECT1 (50) +#define L4_KWQE_OPCODE_VALUE_CONNECT2 (51) +#define L4_KWQE_OPCODE_VALUE_CONNECT3 (52) +#define L4_KWQE_OPCODE_VALUE_RESET (53) +#define L4_KWQE_OPCODE_VALUE_CLOSE (54) +#define L4_KWQE_OPCODE_VALUE_UPDATE_SECRET (60) +#define L4_KWQE_OPCODE_VALUE_INIT_ULP (61) + +#define L4_KWQE_OPCODE_VALUE_OFFLOAD_PG (1) +#define L4_KWQE_OPCODE_VALUE_UPDATE_PG (9) +#define L4_KWQE_OPCODE_VALUE_UPLOAD_PG (14) + +/* KCQ (kernel completion queue) response op codes */ +#define L4_KCQE_OPCODE_VALUE_CONNECT_RESP (52) +#define L4_KCQE_OPCODE_VALUE_CLOSE_COMP (53) +#define L4_KCQE_OPCODE_VALUE_RESET_COMP (54) +#define L4_KCQE_OPCODE_VALUE_FW_TCP_UPDATE (55) +#define L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE (56) +#define L4_KCQE_OPCODE_VALUE_RESET_RECEIVED (57) +#define L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED (58) +#define L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE (59) +#define L4_KCQE_OPCODE_VALUE_CONNECT_RESET (60) +#define L4_KCQE_OPCODE_VALUE_INIT_ULP (61) + +#define L4_KCQE_OPCODE_VALUE_OFFLOAD_PG (1) +#define L4_KCQE_OPCODE_VALUE_UPLOAD_PG (14) + +/* KCQ (kernel completion queue) completion status */ +#define L4_KCQE_COMPLETION_STATUS_SUCCESS (0) +#define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93) + +#define L4_LAYER_CODE (4) + +/* + * L4 KCQ CQE + */ +struct l4_kcq { + u32 cid; + u32 pg_cid; + u32 conn_id; + u32 pg_host_opaque; +#if defined(__BIG_ENDIAN) + u16 status; + u16 reserved1; +#elif defined(__LITTLE_ENDIAN) + u16 reserved1; + u16 status; +#endif + u32 reserved2[2]; +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KCQ_RESERVED3 (0xF<<0) +#define L4_KCQ_RESERVED3_SHIFT 0 +#define L4_KCQ_LAYER_CODE (0x7<<4) +#define L4_KCQ_LAYER_CODE_SHIFT 4 +#define L4_KCQ_RESERVED4 (0x1<<7) +#define L4_KCQ_RESERVED4_SHIFT 7 + u8 op_code; + u16 qe_self_seq; +#elif defined(__LITTLE_ENDIAN) + u16 qe_self_seq; + u8 op_code; + u8 flags; +#define L4_KCQ_RESERVED3 (0xF<<0) +#define L4_KCQ_RESERVED3_SHIFT 0 +#define L4_KCQ_LAYER_CODE (0x7<<4) +#define L4_KCQ_LAYER_CODE_SHIFT 4 +#define L4_KCQ_RESERVED4 (0x1<<7) +#define L4_KCQ_RESERVED4_SHIFT 7 +#endif +}; + + +/* + * L4 KCQ CQE PG upload + */ +struct l4_kcq_upload_pg { + u32 pg_cid; +#if defined(__BIG_ENDIAN) + u16 pg_status; + u16 pg_ipid_count; +#elif defined(__LITTLE_ENDIAN) + u16 pg_ipid_count; + u16 pg_status; +#endif + u32 reserved1[5]; +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0) +#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0 +#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4) +#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4 +#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7) +#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7 + u8 op_code; + u16 qe_self_seq; +#elif defined(__LITTLE_ENDIAN) + u16 qe_self_seq; + u8 op_code; + u8 flags; +#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0) +#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0 +#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4) +#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4 +#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7) +#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7 +#endif +}; + + +/* + * Gracefully close the connection request + */ +struct l4_kwq_close_req { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0) +#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0 +#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4) +#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0) +#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0 +#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4) +#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 cid; + u32 reserved2[6]; +}; + + +/* + * The first request to be passed in order to establish connection in option2 + */ +struct l4_kwq_connect_req1 { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u8 reserved0; + u8 conn_flags; +#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0) +#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1) +#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1 +#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2) +#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2 +#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3) +#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3 +#elif defined(__LITTLE_ENDIAN) + u8 conn_flags; +#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0) +#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1) +#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1 +#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2) +#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2 +#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3) +#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3 + u8 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 cid; + u32 pg_cid; + u32 src_ip; + u32 dst_ip; +#if defined(__BIG_ENDIAN) + u16 dst_port; + u16 src_port; +#elif defined(__LITTLE_ENDIAN) + u16 src_port; + u16 dst_port; +#endif +#if defined(__BIG_ENDIAN) + u8 rsrv1[3]; + u8 tcp_flags; +#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0) +#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1) +#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1 +#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2) +#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2 +#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3) +#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3 +#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4) +#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4 +#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5) +#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5 +#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6) +#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6 +#elif defined(__LITTLE_ENDIAN) + u8 tcp_flags; +#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0) +#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0 +#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1) +#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1 +#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2) +#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2 +#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3) +#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3 +#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4) +#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4 +#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5) +#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5 +#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6) +#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6 + u8 rsrv1[3]; +#endif + u32 rsrv2; +}; + + +/* + * The second ( optional )request to be passed in order to establish connection in option2 - for IPv6 only + */ +struct l4_kwq_connect_req2 { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u8 reserved0; + u8 rsrv; +#elif defined(__LITTLE_ENDIAN) + u8 rsrv; + u8 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 reserved2; + u32 src_ip_v6_2; + u32 src_ip_v6_3; + u32 src_ip_v6_4; + u32 dst_ip_v6_2; + u32 dst_ip_v6_3; + u32 dst_ip_v6_4; +}; + + +/* + * The third ( and last )request to be passed in order to establish connection in option2 + */ +struct l4_kwq_connect_req3 { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0) +#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0 +#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4) +#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4 +#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 ka_timeout; + u32 ka_interval ; +#if defined(__BIG_ENDIAN) + u8 snd_seq_scale; + u8 ttl; + u8 tos; + u8 ka_max_probe_count; +#elif defined(__LITTLE_ENDIAN) + u8 ka_max_probe_count; + u8 tos; + u8 ttl; + u8 snd_seq_scale; +#endif +#if defined(__BIG_ENDIAN) + u16 pmtu; + u16 mss; +#elif defined(__LITTLE_ENDIAN) + u16 mss; + u16 pmtu; +#endif + u32 rcv_buf; + u32 snd_buf; + u32 seed; +}; + + +/* + * a KWQE request to offload a PG connection + */ +struct l4_kwq_offload_pg { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0) +#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0 +#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4) +#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4 +#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0) +#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0 +#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4) +#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4 +#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7 +#endif +#if defined(__BIG_ENDIAN) + u8 l2hdr_nbytes; + u8 pg_flags; +#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0) +#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0 +#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1) +#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1 +#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2) +#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2 + u8 da0; + u8 da1; +#elif defined(__LITTLE_ENDIAN) + u8 da1; + u8 da0; + u8 pg_flags; +#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0) +#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0 +#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1) +#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1 +#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2) +#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2 + u8 l2hdr_nbytes; +#endif +#if defined(__BIG_ENDIAN) + u8 da2; + u8 da3; + u8 da4; + u8 da5; +#elif defined(__LITTLE_ENDIAN) + u8 da5; + u8 da4; + u8 da3; + u8 da2; +#endif +#if defined(__BIG_ENDIAN) + u8 sa0; + u8 sa1; + u8 sa2; + u8 sa3; +#elif defined(__LITTLE_ENDIAN) + u8 sa3; + u8 sa2; + u8 sa1; + u8 sa0; +#endif +#if defined(__BIG_ENDIAN) + u8 sa4; + u8 sa5; + u16 etype; +#elif defined(__LITTLE_ENDIAN) + u16 etype; + u8 sa5; + u8 sa4; +#endif +#if defined(__BIG_ENDIAN) + u16 vlan_tag; + u16 ipid_start; +#elif defined(__LITTLE_ENDIAN) + u16 ipid_start; + u16 vlan_tag; +#endif +#if defined(__BIG_ENDIAN) + u16 ipid_count; + u16 reserved3; +#elif defined(__LITTLE_ENDIAN) + u16 reserved3; + u16 ipid_count; +#endif + u32 host_opaque; +}; + + +/* + * Abortively close the connection request + */ +struct l4_kwq_reset_req { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0) +#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0 +#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4) +#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4 +#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7 + u8 op_code; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_code; + u8 flags; +#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0) +#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0 +#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4) +#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4 +#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 cid; + u32 reserved2[6]; +}; + + +/* + * a KWQE request to update a PG connection + */ +struct l4_kwq_update_pg { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0) +#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0 +#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4) +#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4 +#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7 + u8 opcode; + u16 oper16; +#elif defined(__LITTLE_ENDIAN) + u16 oper16; + u8 opcode; + u8 flags; +#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0) +#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0 +#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4) +#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4 +#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 pg_cid; + u32 pg_host_opaque; +#if defined(__BIG_ENDIAN) + u8 pg_valids; +#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0) +#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0 +#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1) +#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1 +#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2) +#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2 + u8 pg_unused_a; + u16 pg_ipid_count; +#elif defined(__LITTLE_ENDIAN) + u16 pg_ipid_count; + u8 pg_unused_a; + u8 pg_valids; +#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0) +#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0 +#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1) +#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1 +#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2) +#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2 +#endif +#if defined(__BIG_ENDIAN) + u16 reserverd3; + u8 da0; + u8 da1; +#elif defined(__LITTLE_ENDIAN) + u8 da1; + u8 da0; + u16 reserverd3; +#endif +#if defined(__BIG_ENDIAN) + u8 da2; + u8 da3; + u8 da4; + u8 da5; +#elif defined(__LITTLE_ENDIAN) + u8 da5; + u8 da4; + u8 da3; + u8 da2; +#endif + u32 reserved4; + u32 reserved5; +}; + + +/* + * a KWQE request to upload a PG or L4 context + */ +struct l4_kwq_upload { +#if defined(__BIG_ENDIAN) + u8 flags; +#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0) +#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0 +#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4) +#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4 +#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7 + u8 opcode; + u16 oper16; +#elif defined(__LITTLE_ENDIAN) + u16 oper16; + u8 opcode; + u8 flags; +#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0) +#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0 +#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4) +#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4 +#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7) +#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7 +#endif + u32 cid; + u32 reserved2[6]; +}; + +#endif /* __57XX_L5CM_HSI_LINUX_LE__ */ diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h new file mode 100644 index 0000000..2cce89e --- /dev/null +++ b/drivers/net/cnic_if.h @@ -0,0 +1,152 @@ +/* cnic_if.h: Broadcom CNIC core network driver. + * + * Copyright (c) 2006 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: John(Zongxi) Chen (zongxic@...adcom.com) + */ + + +#ifndef CNIC_IF_H +#define CNIC_IF_H + +#define CNIC_MODULE_VERSION "1.1.10" +#define CNIC_MODULE_RELDATE "July 30, 2007" + +#define CNIC_ULP_RDMA 0 +#define CNIC_ULP_ISCSI 1 +#define CNIC_ULP_L4 2 +#define MAX_CNIC_ULP_TYPE_EXT 2 +#define MAX_CNIC_ULP_TYPE 3 + +struct kwqe { + u32 kwqe_op_flag; + + u32 kwqe_info0; + u32 kwqe_info1; + u32 kwqe_info2; + u32 kwqe_info3; + u32 kwqe_info4; + u32 kwqe_info5; + u32 kwqe_info6; +}; + +struct kcqe { + u32 kcqe_info0; + u32 kcqe_info1; + u32 kcqe_info2; + u32 kcqe_info3; + u32 kcqe_info4; + u32 kcqe_info5; + u32 kcqe_info6; + u32 kcqe_op_flag; + #define KCQE_FLAGS_LAYER_MASK (0x7<<28) + #define KCQE_FLAGS_LAYER_MASK_MISC (0<<28) + #define KCQE_FLAGS_LAYER_MASK_L2 (2<<28) + #define KCQE_FLAGS_LAYER_MASK_L3 (3<<28) + #define KCQE_FLAGS_LAYER_MASK_L4 (4<<28) + #define KCQE_FLAGS_LAYER_MASK_L5_RDMA (5<<28) + #define KCQE_FLAGS_LAYER_MASK_L5_ISCSI (6<<28) + #define KCQE_FLAGS_NEXT (1<<31) +}; + +struct cnic_sockaddr { + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; +}; + +struct cnic_sock { + struct cnic_dev *dev; + void *context; + u32 src_ip; + u32 dst_ip; + u16 src_port; + u16 dst_port; + u16 vlan_id; + struct dst_entry *dst; + u32 cid; + u32 l5_cid; + u32 pg_cid; + int ulp_type; + + u32 ka_timeout; + u32 ka_interval; + u8 ka_max_probe_count; + u8 tos; + u8 ttl; + u8 snd_seq_scale; + u32 rcv_buf; + u32 snd_buf; + u32 seed; + + unsigned long tcp_flags; +#define SK_TCP_NO_DELAY_ACK 0 +#define SK_TCP_KEEP_ALIVE 1 +#define SK_TCP_NAGLE 2 +#define SK_TCP_TIMESTAMP 3 +#define SK_TCP_SACK 4 +#define SK_TCP_SEG_SCALING 5 + unsigned long flags; +#define SK_F_INUSE 0 +#define SK_F_OFFLD_COMPLETE 1 +#define SK_F_OFFLD_PENDING 2 +#define SK_F_PG_OFFLD_COMPLETE 3 + struct kwqe kwqe1; + struct kwqe kwqe2; + struct kwqe kwqe3; +}; + +struct cnic_dev { + struct net_device *netdev; + struct bnx2 *bp; + struct pci_dev *pcidev; + void __iomem *regview; + struct list_head list; + struct class_device class_dev; + + int (*register_device)(struct cnic_dev *dev, int ulp_type, + void *ulp_ctx); + int (*unregister_device)(struct cnic_dev *dev, int ulp_type); + int (*submit_kwqes)(struct cnic_dev *dev, struct kwqe *wqes[], + u32 num_wqes); + + int (*cm_create)(struct cnic_dev *, int, u32, u32, struct cnic_sock **, + void *); + int (*cm_destroy)(struct cnic_sock *); + int (*cm_connect)(struct cnic_sock *, struct cnic_sockaddr *); + int (*cm_abort)(struct cnic_sock *); + int (*cm_close)(struct cnic_sock *); + struct cnic_dev *(*cm_select_dev)(struct sockaddr_in *, int ulp_type); + unsigned long flags; +#define CNIC_F_IF_UP 0 +#define CNIC_F_CNIC_UP 1 + atomic_t ref_count; + int use_count; + void *cnic_priv; +}; + +struct cnic_ulp_ops { + void (*cnic_init)(struct cnic_dev *dev); + void (*cnic_exit)(struct cnic_dev *dev); + void (*cnic_start)(void *ulp_ctx); + void (*cnic_stop)(void *ulp_ctx); + void (*indicate_kcqes)(void *ulp_ctx, struct kcqe *cqes[], + u32 num_cqes); + void (*indicate_netevent)(void *ulp_ctx, unsigned long event); + void (*indicate_inetevent)(void *ulp_ctx, unsigned long event); + void (*cm_connect_complete)(struct cnic_sock *); + void (*cm_close_complete)(struct cnic_sock *); + void (*cm_abort_complete)(struct cnic_sock *); + void (*cm_remote_close)(struct cnic_sock *); + void (*cm_remote_abort)(struct cnic_sock *); + struct module *owner; +}; + +extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); + +extern int cnic_unregister_driver(int ulp_type); + +#endif - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists