diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index fa7c532012654eb05ad42fce20e92c6ab03cef32..e18a0e4d3ed17a0a7a224b60fdd03a1025e45be0 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -1386,4 +1386,16 @@ void bnx2x_schedule_sp_rtnl(struct bnx2x*, enum sp_rtnl_flag, * @state: OS_DRIVER_STATE_* value reflecting current driver state */ void bnx2x_set_os_driver_state(struct bnx2x *bp, u32 state); + +/** + * bnx2x_nvram_read - reads data from nvram [might sleep] + * + * @bp: driver handle + * @offset: byte offset in nvram + * @ret_buf: pointer to buffer where data is to be stored + * @buf_size: Length of 'ret_buf' in bytes + */ +int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf, + int buf_size); + #endif /* BNX2X_CMN_H */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 6b2050a198df8ebd43fb29ec4176424491f29ac8..6f909077b919281bfb51661f7aecea41b7b3ed2f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1348,8 +1348,8 @@ static int bnx2x_nvram_read_dword(struct bnx2x *bp, u32 offset, __be32 *ret_val, return rc; } -static int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf, - int buf_size) +int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf, + int buf_size) { int rc; u32 cmd_flags; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 08a08fa49caad3fb8850b0f92f4285c43a667835..cafd5de675cf3836bd9f1a9776c5dfc4d6d5bdbd 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -2075,6 +2075,25 @@ enum curr_cfg_method_e { CURR_CFG_MET_VENDOR_SPEC = 2,/* e.g. Option ROM, NPAR, O/S Cfg Utils */ }; +#define FC_NPIV_WWPN_SIZE 8 +#define FC_NPIV_WWNN_SIZE 8 +struct bdn_npiv_settings { + u8 npiv_wwpn[FC_NPIV_WWPN_SIZE]; + u8 npiv_wwnn[FC_NPIV_WWNN_SIZE]; +}; + +struct bdn_fc_npiv_cfg { + /* hdr used internally by the MFW */ + u32 hdr; + u32 num_of_npiv; +}; + +#define MAX_NUMBER_NPIV 64 +struct bdn_fc_npiv_tbl { + struct bdn_fc_npiv_cfg fc_npiv_cfg; + struct bdn_npiv_settings settings[MAX_NUMBER_NPIV]; +}; + struct mdump_driver_info { u32 epoc; u32 drv_ver; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 31c63aa2252166a4a9fb5d811735d764e8d1d082..ad73a60de333e720043fec43b43c37befbb2f945 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -14653,6 +14653,90 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl) rc = -EINVAL; } + /* For storage-only interfaces, change driver state */ + if (IS_MF_SD_STORAGE_PERSONALITY_ONLY(bp)) { + switch (ctl->drv_state) { + case DRV_NOP: + break; + case DRV_ACTIVE: + bnx2x_set_os_driver_state(bp, + OS_DRIVER_STATE_ACTIVE); + break; + case DRV_INACTIVE: + bnx2x_set_os_driver_state(bp, + OS_DRIVER_STATE_DISABLED); + break; + case DRV_UNLOADED: + bnx2x_set_os_driver_state(bp, + OS_DRIVER_STATE_NOT_LOADED); + break; + default: + BNX2X_ERR("Unknown cnic driver state: %d\n", ctl->drv_state); + } + } + + return rc; +} + +static int bnx2x_get_fc_npiv(struct net_device *dev, + struct cnic_fc_npiv_tbl *cnic_tbl) +{ + struct bnx2x *bp = netdev_priv(dev); + struct bdn_fc_npiv_tbl *tbl = NULL; + u32 offset, entries; + int rc = -EINVAL; + int i; + + if (!SHMEM2_HAS(bp, fc_npiv_nvram_tbl_addr[0])) + goto out; + + DP(BNX2X_MSG_MCP, "About to read the FC-NPIV table\n"); + + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (!tbl) { + BNX2X_ERR("Failed to allocate fc_npiv table\n"); + goto out; + } + + offset = SHMEM2_RD(bp, fc_npiv_nvram_tbl_addr[BP_PORT(bp)]); + DP(BNX2X_MSG_MCP, "Offset of FC-NPIV in NVRAM: %08x\n", offset); + + /* Read the table contents from nvram */ + if (bnx2x_nvram_read(bp, offset, (u8 *)tbl, sizeof(*tbl))) { + BNX2X_ERR("Failed to read FC-NPIV table\n"); + goto out; + } + + /* Since bnx2x_nvram_read() returns data in be32, we need to convert + * the number of entries back to cpu endianness. + */ + entries = tbl->fc_npiv_cfg.num_of_npiv; + entries = (__force u32)be32_to_cpu((__force __be32)entries); + tbl->fc_npiv_cfg.num_of_npiv = entries; + + if (!tbl->fc_npiv_cfg.num_of_npiv) { + DP(BNX2X_MSG_MCP, + "No FC-NPIV table [valid, simply not present]\n"); + goto out; + } else if (tbl->fc_npiv_cfg.num_of_npiv > MAX_NUMBER_NPIV) { + BNX2X_ERR("FC-NPIV table with bad length 0x%08x\n", + tbl->fc_npiv_cfg.num_of_npiv); + goto out; + } else { + DP(BNX2X_MSG_MCP, "Read 0x%08x entries from NVRAM\n", + tbl->fc_npiv_cfg.num_of_npiv); + } + + /* Copy the data into cnic-provided struct */ + cnic_tbl->count = tbl->fc_npiv_cfg.num_of_npiv; + for (i = 0; i < cnic_tbl->count; i++) { + memcpy(cnic_tbl->wwpn[i], tbl->settings[i].npiv_wwpn, 8); + memcpy(cnic_tbl->wwnn[i], tbl->settings[i].npiv_wwnn, 8); + } + + rc = 0; +out: + kfree(tbl); return rc; } @@ -14798,6 +14882,7 @@ static struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev) cp->starting_cid = bnx2x_cid_ilt_lines(bp) * ILT_PAGE_CIDS; cp->drv_submit_kwqes_16 = bnx2x_cnic_sp_queue; cp->drv_ctl = bnx2x_drv_ctl; + cp->drv_get_fc_npiv_tbl = bnx2x_get_fc_npiv; cp->drv_register_cnic = bnx2x_register_cnic; cp->drv_unregister_cnic = bnx2x_unregister_cnic; cp->fcoe_init_cid = BNX2X_FCOE_ETH_CID(bp); diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 17c145fdf3ff6f40e2ef25098e1c0b423dcf9c5f..b69dc58faeab5b74ffc3142341efea084d83706d 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -192,6 +192,7 @@ static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val) struct drv_ctl_info info; struct drv_ctl_io *io = &info.data.io; + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = DRV_CTL_CTX_WR_CMD; io->cid_addr = cid_addr; io->offset = off; @@ -206,6 +207,7 @@ static void cnic_ctx_tbl_wr(struct cnic_dev *dev, u32 off, dma_addr_t addr) struct drv_ctl_info info; struct drv_ctl_io *io = &info.data.io; + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = DRV_CTL_CTXTBL_WR_CMD; io->offset = off; io->dma_addr = addr; @@ -219,6 +221,7 @@ static void cnic_ring_ctl(struct cnic_dev *dev, u32 cid, u32 cl_id, int start) struct drv_ctl_info info; struct drv_ctl_l2_ring *ring = &info.data.ring; + memset(&info, 0, sizeof(struct drv_ctl_info)); if (start) info.cmd = DRV_CTL_START_L2_CMD; else @@ -236,6 +239,7 @@ static void cnic_reg_wr_ind(struct cnic_dev *dev, u32 off, u32 val) struct drv_ctl_info info; struct drv_ctl_io *io = &info.data.io; + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = DRV_CTL_IO_WR_CMD; io->offset = off; io->data = val; @@ -249,13 +253,14 @@ static u32 cnic_reg_rd_ind(struct cnic_dev *dev, u32 off) struct drv_ctl_info info; struct drv_ctl_io *io = &info.data.io; + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = DRV_CTL_IO_RD_CMD; io->offset = off; ethdev->drv_ctl(dev->netdev, &info); return io->data; } -static void cnic_ulp_ctl(struct cnic_dev *dev, int ulp_type, bool reg) +static void cnic_ulp_ctl(struct cnic_dev *dev, int ulp_type, bool reg, int state) { struct cnic_local *cp = dev->cnic_priv; struct cnic_eth_dev *ethdev = cp->ethdev; @@ -263,6 +268,7 @@ static void cnic_ulp_ctl(struct cnic_dev *dev, int ulp_type, bool reg) struct fcoe_capabilities *fcoe_cap = &info.data.register_data.fcoe_features; + memset(&info, 0, sizeof(struct drv_ctl_info)); if (reg) { info.cmd = DRV_CTL_ULP_REGISTER_CMD; if (ulp_type == CNIC_ULP_FCOE && dev->fcoe_cap) @@ -272,6 +278,7 @@ static void cnic_ulp_ctl(struct cnic_dev *dev, int ulp_type, bool reg) } info.data.ulp_type = ulp_type; + info.drv_state = state; ethdev->drv_ctl(dev->netdev, &info); } @@ -286,6 +293,7 @@ static void cnic_spq_completion(struct cnic_dev *dev, int cmd, u32 count) struct cnic_eth_dev *ethdev = cp->ethdev; struct drv_ctl_info info; + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = cmd; info.data.credit.credit_count = count; ethdev->drv_ctl(dev->netdev, &info); @@ -591,7 +599,7 @@ static int cnic_register_device(struct cnic_dev *dev, int ulp_type, mutex_unlock(&cnic_lock); - cnic_ulp_ctl(dev, ulp_type, true); + cnic_ulp_ctl(dev, ulp_type, true, DRV_ACTIVE); return 0; @@ -636,7 +644,10 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) if (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type])) netdev_warn(dev->netdev, "Failed waiting for ULP up call to complete\n"); - cnic_ulp_ctl(dev, ulp_type, false); + if (test_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type])) + cnic_ulp_ctl(dev, ulp_type, false, DRV_UNLOADED); + else + cnic_ulp_ctl(dev, ulp_type, false, DRV_INACTIVE); return 0; } @@ -4267,6 +4278,7 @@ static void cnic_delete_task(struct work_struct *work) cnic_ulp_stop_one(cp, CNIC_ULP_ISCSI); + memset(&info, 0, sizeof(struct drv_ctl_info)); info.cmd = DRV_CTL_ISCSI_STOPPED_CMD; cp->ethdev->drv_ctl(dev->netdev, &info); } @@ -5433,6 +5445,23 @@ static void cnic_free_dev(struct cnic_dev *dev) kfree(dev); } +static int cnic_get_fc_npiv_tbl(struct cnic_dev *dev, + struct cnic_fc_npiv_tbl *npiv_tbl) +{ + struct cnic_local *cp = dev->cnic_priv; + struct bnx2x *bp = netdev_priv(dev->netdev); + int ret; + + if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) + return -EAGAIN; /* bnx2x is down */ + + if (!BNX2X_CHIP_IS_E2_PLUS(bp)) + return -EINVAL; + + ret = cp->ethdev->drv_get_fc_npiv_tbl(dev->netdev, npiv_tbl); + return ret; +} + static struct cnic_dev *cnic_alloc_dev(struct net_device *dev, struct pci_dev *pdev) { @@ -5451,6 +5480,7 @@ static struct cnic_dev *cnic_alloc_dev(struct net_device *dev, cdev->register_device = cnic_register_device; cdev->unregister_device = cnic_unregister_device; cdev->iscsi_nl_msg_recv = cnic_iscsi_nl_msg_recv; + cdev->get_fc_npiv_tbl = cnic_get_fc_npiv_tbl; cp = cdev->cnic_priv; cp->dev = cdev; diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index ef6125b0ee3ed35fbd96dc0a2c5647fa0894e3b0..789e5c7e93116ab29d944e57296d511789bfedb0 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -15,8 +15,8 @@ #include "bnx2x/bnx2x_mfw_req.h" -#define CNIC_MODULE_VERSION "2.5.21" -#define CNIC_MODULE_RELDATE "January 29, 2015" +#define CNIC_MODULE_VERSION "2.5.22" +#define CNIC_MODULE_RELDATE "July 20, 2015" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 @@ -151,6 +151,11 @@ struct drv_ctl_register_data { struct drv_ctl_info { int cmd; + int drv_state; +#define DRV_NOP 0 +#define DRV_ACTIVE 1 +#define DRV_INACTIVE 2 +#define DRV_UNLOADED 3 union { struct drv_ctl_spq_credit credit; struct drv_ctl_io io; @@ -161,6 +166,15 @@ struct drv_ctl_info { } data; }; +#define MAX_NPIV_ENTRIES 64 +#define FC_NPIV_WWN_SIZE 8 + +struct cnic_fc_npiv_tbl { + u8 wwpn[MAX_NPIV_ENTRIES][FC_NPIV_WWN_SIZE]; + u8 wwnn[MAX_NPIV_ENTRIES][FC_NPIV_WWN_SIZE]; + u32 count; +}; + struct cnic_ops { struct module *cnic_owner; /* Calls to these functions are protected by RCU. When @@ -226,6 +240,8 @@ struct cnic_eth_dev { int (*drv_submit_kwqes_16)(struct net_device *, struct kwqe_16 *[], u32); int (*drv_ctl)(struct net_device *, struct drv_ctl_info *); + int (*drv_get_fc_npiv_tbl)(struct net_device *, + struct cnic_fc_npiv_tbl *); unsigned long reserved1[2]; union drv_info_to_mcp *addr_drv_info_to_mcp; }; @@ -314,6 +330,7 @@ struct cnic_dev { struct cnic_dev *(*cm_select_dev)(struct sockaddr_in *, int ulp_type); int (*iscsi_nl_msg_recv)(struct cnic_dev *dev, u32 msg_type, char *data, u16 data_size); + int (*get_fc_npiv_tbl)(struct cnic_dev *, struct cnic_fc_npiv_tbl *); unsigned long flags; #define CNIC_F_CNIC_UP 1 #define CNIC_F_BNX2_CLASS 3 diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 98d06d15195806cd915a7ccf23f19447e27854d3..d5cdc4776707b13ad67159c5e0822adf26f392b8 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2051,9 +2051,49 @@ static int bnx2fc_disable(struct net_device *netdev) return rc; } +static uint bnx2fc_npiv_create_vports(struct fc_lport *lport, + struct cnic_fc_npiv_tbl *npiv_tbl) +{ + struct fc_vport_identifiers vpid; + uint i, created = 0; + + if (npiv_tbl->count > MAX_NPIV_ENTRIES) { + BNX2FC_HBA_DBG(lport, "Exceeded count max of npiv table\n"); + goto done; + } + + /* Sanity check the first entry to make sure it's not 0 */ + if (wwn_to_u64(npiv_tbl->wwnn[0]) == 0 && + wwn_to_u64(npiv_tbl->wwpn[0]) == 0) { + BNX2FC_HBA_DBG(lport, "First NPIV table entries invalid.\n"); + goto done; + } + + vpid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vpid.vport_type = FC_PORTTYPE_NPIV; + vpid.disable = false; + + for (i = 0; i < npiv_tbl->count; i++) { + vpid.node_name = wwn_to_u64(npiv_tbl->wwnn[i]); + vpid.port_name = wwn_to_u64(npiv_tbl->wwpn[i]); + scnprintf(vpid.symbolic_name, sizeof(vpid.symbolic_name), + "NPIV[%u]:%016llx-%016llx", + created, vpid.port_name, vpid.node_name); + if (fc_vport_create(lport->host, 0, &vpid)) + created++; + else + BNX2FC_HBA_DBG(lport, "Failed to create vport\n"); + } +done: + return created; +} + static int __bnx2fc_enable(struct fcoe_ctlr *ctlr) { struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr); + struct bnx2fc_hba *hba; + struct cnic_fc_npiv_tbl npiv_tbl; + struct fc_lport *lport; if (interface->enabled == false) { if (!ctlr->lp) { @@ -2064,6 +2104,32 @@ static int __bnx2fc_enable(struct fcoe_ctlr *ctlr) interface->enabled = true; } } + + /* Create static NPIV ports if any are contained in NVRAM */ + hba = interface->hba; + lport = ctlr->lp; + + if (!hba) + goto done; + + if (!hba->cnic) + goto done; + + if (!lport) + goto done; + + if (!lport->host) + goto done; + + if (!hba->cnic->get_fc_npiv_tbl) + goto done; + + memset(&npiv_tbl, 0, sizeof(npiv_tbl)); + if (hba->cnic->get_fc_npiv_tbl(hba->cnic, &npiv_tbl)) + goto done; + + bnx2fc_npiv_create_vports(lport, &npiv_tbl); +done: return 0; }