diff options
Diffstat (limited to 'drivers/scsi/bnx2fc')
| -rw-r--r-- | drivers/scsi/bnx2fc/Makefile | 3 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc.h | 82 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_constants.h | 3 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_debug.c | 70 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_debug.h | 73 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_els.c | 43 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 654 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_hwi.c | 183 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_io.c | 197 | ||||
| -rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_tgt.c | 193 |
10 files changed, 987 insertions, 514 deletions
diff --git a/drivers/scsi/bnx2fc/Makefile b/drivers/scsi/bnx2fc/Makefile index a92695a2517..141149e8cda 100644 --- a/drivers/scsi/bnx2fc/Makefile +++ b/drivers/scsi/bnx2fc/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_SCSI_BNX2X_FCOE) += bnx2fc.o -bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o +bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o \ + bnx2fc_debug.o diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index 63de1c7cd0c..6a976657b47 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -2,7 +2,7 @@ #define _BNX2FC_H_ /* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -11,6 +11,8 @@ * Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com) */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -57,15 +59,17 @@ #include <scsi/fc/fc_fcp.h> #include "57xx_hsi_bnx2fc.h" -#include "bnx2fc_debug.h" #include "../../net/ethernet/broadcom/cnic_if.h" +#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h" #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" -#define BNX2FC_VERSION "1.0.8" +#define BNX2FC_VERSION "2.4.2" #define PFX "bnx2fc: " +#define BCM_CHIP_LEN 16 + #define BNX2X_DOORBELL_PCI_BAR 2 #define BNX2FC_MAX_BD_LEN 0xffff @@ -84,9 +88,8 @@ #define BNX2FC_NUM_MAX_SESS 1024 #define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS)) -#define BNX2FC_MAX_OUTSTANDING_CMNDS 2048 -#define BNX2FC_CAN_QUEUE BNX2FC_MAX_OUTSTANDING_CMNDS -#define BNX2FC_ELSTM_XIDS BNX2FC_CAN_QUEUE +#define BNX2FC_MAX_NPIV 256 + #define BNX2FC_MIN_PAYLOAD 256 #define BNX2FC_MAX_PAYLOAD 2048 #define BNX2FC_MFS \ @@ -102,27 +105,25 @@ #define BNX2FC_RQ_WQE_SIZE (BNX2FC_RQ_BUF_SZ) #define BNX2FC_XFERQ_WQE_SIZE (sizeof(struct fcoe_xfrqe)) #define BNX2FC_CONFQ_WQE_SIZE (sizeof(struct fcoe_confqe)) -#define BNX2FC_5771X_DB_PAGE_SIZE 128 +#define BNX2X_DB_SHIFT 3 -#define BNX2FC_MAX_TASKS \ - (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS) #define BNX2FC_TASK_SIZE 128 #define BNX2FC_TASKS_PER_PAGE (PAGE_SIZE/BNX2FC_TASK_SIZE) -#define BNX2FC_TASK_CTX_ARR_SZ (BNX2FC_MAX_TASKS/BNX2FC_TASKS_PER_PAGE) #define BNX2FC_MAX_ROWS_IN_HASH_TBL 8 #define BNX2FC_HASH_TBL_CHUNK_SIZE (16 * 1024) #define BNX2FC_MAX_SEQS 255 +#define BNX2FC_MAX_RETRY_CNT 3 +#define BNX2FC_MAX_RPORT_RETRY_CNT 255 #define BNX2FC_READ (1 << 1) #define BNX2FC_WRITE (1 << 0) #define BNX2FC_MIN_XID 0 -#define BNX2FC_MAX_XID \ - (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS - 1) -#define FCOE_MIN_XID (BNX2FC_MAX_XID + 1) -#define FCOE_MAX_XID (FCOE_MIN_XID + 4095) +#define FCOE_MAX_NUM_XIDS 0x2000 +#define FCOE_MAX_XID_OFFSET (FCOE_MAX_NUM_XIDS - 1) +#define FCOE_XIDS_PER_CPU_OFFSET ((512 * nr_cpu_ids) - 1) #define BNX2FC_MAX_LUN 0xFFFF #define BNX2FC_MAX_FCP_TGT 256 #define BNX2FC_MAX_CMD_LEN 16 @@ -145,6 +146,21 @@ #define REC_RETRY_COUNT 1 #define BNX2FC_NUM_ERR_BITS 63 +#define BNX2FC_RELOGIN_WAIT_TIME 200 +#define BNX2FC_RELOGIN_WAIT_CNT 10 + +#define BNX2FC_STATS(hba, stat, cnt) \ + do { \ + u32 val; \ + \ + val = fw_stats->stat.cnt; \ + if (hba->prev_stats.stat.cnt <= val) \ + val -= hba->prev_stats.stat.cnt; \ + else \ + val += (0xfffffff - hba->prev_stats.stat.cnt); \ + hba->bfw_stats.cnt += val; \ + } while (0) + /* bnx2fc driver uses only one instance of fcoe_percpu_s */ extern struct fcoe_percpu_s bnx2fc_global; @@ -156,6 +172,14 @@ struct bnx2fc_percpu_s { spinlock_t fp_work_lock; }; +struct bnx2fc_fw_stats { + u64 fc_crc_cnt; + u64 fcoe_tx_pkt_cnt; + u64 fcoe_rx_pkt_cnt; + u64 fcoe_tx_byte_cnt; + u64 fcoe_rx_byte_cnt; +}; + struct bnx2fc_hba { struct list_head list; struct cnic_dev *cnic; @@ -175,6 +199,13 @@ struct bnx2fc_hba { #define BNX2FC_FLAG_FW_INIT_DONE 0 #define BNX2FC_FLAG_DESTROY_CMPL 1 u32 next_conn_id; + + /* xid resources */ + u16 max_xid; + u32 max_tasks; + u32 max_outstanding_cmds; + u32 elstm_xids; + struct fcoe_task_ctx_entry **task_ctx; dma_addr_t *task_ctx_dma; struct regpair *task_ctx_bd_tbl; @@ -196,9 +227,12 @@ struct bnx2fc_hba { struct bnx2fc_rport **tgt_ofld_list; /* statistics */ + struct bnx2fc_fw_stats bfw_stats; + struct fcoe_statistics_params prev_stats; struct fcoe_statistics_params *stats_buffer; dma_addr_t stats_buf_dma; struct completion stat_req_done; + struct fcoe_capabilities fcoe_cap; /*destroy handling */ struct timer_list destroy_timer; @@ -209,6 +243,8 @@ struct bnx2fc_hba { int wait_for_link_down; int num_ofld_sess; struct list_head vports; + + char chip_num[BCM_CHIP_LEN]; }; struct bnx2fc_interface { @@ -221,13 +257,16 @@ struct bnx2fc_interface { struct packet_type fip_packet_type; struct workqueue_struct *timer_work_queue; struct kref kref; - struct fcoe_ctlr ctlr; u8 vlan_enabled; int vlan_id; bool enabled; }; -#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_interface, ctlr) +#define bnx2fc_from_ctlr(x) \ + ((struct bnx2fc_interface *)((x) + 1)) + +#define bnx2fc_to_ctlr(x) \ + ((struct fcoe_ctlr *)(((struct fcoe_ctlr *)(x)) - 1)) struct bnx2fc_lport { struct list_head list; @@ -264,6 +303,8 @@ struct bnx2fc_rport { #define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x6 #define BNX2FC_FLAG_UPLD_REQ_COMPL 0x7 #define BNX2FC_FLAG_EXPL_LOGO 0x8 +#define BNX2FC_FLAG_DISABLE_FAILED 0x9 +#define BNX2FC_FLAG_ENABLED 0xa u8 src_addr[ETH_ALEN]; u32 max_sqes; @@ -326,6 +367,7 @@ struct bnx2fc_rport { atomic_t num_active_ios; u32 flush_in_prog; unsigned long timestamp; + unsigned long retry_delay_timestamp; struct list_head free_task_list; struct bnx2fc_cmd *pending_queue[BNX2FC_SQ_WQES_MAX+1]; struct list_head active_cmd_queue; @@ -452,6 +494,8 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba); int bnx2fc_send_fw_fcoe_destroy_msg(struct bnx2fc_hba *hba); int bnx2fc_send_session_ofld_req(struct fcoe_port *port, struct bnx2fc_rport *tgt); +int bnx2fc_send_session_enable_req(struct fcoe_port *port, + struct bnx2fc_rport *tgt); int bnx2fc_send_session_disable_req(struct fcoe_port *port, struct bnx2fc_rport *tgt); int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba, @@ -463,8 +507,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba); void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba); int bnx2fc_setup_fw_resc(struct bnx2fc_hba *hba); void bnx2fc_free_fw_resc(struct bnx2fc_hba *hba); -struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, - u16 min_xid, u16 max_xid); +struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba); void bnx2fc_cmd_mgr_free(struct bnx2fc_cmd_mgr *cmgr); void bnx2fc_get_link_state(struct bnx2fc_hba *hba); char *bnx2fc_get_next_rqe(struct bnx2fc_rport *tgt, u8 num_items); @@ -544,4 +587,7 @@ void bnx2fc_process_seq_cleanup_compl(struct bnx2fc_cmd *seq_clnup_req, int bnx2fc_initiate_seq_cleanup(struct bnx2fc_cmd *orig_io_req, u32 offset, enum fc_rctl r_ctl); + +#include "bnx2fc_debug.h" + #endif diff --git a/drivers/scsi/bnx2fc/bnx2fc_constants.h b/drivers/scsi/bnx2fc/bnx2fc_constants.h index 399cda047a7..dad9924abbb 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_constants.h +++ b/drivers/scsi/bnx2fc/bnx2fc_constants.h @@ -7,7 +7,7 @@ /* Current FCoE HSI version number composed of two fields (16 bit) */ /* Implies on a change broken previous HSI */ -#define FCOE_HSI_MAJOR_VERSION (1) +#define FCOE_HSI_MAJOR_VERSION (2) /* Implies on a change which does not broken previous HSI */ #define FCOE_HSI_MINOR_VERSION (1) @@ -47,6 +47,7 @@ #define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4) #define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5) #define FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION (0x6) +#define FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81) /* CQE type */ #define FCOE_PENDING_CQE_TYPE 0 diff --git a/drivers/scsi/bnx2fc/bnx2fc_debug.c b/drivers/scsi/bnx2fc/bnx2fc_debug.c new file mode 100644 index 00000000000..0cbee1b23ee --- /dev/null +++ b/drivers/scsi/bnx2fc/bnx2fc_debug.c @@ -0,0 +1,70 @@ +#include "bnx2fc.h" + +void BNX2FC_IO_DBG(const struct bnx2fc_cmd *io_req, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (likely(!(bnx2fc_debug_level & LOG_IO))) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (io_req && io_req->port && io_req->port->lport && + io_req->port->lport->host) + shost_printk(KERN_INFO, io_req->port->lport->host, + PFX "xid:0x%x %pV", + io_req->xid, &vaf); + else + pr_info("NULL %pV", &vaf); + + va_end(args); +} + +void BNX2FC_TGT_DBG(const struct bnx2fc_rport *tgt, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (likely(!(bnx2fc_debug_level & LOG_TGT))) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (tgt && tgt->port && tgt->port->lport && tgt->port->lport->host && + tgt->rport) + shost_printk(KERN_INFO, tgt->port->lport->host, + PFX "port:%x %pV", + tgt->rport->port_id, &vaf); + else + pr_info("NULL %pV", &vaf); + + va_end(args); +} + +void BNX2FC_HBA_DBG(const struct fc_lport *lport, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (likely(!(bnx2fc_debug_level & LOG_HBA))) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (lport && lport->host) + shost_printk(KERN_INFO, lport->host, PFX "%pV", &vaf); + else + pr_info("NULL %pV", &vaf); + + va_end(args); +} diff --git a/drivers/scsi/bnx2fc/bnx2fc_debug.h b/drivers/scsi/bnx2fc/bnx2fc_debug.h index 3416d9a746c..4808ff99621 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_debug.h +++ b/drivers/scsi/bnx2fc/bnx2fc_debug.h @@ -11,60 +11,23 @@ extern unsigned int bnx2fc_debug_level; -#define BNX2FC_CHK_LOGGING(LEVEL, CMD) \ - do { \ - if (unlikely(bnx2fc_debug_level & LEVEL)) \ - do { \ - CMD; \ - } while (0); \ - } while (0) - -#define BNX2FC_ELS_DBG(fmt, arg...) \ - BNX2FC_CHK_LOGGING(LOG_ELS, \ - printk(KERN_INFO PFX fmt, ##arg)) - -#define BNX2FC_MISC_DBG(fmt, arg...) \ - BNX2FC_CHK_LOGGING(LOG_MISC, \ - printk(KERN_INFO PFX fmt, ##arg)) - -#define BNX2FC_IO_DBG(io_req, fmt, arg...) \ - do { \ - if (!io_req || !io_req->port || !io_req->port->lport || \ - !io_req->port->lport->host) \ - BNX2FC_CHK_LOGGING(LOG_IO, \ - printk(KERN_INFO PFX "NULL " fmt, ##arg)); \ - else \ - BNX2FC_CHK_LOGGING(LOG_IO, \ - shost_printk(KERN_INFO, \ - (io_req)->port->lport->host, \ - PFX "xid:0x%x " fmt, \ - (io_req)->xid, ##arg)); \ - } while (0) - -#define BNX2FC_TGT_DBG(tgt, fmt, arg...) \ - do { \ - if (!tgt || !tgt->port || !tgt->port->lport || \ - !tgt->port->lport->host || !tgt->rport) \ - BNX2FC_CHK_LOGGING(LOG_TGT, \ - printk(KERN_INFO PFX "NULL " fmt, ##arg)); \ - else \ - BNX2FC_CHK_LOGGING(LOG_TGT, \ - shost_printk(KERN_INFO, \ - (tgt)->port->lport->host, \ - PFX "port:%x " fmt, \ - (tgt)->rport->port_id, ##arg)); \ - } while (0) - - -#define BNX2FC_HBA_DBG(lport, fmt, arg...) \ - do { \ - if (!lport || !lport->host) \ - BNX2FC_CHK_LOGGING(LOG_HBA, \ - printk(KERN_INFO PFX "NULL " fmt, ##arg)); \ - else \ - BNX2FC_CHK_LOGGING(LOG_HBA, \ - shost_printk(KERN_INFO, lport->host, \ - PFX fmt, ##arg)); \ - } while (0) +#define BNX2FC_ELS_DBG(fmt, ...) \ +do { \ + if (unlikely(bnx2fc_debug_level & LOG_ELS)) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +#define BNX2FC_MISC_DBG(fmt, ...) \ +do { \ + if (unlikely(bnx2fc_debug_level & LOG_MISC)) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +__printf(2, 3) +void BNX2FC_IO_DBG(const struct bnx2fc_cmd *io_req, const char *fmt, ...); +__printf(2, 3) +void BNX2FC_TGT_DBG(const struct bnx2fc_rport *tgt, const char *fmt, ...); +__printf(2, 3) +void BNX2FC_HBA_DBG(const struct fc_lport *lport, const char *fmt, ...); #endif diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c index fd382fe33f6..b1c9a4f8cae 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -3,7 +3,7 @@ * This file contains helper routines that handle ELS requests * and responses. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -268,17 +268,6 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) orig_io_req = cb_arg->aborted_io_req; srr_req = cb_arg->io_req; - if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "srr_compl: xid - 0x%x completed", - orig_io_req->xid); - goto srr_compl_done; - } - if (test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { - BNX2FC_IO_DBG(srr_req, "rec abts in prog " - "orig_io - 0x%x\n", - orig_io_req->xid); - goto srr_compl_done; - } if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &srr_req->req_flags)) { /* SRR timedout */ BNX2FC_IO_DBG(srr_req, "srr timed out, abort " @@ -290,6 +279,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) "failed. issue cleanup\n"); bnx2fc_initiate_cleanup(srr_req); } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } orig_io_req->srr_retry++; if (orig_io_req->srr_retry <= SRR_RETRY_COUNT) { struct bnx2fc_rport *tgt = orig_io_req->tgt; @@ -311,6 +306,12 @@ void bnx2fc_srr_compl(struct bnx2fc_els_cb_arg *cb_arg) } goto srr_compl_done; } + if (test_bit(BNX2FC_FLAG_IO_COMPL, &orig_io_req->req_flags) || + test_bit(BNX2FC_FLAG_ISSUE_ABTS, &orig_io_req->req_flags)) { + BNX2FC_IO_DBG(srr_req, "srr_compl:xid - 0x%x flags = %lx", + orig_io_req->xid, orig_io_req->req_flags); + goto srr_compl_done; + } mp_req = &(srr_req->mp_req); fc_hdr = &(mp_req->resp_fc_hdr); resp_len = mp_req->resp_len; @@ -853,7 +854,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, struct fc_exch *exch = fc_seq_exch(seq); struct fc_lport *lport = exch->lp; u8 *mac; - struct fc_frame_header *fh; u8 op; if (IS_ERR(fp)) @@ -861,13 +861,6 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, mac = fr_cb(fp)->granted_mac; if (is_zero_ether_addr(mac)) { - fh = fc_frame_header_get(fp); - if (fh->fh_type != FC_TYPE_ELS) { - printk(KERN_ERR PFX "bnx2fc_flogi_resp:" - "fh_type != FC_TYPE_ELS\n"); - fc_frame_free(fp); - return; - } op = fc_frame_payload_op(fp); if (lport->vport) { if (op == ELS_LS_RJT) { @@ -877,12 +870,10 @@ static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, return; } } - if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { - fc_frame_free(fp); - return; - } + fcoe_ctlr_recv_flogi(fip, lport, fp); } - fip->update_mac(lport, mac); + if (!is_zero_ether_addr(mac)) + fip->update_mac(lport, mac); done: fc_lport_flogi_resp(seq, fp, lport); } @@ -909,7 +900,7 @@ struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did, { struct fcoe_port *port = lport_priv(lport); struct bnx2fc_interface *interface = port->priv; - struct fcoe_ctlr *fip = &interface->ctlr; + struct fcoe_ctlr *fip = bnx2fc_to_ctlr(interface); struct fc_frame_header *fh = fc_frame_header_get(fp); switch (op) { diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 85bcc4b5596..785d0d71781 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -3,7 +3,7 @@ * cnic modules to create FCoE instances, send/receive non-offloaded * FIP/FCoE packets, listen to link events etc. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -22,10 +22,10 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu); #define DRV_MODULE_NAME "bnx2fc" #define DRV_MODULE_VERSION BNX2FC_VERSION -#define DRV_MODULE_RELDATE "Oct 02, 2011" +#define DRV_MODULE_RELDATE "Dec 11, 2013" -static char version[] __devinitdata = +static char version[] = "Broadcom NetXtreme II FCoE Driver " DRV_MODULE_NAME \ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -54,6 +54,7 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb; static struct libfc_function_template bnx2fc_libfc_fcn_templ; static struct scsi_host_template bnx2fc_shost_template; static struct fc_function_template bnx2fc_transport_function; +static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ; static struct fc_function_template bnx2fc_vport_xport_function; static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode); static void __bnx2fc_destroy(struct bnx2fc_interface *interface); @@ -61,12 +62,16 @@ static int bnx2fc_destroy(struct net_device *net_device); static int bnx2fc_enable(struct net_device *netdev); static int bnx2fc_disable(struct net_device *netdev); +/* fcoe_syfs control interface handlers */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev); +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev); + static void bnx2fc_recv_frame(struct sk_buff *skb); static void bnx2fc_start_disc(struct bnx2fc_interface *interface); static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev); static int bnx2fc_lport_config(struct fc_lport *lport); -static int bnx2fc_em_config(struct fc_lport *lport); +static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba); static int bnx2fc_bind_adapter_devices(struct bnx2fc_hba *hba); static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba); static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba); @@ -105,17 +110,14 @@ static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport) ((struct fcoe_port *)lport_priv(lport))->priv)->netdev; } -/** - * bnx2fc_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void bnx2fc_get_lesb(struct fc_lport *lport, - struct fc_els_lesb *fc_lesb) +static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { - struct net_device *netdev = bnx2fc_netdev(lport); + struct fcoe_ctlr_device *ctlr_dev = + fcoe_fcf_dev_to_ctlr_dev(fcf_dev); + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); + struct bnx2fc_interface *fcoe = fcoe_ctlr_priv(ctlr); - __fcoe_get_lesb(lport, fc_lesb, netdev); + fcf_dev->vlan_id = fcoe->vlan_id; } static void bnx2fc_clean_rx_queue(struct fc_lport *lp) @@ -244,11 +246,12 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) struct sk_buff *skb; struct fc_frame_header *fh; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; struct bnx2fc_hba *hba; struct fcoe_port *port; struct fcoe_hdr *hp; struct bnx2fc_rport *tgt; - struct fcoe_dev_stats *stats; + struct fc_stats *stats; u8 sof, eof; u32 crc; unsigned int hlen, tlen, elen; @@ -256,6 +259,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) port = (struct fcoe_port *)lport_priv(lport); interface = port->priv; + ctlr = bnx2fc_to_ctlr(interface); hba = interface->hba; fh = fc_frame_header_get(fp); @@ -268,12 +272,12 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) { - if (!interface->ctlr.sel_fcf) { + if (!ctlr->sel_fcf) { BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n"); kfree_skb(skb); return -EINVAL; } - if (fcoe_ctlr_els_send(&interface->ctlr, lport, skb)) + if (fcoe_ctlr_els_send(ctlr, lport, skb)) return 0; } @@ -322,8 +326,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) return -ENOMEM; } frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; - cp = kmap_atomic(skb_frag_page(frag), KM_SKB_DATA_SOFTIRQ) - + frag->page_offset; + cp = kmap_atomic(skb_frag_page(frag)) + frag->page_offset; } else { cp = (struct fcoe_crc_eof *)skb_put(skb, tlen); } @@ -332,7 +335,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) cp->fcoe_eof = eof; cp->fcoe_crc32 = cpu_to_le32(~crc); if (skb_is_nonlinear(skb)) { - kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ); + kunmap_atomic(cp); cp = NULL; } @@ -347,14 +350,14 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); - if (interface->ctlr.map_dest) + if (ctlr->map_dest) fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); else /* insert GW address */ - memcpy(eh->h_dest, interface->ctlr.dest_addr, ETH_ALEN); + memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN); - if (unlikely(interface->ctlr.flogi_oxid != FC_XID_UNKNOWN)) - memcpy(eh->h_source, interface->ctlr.ctl_src_addr, ETH_ALEN); + if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN)) + memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN); else memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); @@ -374,7 +377,7 @@ static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp) } /*update tx stats */ - stats = per_cpu_ptr(lport->dev_stats, get_cpu()); + stats = per_cpu_ptr(lport->stats, get_cpu()); stats->TxFrames++; stats->TxWords += wlen; put_cpu(); @@ -404,6 +407,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, { struct fc_lport *lport; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; struct fc_frame_header *fh; struct fcoe_rcv_info *fr; struct fcoe_percpu_s *bg; @@ -411,7 +415,8 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, interface = container_of(ptype, struct bnx2fc_interface, fcoe_packet_type); - lport = interface->ctlr.lp; + ctlr = bnx2fc_to_ctlr(interface); + lport = ctlr->lp; if (unlikely(lport == NULL)) { printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n"); @@ -440,13 +445,13 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev, fr->fr_dev = lport; bg = &bnx2fc_global; - spin_lock_bh(&bg->fcoe_rx_list.lock); + spin_lock(&bg->fcoe_rx_list.lock); __skb_queue_tail(&bg->fcoe_rx_list, skb); if (bg->fcoe_rx_list.qlen == 1) wake_up_process(bg->thread); - spin_unlock_bh(&bg->fcoe_rx_list.lock); + spin_unlock(&bg->fcoe_rx_list.lock); return 0; err: @@ -459,7 +464,7 @@ static int bnx2fc_l2_rcv_thread(void *arg) struct fcoe_percpu_s *bg = arg; struct sk_buff *skb; - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); @@ -482,7 +487,7 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) u32 fr_len; struct fc_lport *lport; struct fcoe_rcv_info *fr; - struct fcoe_dev_stats *stats; + struct fc_stats *stats; struct fc_frame_header *fh; struct fcoe_crc_eof crc_eof; struct fc_frame *fp; @@ -511,23 +516,17 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) skb_pull(skb, sizeof(struct fcoe_hdr)); fr_len = skb->len - sizeof(struct fcoe_crc_eof); - stats = per_cpu_ptr(lport->dev_stats, get_cpu()); - stats->RxFrames++; - stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; - fp = (struct fc_frame *)skb; fc_frame_init(fp); fr_dev(fp) = lport; fr_sof(fp) = hp->fcoe_sof; if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { - put_cpu(); kfree_skb(skb); return; } fr_eof(fp) = crc_eof.fcoe_eof; fr_crc(fp) = crc_eof.fcoe_crc32; if (pskb_trim(skb, fr_len)) { - put_cpu(); kfree_skb(skb); return; } @@ -537,10 +536,8 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id)); if (vn_port) { port = lport_priv(vn_port); - if (compare_ether_addr(port->data_src_addr, dest_mac) - != 0) { + if (!ether_addr_equal(port->data_src_addr, dest_mac)) { BNX2FC_HBA_DBG(lport, "fpma mismatch\n"); - put_cpu(); kfree_skb(skb); return; } @@ -548,7 +545,6 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) { /* Drop FCP data. We dont this in L2 path */ - put_cpu(); kfree_skb(skb); return; } @@ -558,7 +554,6 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) case ELS_LOGO: if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { /* drop non-FIP LOGO */ - put_cpu(); kfree_skb(skb); return; } @@ -568,22 +563,23 @@ static void bnx2fc_recv_frame(struct sk_buff *skb) if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) { /* Drop incoming ABTS */ - put_cpu(); kfree_skb(skb); return; } + stats = per_cpu_ptr(lport->stats, smp_processor_id()); + stats->RxFrames++; + stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; + if (le32_to_cpu(fr_crc(fp)) != ~crc32(~0, skb->data, fr_len)) { if (stats->InvalidCRCCount < 5) printk(KERN_WARNING PFX "dropping frame with " "CRC error\n"); stats->InvalidCRCCount++; - put_cpu(); kfree_skb(skb); return; } - put_cpu(); fc_exch_recv(lport, fp); } @@ -598,7 +594,7 @@ int bnx2fc_percpu_io_thread(void *arg) struct bnx2fc_work *work, *tmp; LIST_HEAD(work_list); - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); @@ -647,11 +643,16 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost) BNX2FC_HBA_DBG(lport, "FW stat req timed out\n"); return bnx2fc_stats; } - bnx2fc_stats->invalid_crc_count += fw_stats->rx_stat2.fc_crc_cnt; - bnx2fc_stats->tx_frames += fw_stats->tx_stat.fcoe_tx_pkt_cnt; - bnx2fc_stats->tx_words += (fw_stats->tx_stat.fcoe_tx_byte_cnt) / 4; - bnx2fc_stats->rx_frames += fw_stats->rx_stat0.fcoe_rx_pkt_cnt; - bnx2fc_stats->rx_words += (fw_stats->rx_stat0.fcoe_rx_byte_cnt) / 4; + BNX2FC_STATS(hba, rx_stat2, fc_crc_cnt); + bnx2fc_stats->invalid_crc_count += hba->bfw_stats.fc_crc_cnt; + BNX2FC_STATS(hba, tx_stat, fcoe_tx_pkt_cnt); + bnx2fc_stats->tx_frames += hba->bfw_stats.fcoe_tx_pkt_cnt; + BNX2FC_STATS(hba, tx_stat, fcoe_tx_byte_cnt); + bnx2fc_stats->tx_words += ((hba->bfw_stats.fcoe_tx_byte_cnt) / 4); + BNX2FC_STATS(hba, rx_stat0, fcoe_rx_pkt_cnt); + bnx2fc_stats->rx_frames += hba->bfw_stats.fcoe_rx_pkt_cnt; + BNX2FC_STATS(hba, rx_stat0, fcoe_rx_byte_cnt); + bnx2fc_stats->rx_words += ((hba->bfw_stats.fcoe_rx_byte_cnt) / 4); bnx2fc_stats->dumped_frames = 0; bnx2fc_stats->lip_count = 0; @@ -660,6 +661,8 @@ static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost) bnx2fc_stats->loss_of_signal_count = 0; bnx2fc_stats->prim_seq_protocol_err_count = 0; + memcpy(&hba->prev_stats, hba->stats_buffer, + sizeof(struct fcoe_statistics_params)); return bnx2fc_stats; } @@ -667,6 +670,7 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev) { struct fcoe_port *port = lport_priv(lport); struct bnx2fc_interface *interface = port->priv; + struct bnx2fc_hba *hba = interface->hba; struct Scsi_Host *shost = lport->host; int rc = 0; @@ -687,42 +691,14 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev) } if (!lport->vport) fc_host_max_npiv_vports(lport->host) = USHRT_MAX; - sprintf(fc_host_symbolic_name(lport->host), "%s v%s over %s", - BNX2FC_NAME, BNX2FC_VERSION, + snprintf(fc_host_symbolic_name(lport->host), 256, + "%s (Broadcom %s) v%s over %s", + BNX2FC_NAME, hba->chip_num, BNX2FC_VERSION, interface->netdev->name); return 0; } -static void bnx2fc_link_speed_update(struct fc_lport *lport) -{ - struct fcoe_port *port = lport_priv(lport); - struct bnx2fc_interface *interface = port->priv; - struct net_device *netdev = interface->netdev; - struct ethtool_cmd ecmd; - - if (!__ethtool_get_settings(netdev, &ecmd)) { - lport->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; - - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_2500: - lport->link_speed = FC_PORTSPEED_2GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - } -} static int bnx2fc_link_ok(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); @@ -759,11 +735,13 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev) { struct bnx2fc_hba *hba; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; struct fcoe_port *port; u64 wwnn, wwpn; port = lport_priv(lport); interface = port->priv; + ctlr = bnx2fc_to_ctlr(interface); hba = interface->hba; /* require support for get_pauseparam ethtool op. */ @@ -778,17 +756,17 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long) lport); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) - wwnn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr, + wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0); BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn); fc_set_wwnn(lport, wwnn); if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN)) - wwpn = fcoe_wwn_from_mac(interface->ctlr.ctl_src_addr, + wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 2, 0); BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn); @@ -822,9 +800,11 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, u16 vlan_id) { struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context; + struct fcoe_ctlr_device *cdev; struct fc_lport *lport; struct fc_lport *vport; struct bnx2fc_interface *interface, *tmp; + struct fcoe_ctlr *ctlr; int wait_for_upload = 0; u32 link_possible = 1; @@ -862,10 +842,13 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, __bnx2fc_destroy(interface); } mutex_unlock(&bnx2fc_dev_lock); + + /* Ensure ALL destroy work has been completed before return */ + flush_workqueue(bnx2fc_wq); return; default: - printk(KERN_ERR PFX "Unkonwn netevent %ld", event); + printk(KERN_ERR PFX "Unknown netevent %ld", event); return; } @@ -875,34 +858,52 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, if (interface->hba != hba) continue; - lport = interface->ctlr.lp; + ctlr = bnx2fc_to_ctlr(interface); + lport = ctlr->lp; BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n", interface->netdev->name, event); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); + + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); if (link_possible && !bnx2fc_link_ok(lport)) { - /* Reset max recv frame size to default */ - fc_set_mfs(lport, BNX2FC_MFS); - /* - * ctlr link up will only be handled during - * enable to avoid sending discovery solicitation - * on a stale vlan - */ - if (interface->enabled) - fcoe_ctlr_link_up(&interface->ctlr); - } else if (fcoe_ctlr_link_down(&interface->ctlr)) { - mutex_lock(&lport->lp_mutex); - list_for_each_entry(vport, &lport->vports, list) - fc_host_port_type(vport->host) = - FC_PORTTYPE_UNKNOWN; - mutex_unlock(&lport->lp_mutex); - fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN; - per_cpu_ptr(lport->dev_stats, - get_cpu())->LinkFailureCount++; - put_cpu(); - fcoe_clean_pending_queue(lport); - wait_for_upload = 1; + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link up while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + /* Reset max recv frame size to default */ + fc_set_mfs(lport, BNX2FC_MFS); + /* + * ctlr link up will only be handled during + * enable to avoid sending discovery + * solicitation on a stale vlan + */ + if (interface->enabled) + fcoe_ctlr_link_up(ctlr); + }; + } else if (fcoe_ctlr_link_down(ctlr)) { + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link down while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + mutex_lock(&lport->lp_mutex); + list_for_each_entry(vport, &lport->vports, list) + fc_host_port_type(vport->host) = + FC_PORTTYPE_UNKNOWN; + mutex_unlock(&lport->lp_mutex); + fc_host_port_type(lport->host) = + FC_PORTTYPE_UNKNOWN; + per_cpu_ptr(lport->stats, + get_cpu())->LinkFailureCount++; + put_cpu(); + fcoe_clean_pending_queue(lport); + wait_for_upload = 1; + }; } } mutex_unlock(&bnx2fc_dev_lock); @@ -935,13 +936,21 @@ static int bnx2fc_libfc_config(struct fc_lport *lport) fc_exch_init(lport); fc_rport_init(lport); fc_disc_init(lport); + fc_disc_config(lport, lport); return 0; } -static int bnx2fc_em_config(struct fc_lport *lport) +static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba) { - if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_MIN_XID, - FCOE_MAX_XID, NULL)) { + int fcoe_min_xid, fcoe_max_xid; + + fcoe_min_xid = hba->max_xid + 1; + if (nr_cpu_ids <= 2) + fcoe_max_xid = hba->max_xid + FCOE_XIDS_PER_CPU_OFFSET; + else + fcoe_max_xid = hba->max_xid + FCOE_MAX_XID_OFFSET; + if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, fcoe_min_xid, + fcoe_max_xid, NULL)) { printk(KERN_ERR PFX "em_config:fc_exch_mgr_alloc failed\n"); return -ENOMEM; } @@ -953,8 +962,8 @@ static int bnx2fc_lport_config(struct fc_lport *lport) { lport->link_up = 0; lport->qfull = 0; - lport->max_retry_count = 3; - lport->max_rport_retry_count = 3; + lport->max_retry_count = BNX2FC_MAX_RETRY_CNT; + lport->max_rport_retry_count = BNX2FC_MAX_RPORT_RETRY_CNT; lport->e_d_tov = 2 * 1000; lport->r_a_tov = 10 * 1000; @@ -990,9 +999,11 @@ static int bnx2fc_fip_recv(struct sk_buff *skb, struct net_device *dev, struct net_device *orig_dev) { struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; interface = container_of(ptype, struct bnx2fc_interface, fip_packet_type); - fcoe_ctlr_recv(&interface->ctlr, skb); + ctlr = bnx2fc_to_ctlr(interface); + fcoe_ctlr_recv(ctlr, skb); return 0; } @@ -1150,6 +1161,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface) { struct net_device *netdev = interface->netdev; struct net_device *physdev = interface->hba->phys_dev; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct netdev_hw_addr *ha; int sel_san_mac = 0; @@ -1164,7 +1176,7 @@ static int bnx2fc_interface_setup(struct bnx2fc_interface *interface) if ((ha->type == NETDEV_HW_ADDR_T_SAN) && (is_valid_ether_addr(ha->addr))) { - memcpy(interface->ctlr.ctl_src_addr, ha->addr, + memcpy(ctlr->ctl_src_addr, ha->addr, ETH_ALEN); sel_san_mac = 1; BNX2FC_MISC_DBG("Found SAN MAC\n"); @@ -1219,19 +1231,23 @@ static void bnx2fc_release_transport(void) static void bnx2fc_interface_release(struct kref *kref) { + struct fcoe_ctlr_device *ctlr_dev; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; struct net_device *netdev; interface = container_of(kref, struct bnx2fc_interface, kref); BNX2FC_MISC_DBG("Interface is being released\n"); + ctlr = bnx2fc_to_ctlr(interface); + ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr); netdev = interface->netdev; /* tear-down FIP controller */ if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags)) - fcoe_ctlr_destroy(&interface->ctlr); + fcoe_ctlr_destroy(ctlr); - kfree(interface); + fcoe_ctlr_device_delete(ctlr_dev); dev_put(netdev); module_put(THIS_MODULE); @@ -1269,6 +1285,7 @@ static void bnx2fc_hba_destroy(struct bnx2fc_hba *hba) static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) { struct bnx2fc_hba *hba; + struct fcoe_capabilities *fcoe_cap; int rc; hba = kzalloc(sizeof(*hba), GFP_KERNEL); @@ -1280,6 +1297,12 @@ static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) mutex_init(&hba->hba_mutex); hba->cnic = cnic; + + hba->max_tasks = cnic->max_fcoe_exchanges; + hba->elstm_xids = (hba->max_tasks / 2); + hba->max_outstanding_cmds = hba->elstm_xids; + hba->max_xid = (hba->max_tasks - 1); + rc = bnx2fc_bind_pcidev(hba); if (rc) { printk(KERN_ERR PFX "create_adapter: bind error\n"); @@ -1298,12 +1321,26 @@ static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) hba->num_ofld_sess = 0; - hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba, BNX2FC_MIN_XID, - BNX2FC_MAX_XID); + hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba); if (!hba->cmd_mgr) { printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n"); goto cmgr_err; } + fcoe_cap = &hba->fcoe_cap; + + fcoe_cap->capability1 = BNX2FC_TM_MAX_SQES << + FCOE_IOS_PER_CONNECTION_SHIFT; + fcoe_cap->capability1 |= BNX2FC_NUM_MAX_SESS << + FCOE_LOGINS_PER_PORT_SHIFT; + fcoe_cap->capability2 = hba->max_outstanding_cmds << + FCOE_NUMBER_OF_EXCHANGES_SHIFT; + fcoe_cap->capability2 |= BNX2FC_MAX_NPIV << + FCOE_NPIV_WWN_PER_PORT_SHIFT; + fcoe_cap->capability3 = BNX2FC_NUM_MAX_SESS << + FCOE_TARGETS_SUPPORTED_SHIFT; + fcoe_cap->capability3 |= hba->max_outstanding_cmds << + FCOE_OUTSTANDING_COMMANDS_SHIFT; + fcoe_cap->capability4 = FCOE_CAPABILITY4_STATEFUL; init_waitqueue_head(&hba->shutdown_wait); init_waitqueue_head(&hba->destroy_wait); @@ -1324,33 +1361,41 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba, struct net_device *netdev, enum fip_state fip_mode) { + struct fcoe_ctlr_device *ctlr_dev; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; + int size; int rc = 0; - interface = kzalloc(sizeof(*interface), GFP_KERNEL); - if (!interface) { + size = (sizeof(*interface) + sizeof(struct fcoe_ctlr)); + ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &bnx2fc_fcoe_sysfs_templ, + size); + if (!ctlr_dev) { printk(KERN_ERR PFX "Unable to allocate interface structure\n"); return NULL; } + ctlr = fcoe_ctlr_device_priv(ctlr_dev); + ctlr->cdev = ctlr_dev; + interface = fcoe_ctlr_priv(ctlr); dev_hold(netdev); kref_init(&interface->kref); interface->hba = hba; interface->netdev = netdev; /* Initialize FIP */ - fcoe_ctlr_init(&interface->ctlr, fip_mode); - interface->ctlr.send = bnx2fc_fip_send; - interface->ctlr.update_mac = bnx2fc_update_src_mac; - interface->ctlr.get_src_addr = bnx2fc_get_src_mac; + fcoe_ctlr_init(ctlr, fip_mode); + ctlr->send = bnx2fc_fip_send; + ctlr->update_mac = bnx2fc_update_src_mac; + ctlr->get_src_addr = bnx2fc_get_src_mac; set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags); rc = bnx2fc_interface_setup(interface); if (!rc) return interface; - fcoe_ctlr_destroy(&interface->ctlr); + fcoe_ctlr_destroy(ctlr); dev_put(netdev); - kfree(interface); + fcoe_ctlr_device_delete(ctlr_dev); return NULL; } @@ -1368,21 +1413,23 @@ struct bnx2fc_interface *bnx2fc_interface_create(struct bnx2fc_hba *hba, static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, struct device *parent, int npiv) { + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct fc_lport *lport, *n_port; struct fcoe_port *port; struct Scsi_Host *shost; struct fc_vport *vport = dev_to_vport(parent); struct bnx2fc_lport *blport; - struct bnx2fc_hba *hba; + struct bnx2fc_hba *hba = interface->hba; int rc = 0; blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL); if (!blport) { - BNX2FC_HBA_DBG(interface->ctlr.lp, "Unable to alloc blport\n"); + BNX2FC_HBA_DBG(ctlr->lp, "Unable to alloc blport\n"); return NULL; } /* Allocate Scsi_Host structure */ + bnx2fc_shost_template.can_queue = hba->max_outstanding_cmds; if (!npiv) lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port)); else @@ -1396,6 +1443,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, port = lport_priv(lport); port->lport = lport; port->priv = interface; + port->get_netdev = bnx2fc_netdev; INIT_WORK(&port->destroy_work, bnx2fc_destroy_work); /* Configure fcoe_port */ @@ -1433,7 +1481,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, /* Allocate exchange manager */ if (!npiv) - rc = bnx2fc_em_config(lport); + rc = bnx2fc_em_config(lport, hba); else { shost = vport_to_shost(vport); n_port = shost_priv(shost); @@ -1447,7 +1495,6 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, bnx2fc_interface_get(interface); - hba = interface->hba; spin_lock_bh(&hba->hba_lock); blport->lport = lport; list_add_tail(&blport->list, &hba->vports); @@ -1474,7 +1521,8 @@ static void bnx2fc_net_cleanup(struct bnx2fc_interface *interface) static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface) { - struct fc_lport *lport = interface->ctlr.lp; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); + struct fc_lport *lport = ctlr->lp; struct fcoe_port *port = lport_priv(lport); struct bnx2fc_hba *hba = interface->hba; @@ -1514,7 +1562,8 @@ static void bnx2fc_if_destroy(struct fc_lport *lport) static void __bnx2fc_destroy(struct bnx2fc_interface *interface) { - struct fc_lport *lport = interface->ctlr.lp; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); + struct fc_lport *lport = ctlr->lp; struct fcoe_port *port = lport_priv(lport); bnx2fc_interface_cleanup(interface); @@ -1537,21 +1586,24 @@ static void __bnx2fc_destroy(struct bnx2fc_interface *interface) static int bnx2fc_destroy(struct net_device *netdev) { struct bnx2fc_interface *interface = NULL; + struct workqueue_struct *timer_work_queue; + struct fcoe_ctlr *ctlr; int rc = 0; rtnl_lock(); mutex_lock(&bnx2fc_dev_lock); interface = bnx2fc_interface_lookup(netdev); - if (!interface || !interface->ctlr.lp) { + ctlr = bnx2fc_to_ctlr(interface); + if (!interface || !ctlr->lp) { rc = -ENODEV; printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n"); goto netdev_err; } - - destroy_workqueue(interface->timer_work_queue); + timer_work_queue = interface->timer_work_queue; __bnx2fc_destroy(interface); + destroy_workqueue(timer_work_queue); netdev_err: mutex_unlock(&bnx2fc_dev_lock); @@ -1601,32 +1653,95 @@ mem_err: static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba) { struct cnic_dev *cnic; + struct pci_dev *pdev; if (!hba->cnic) { printk(KERN_ERR PFX "cnic is NULL\n"); return -ENODEV; } cnic = hba->cnic; - hba->pcidev = cnic->pcidev; - if (hba->pcidev) - pci_dev_get(hba->pcidev); + pdev = hba->pcidev = cnic->pcidev; + if (!hba->pcidev) + return -ENODEV; + switch (pdev->device) { + case PCI_DEVICE_ID_NX2_57710: + strncpy(hba->chip_num, "BCM57710", BCM_CHIP_LEN); + break; + case PCI_DEVICE_ID_NX2_57711: + strncpy(hba->chip_num, "BCM57711", BCM_CHIP_LEN); + break; + case PCI_DEVICE_ID_NX2_57712: + case PCI_DEVICE_ID_NX2_57712_MF: + case PCI_DEVICE_ID_NX2_57712_VF: + strncpy(hba->chip_num, "BCM57712", BCM_CHIP_LEN); + break; + case PCI_DEVICE_ID_NX2_57800: + case PCI_DEVICE_ID_NX2_57800_MF: + case PCI_DEVICE_ID_NX2_57800_VF: + strncpy(hba->chip_num, "BCM57800", BCM_CHIP_LEN); + break; + case PCI_DEVICE_ID_NX2_57810: + case PCI_DEVICE_ID_NX2_57810_MF: + case PCI_DEVICE_ID_NX2_57810_VF: + strncpy(hba->chip_num, "BCM57810", BCM_CHIP_LEN); + break; + case PCI_DEVICE_ID_NX2_57840: + case PCI_DEVICE_ID_NX2_57840_MF: + case PCI_DEVICE_ID_NX2_57840_VF: + case PCI_DEVICE_ID_NX2_57840_2_20: + case PCI_DEVICE_ID_NX2_57840_4_10: + strncpy(hba->chip_num, "BCM57840", BCM_CHIP_LEN); + break; + default: + pr_err(PFX "Unknown device id 0x%x\n", pdev->device); + break; + } + pci_dev_get(hba->pcidev); return 0; } static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba) { - if (hba->pcidev) + if (hba->pcidev) { + hba->chip_num[0] = '\0'; pci_dev_put(hba->pcidev); + } hba->pcidev = NULL; } +/** + * bnx2fc_ulp_get_stats - cnic callback to populate FCoE stats + * + * @handle: transport handle pointing to adapter struture + */ +static int bnx2fc_ulp_get_stats(void *handle) +{ + struct bnx2fc_hba *hba = handle; + struct cnic_dev *cnic; + struct fcoe_stats_info *stats_addr; + + if (!hba) + return -EINVAL; + + cnic = hba->cnic; + stats_addr = &cnic->stats_addr->fcoe_stat; + if (!stats_addr) + return -EINVAL; + + strncpy(stats_addr->version, BNX2FC_VERSION, + sizeof(stats_addr->version)); + stats_addr->txq_size = BNX2FC_SQ_WQES_MAX; + stats_addr->rxq_size = BNX2FC_CQ_WQES_MAX; + + return 0; +} /** * bnx2fc_ulp_start - cnic callback to initialize & start adapter instance * - * @handle: transport handle pointing to adapter struture + * @handle: transport handle pointing to adapter structure * * This function maps adapter structure to pcidev structure and initiates * firmware handshake to enable/initialize on-chip FCoE components. @@ -1640,6 +1755,7 @@ static void bnx2fc_ulp_start(void *handle) { struct bnx2fc_hba *hba = handle; struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; struct fc_lport *lport; mutex_lock(&bnx2fc_dev_lock); @@ -1651,7 +1767,8 @@ static void bnx2fc_ulp_start(void *handle) list_for_each_entry(interface, &if_list, list) { if (interface->hba == hba) { - lport = interface->ctlr.lp; + ctlr = bnx2fc_to_ctlr(interface); + lport = ctlr->lp; /* Kick off Fabric discovery*/ printk(KERN_ERR PFX "ulp_init: start discovery\n"); lport->tt.frame_send = bnx2fc_xmit; @@ -1671,13 +1788,14 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport) static void bnx2fc_stop(struct bnx2fc_interface *interface) { + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct fc_lport *lport; struct fc_lport *vport; if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) return; - lport = interface->ctlr.lp; + lport = ctlr->lp; bnx2fc_port_shutdown(lport); mutex_lock(&lport->lp_mutex); @@ -1686,7 +1804,7 @@ static void bnx2fc_stop(struct bnx2fc_interface *interface) FC_PORTTYPE_UNKNOWN; mutex_unlock(&lport->lp_mutex); fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN; - fcoe_ctlr_link_down(&interface->ctlr); + fcoe_ctlr_link_down(ctlr); fcoe_clean_pending_queue(lport); } @@ -1798,6 +1916,7 @@ exit: static void bnx2fc_start_disc(struct bnx2fc_interface *interface) { + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct fc_lport *lport; int wait_cnt = 0; @@ -1808,18 +1927,18 @@ static void bnx2fc_start_disc(struct bnx2fc_interface *interface) return; } - lport = interface->ctlr.lp; + lport = ctlr->lp; BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n"); if (!bnx2fc_link_ok(lport) && interface->enabled) { BNX2FC_HBA_DBG(lport, "ctlr_link_up\n"); - fcoe_ctlr_link_up(&interface->ctlr); + fcoe_ctlr_link_up(ctlr); fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state); } /* wait for the FCF to be selected before issuing FLOGI */ - while (!interface->ctlr.sel_fcf) { + while (!ctlr->sel_fcf) { msleep(250); /* give up after 3 secs */ if (++wait_cnt > 12) @@ -1870,6 +1989,7 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev) adapter_count++; mutex_unlock(&bnx2fc_dev_lock); + dev->fcoe_cap = &hba->fcoe_cap; clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic); rc = dev->register_device(dev, CNIC_ULP_FCOE, (void *) hba); @@ -1879,46 +1999,85 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev) set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic); } +/* Assumes rtnl_lock and the bnx2fc_dev_lock are already taken */ +static int __bnx2fc_disable(struct fcoe_ctlr *ctlr) +{ + struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr); + if (interface->enabled == true) { + if (!ctlr->lp) { + pr_err(PFX "__bnx2fc_disable: lport not found\n"); + return -ENODEV; + } else { + interface->enabled = false; + fcoe_ctlr_link_down(ctlr); + fcoe_clean_pending_queue(ctlr->lp); + } + } + return 0; +} + +/** + * Deperecated: Use bnx2fc_enabled() + */ static int bnx2fc_disable(struct net_device *netdev) { struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; int rc = 0; rtnl_lock(); mutex_lock(&bnx2fc_dev_lock); interface = bnx2fc_interface_lookup(netdev); - if (!interface || !interface->ctlr.lp) { + ctlr = bnx2fc_to_ctlr(interface); + + if (!interface) { rc = -ENODEV; - printk(KERN_ERR PFX "bnx2fc_disable: interface or lport not found\n"); + pr_err(PFX "bnx2fc_disable: interface not found\n"); } else { - interface->enabled = false; - fcoe_ctlr_link_down(&interface->ctlr); - fcoe_clean_pending_queue(interface->ctlr.lp); + rc = __bnx2fc_disable(ctlr); } - mutex_unlock(&bnx2fc_dev_lock); rtnl_unlock(); return rc; } +static int __bnx2fc_enable(struct fcoe_ctlr *ctlr) +{ + struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr); + + if (interface->enabled == false) { + if (!ctlr->lp) { + pr_err(PFX "__bnx2fc_enable: lport not found\n"); + return -ENODEV; + } else if (!bnx2fc_link_ok(ctlr->lp)) { + fcoe_ctlr_link_up(ctlr); + interface->enabled = true; + } + } + return 0; +} +/** + * Deprecated: Use bnx2fc_enabled() + */ static int bnx2fc_enable(struct net_device *netdev) { struct bnx2fc_interface *interface; + struct fcoe_ctlr *ctlr; int rc = 0; rtnl_lock(); mutex_lock(&bnx2fc_dev_lock); interface = bnx2fc_interface_lookup(netdev); - if (!interface || !interface->ctlr.lp) { + ctlr = bnx2fc_to_ctlr(interface); + if (!interface) { rc = -ENODEV; - printk(KERN_ERR PFX "bnx2fc_enable: interface or lport not found\n"); - } else if (!bnx2fc_link_ok(interface->ctlr.lp)) { - fcoe_ctlr_link_up(&interface->ctlr); - interface->enabled = true; + pr_err(PFX "bnx2fc_enable: interface not found\n"); + } else { + rc = __bnx2fc_enable(ctlr); } mutex_unlock(&bnx2fc_dev_lock); @@ -1927,24 +2086,63 @@ static int bnx2fc_enable(struct net_device *netdev) } /** - * bnx2fc_create - Create bnx2fc FCoE interface + * bnx2fc_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled + * + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); + + switch (cdev->enabled) { + case FCOE_CTLR_ENABLED: + return __bnx2fc_enable(ctlr); + case FCOE_CTLR_DISABLED: + return __bnx2fc_disable(ctlr); + case FCOE_CTLR_UNUSED: + default: + return -ENOTSUPP; + }; +} + +enum bnx2fc_create_link_state { + BNX2FC_CREATE_LINK_DOWN, + BNX2FC_CREATE_LINK_UP, +}; + +/** + * _bnx2fc_create() - Create bnx2fc FCoE interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * @link_state: The ctlr link state on creation * - * @buffer: The name of Ethernet interface to create on - * @kp: The associated kernel param + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. * - * Called from sysfs. + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. * * Returns: 0 for success */ -static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +static int _bnx2fc_create(struct net_device *netdev, + enum fip_state fip_mode, + enum bnx2fc_create_link_state link_state) { + struct fcoe_ctlr_device *cdev; + struct fcoe_ctlr *ctlr; struct bnx2fc_interface *interface; struct bnx2fc_hba *hba; - struct net_device *phys_dev; + struct net_device *phys_dev = netdev; struct fc_lport *lport; struct ethtool_drvinfo drvinfo; int rc = 0; - int vlan_id; + int vlan_id = 0; BNX2FC_MISC_DBG("Entered bnx2fc_create\n"); if (fip_mode != FIP_MODE_FABRIC) { @@ -1962,14 +2160,9 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) } /* obtain physical netdev */ - if (netdev->priv_flags & IFF_802_1Q_VLAN) { + if (netdev->priv_flags & IFF_802_1Q_VLAN) phys_dev = vlan_dev_real_dev(netdev); - vlan_id = vlan_dev_vlan_id(netdev); - } else { - printk(KERN_ERR PFX "Not a vlan device\n"); - rc = -EINVAL; - goto netdev_err; - } + /* verify if the physical device is a netxtreme2 device */ if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) { memset(&drvinfo, 0, sizeof(drvinfo)); @@ -2004,8 +2197,14 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) goto ifput_err; } + if (netdev->priv_flags & IFF_802_1Q_VLAN) { + vlan_id = vlan_dev_vlan_id(netdev); + interface->vlan_enabled = 1; + } + + ctlr = bnx2fc_to_ctlr(interface); + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); interface->vlan_id = vlan_id; - interface->vlan_enabled = 1; interface->timer_work_queue = create_singlethread_workqueue("bnx2fc_timer_wq"); @@ -2015,7 +2214,7 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) goto ifput_err; } - lport = bnx2fc_if_create(interface, &interface->hba->pcidev->dev, 0); + lport = bnx2fc_if_create(interface, &cdev->dev, 0); if (!lport) { printk(KERN_ERR PFX "Failed to create interface (%s)\n", netdev->name); @@ -2029,17 +2228,26 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) lport->boot_time = jiffies; /* Make this master N_port */ - interface->ctlr.lp = lport; + ctlr->lp = lport; + + if (link_state == BNX2FC_CREATE_LINK_UP) + cdev->enabled = FCOE_CTLR_ENABLED; + else + cdev->enabled = FCOE_CTLR_DISABLED; - if (!bnx2fc_link_ok(lport)) { - fcoe_ctlr_link_up(&interface->ctlr); + if (link_state == BNX2FC_CREATE_LINK_UP && + !bnx2fc_link_ok(lport)) { + fcoe_ctlr_link_up(ctlr); fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state); } BNX2FC_HBA_DBG(lport, "create: START DISC\n"); bnx2fc_start_disc(interface); - interface->enabled = true; + + if (link_state == BNX2FC_CREATE_LINK_UP) + interface->enabled = true; + /* * Release from kref_init in bnx2fc_interface_setup, on success * lport should be holding a reference taken in bnx2fc_if_create @@ -2055,6 +2263,7 @@ if_create_err: ifput_err: bnx2fc_net_cleanup(interface); bnx2fc_interface_put(interface); + goto mod_err; netdev_err: module_put(THIS_MODULE); mod_err: @@ -2064,6 +2273,37 @@ mod_err: } /** + * bnx2fc_create() - Create a bnx2fc interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport + * + * Returns: 0 for success + */ +static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +{ + return _bnx2fc_create(netdev, fip_mode, BNX2FC_CREATE_LINK_UP); +} + +/** + * bnx2fc_ctlr_alloc() - Allocate a bnx2fc interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev) +{ + return _bnx2fc_create(netdev, FIP_MODE_FABRIC, + BNX2FC_CREATE_LINK_DOWN); +} + +/** * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance * * @cnic: Pointer to cnic device instance @@ -2071,13 +2311,10 @@ mod_err: **/ static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic) { - struct list_head *list; - struct list_head *temp; struct bnx2fc_hba *hba; /* Called with bnx2fc_dev_lock held */ - list_for_each_safe(list, temp, &adapter_list) { - hba = (struct bnx2fc_hba *)list; + list_for_each_entry(hba, &adapter_list, list) { if (hba->cnic == cnic) return hba; } @@ -2147,6 +2384,9 @@ static void bnx2fc_ulp_exit(struct cnic_dev *dev) __bnx2fc_destroy(interface); mutex_unlock(&bnx2fc_dev_lock); + /* Ensure ALL destroy work has been completed before return */ + flush_workqueue(bnx2fc_wq); + bnx2fc_ulp_stop(hba); /* unregister cnic device */ if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic)) @@ -2171,15 +2411,17 @@ static int bnx2fc_fcoe_reset(struct Scsi_Host *shost) static bool bnx2fc_match(struct net_device *netdev) { + struct net_device *phys_dev = netdev; + mutex_lock(&bnx2fc_dev_lock); - if (netdev->priv_flags & IFF_802_1Q_VLAN) { - struct net_device *phys_dev = vlan_dev_real_dev(netdev); + if (netdev->priv_flags & IFF_802_1Q_VLAN) + phys_dev = vlan_dev_real_dev(netdev); - if (bnx2fc_hba_lookup(phys_dev)) { - mutex_unlock(&bnx2fc_dev_lock); - return true; - } + if (bnx2fc_hba_lookup(phys_dev)) { + mutex_unlock(&bnx2fc_dev_lock); + return true; } + mutex_unlock(&bnx2fc_dev_lock); return false; } @@ -2189,6 +2431,7 @@ static struct fcoe_transport bnx2fc_transport = { .name = {"bnx2fc"}, .attached = false, .list = LIST_HEAD_INIT(bnx2fc_transport.list), + .alloc = bnx2fc_ctlr_alloc, .match = bnx2fc_match, .create = bnx2fc_create, .destroy = bnx2fc_destroy, @@ -2209,9 +2452,9 @@ static void bnx2fc_percpu_thread_create(unsigned int cpu) p = &per_cpu(bnx2fc_percpu, cpu); - thread = kthread_create(bnx2fc_percpu_io_thread, - (void *)p, - "bnx2fc_thread/%d", cpu); + thread = kthread_create_on_node(bnx2fc_percpu_io_thread, + (void *)p, cpu_to_node(cpu), + "bnx2fc_thread/%d", cpu); /* bind thread to the cpu */ if (likely(!IS_ERR(thread))) { kthread_bind(thread, cpu); @@ -2341,12 +2584,16 @@ static int __init bnx2fc_mod_init(void) spin_lock_init(&p->fp_work_lock); } + cpu_notifier_register_begin(); + for_each_online_cpu(cpu) { bnx2fc_percpu_thread_create(cpu); } /* Initialize per CPU interrupt thread */ - register_hotcpu_notifier(&bnx2fc_cpu_notifier); + __register_hotcpu_notifier(&bnx2fc_cpu_notifier); + + cpu_notifier_register_done(); cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb); @@ -2411,13 +2658,17 @@ static void __exit bnx2fc_mod_exit(void) if (l2_thread) kthread_stop(l2_thread); - unregister_hotcpu_notifier(&bnx2fc_cpu_notifier); + cpu_notifier_register_begin(); /* Destroy per cpu threads */ for_each_online_cpu(cpu) { bnx2fc_percpu_thread_destroy(cpu); } + __unregister_hotcpu_notifier(&bnx2fc_cpu_notifier); + + cpu_notifier_register_done(); + destroy_workqueue(bnx2fc_wq); /* * detach from scsi transport @@ -2432,6 +2683,19 @@ static void __exit bnx2fc_mod_exit(void) module_init(bnx2fc_mod_init); module_exit(bnx2fc_mod_exit); +static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { + .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled, + .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, + + .get_fcoe_fcf_selected = fcoe_fcf_get_selected, + .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id, +}; + static struct fc_function_template bnx2fc_transport_function = { .show_host_node_name = 1, .show_host_port_name = 1, @@ -2522,10 +2786,9 @@ static struct scsi_host_template bnx2fc_shost_template = { .change_queue_type = fc_change_queue_type, .this_id = -1, .cmd_per_lun = 3, - .can_queue = BNX2FC_CAN_QUEUE, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = BNX2FC_MAX_BDS_PER_CMD, - .max_sectors = 512, + .max_sectors = 1024, }; static struct libfc_function_template bnx2fc_libfc_fcn_templ = { @@ -2533,7 +2796,7 @@ static struct libfc_function_template bnx2fc_libfc_fcn_templ = { .elsct_send = bnx2fc_elsct_send, .fcp_abort_io = bnx2fc_abort_io, .fcp_cleanup = bnx2fc_cleanup, - .get_lesb = bnx2fc_get_lesb, + .get_lesb = fcoe_get_lesb, .rport_event_callback = bnx2fc_rport_event_handler, }; @@ -2549,4 +2812,5 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb = { .cnic_stop = bnx2fc_ulp_stop, .indicate_kcqes = bnx2fc_indicate_kcqe, .indicate_netevent = bnx2fc_indicate_netevent, + .cnic_get_stats = bnx2fc_ulp_get_stats, }; diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 1923a25cb6a..512aed3ae4f 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -2,7 +2,7 @@ * This file contains the code that low level functions that interact * with 57712 FCoE firmware. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -77,7 +77,7 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba) fcoe_init1.hdr.flags = (FCOE_KWQE_LAYER_CODE << FCOE_KWQE_HEADER_LAYER_CODE_SHIFT); - fcoe_init1.num_tasks = BNX2FC_MAX_TASKS; + fcoe_init1.num_tasks = hba->max_tasks; fcoe_init1.sq_num_wqes = BNX2FC_SQ_WQES_MAX; fcoe_init1.rq_num_wqes = BNX2FC_RQ_WQES_MAX; fcoe_init1.rq_buffer_log_size = BNX2FC_RQ_BUF_LOG_SZ; @@ -126,7 +126,11 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba) fcoe_init3.error_bit_map_lo = 0xffffffff; fcoe_init3.error_bit_map_hi = 0xffffffff; - fcoe_init3.perf_config = 1; + /* + * enable both cached connection and cached tasks + * 0 = none, 1 = cached connection, 2 = cached tasks, 3 = both + */ + fcoe_init3.perf_config = 3; kwqe_arr[0] = (struct kwqe *) &fcoe_init1; kwqe_arr[1] = (struct kwqe *) &fcoe_init2; @@ -167,6 +171,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port, { struct fc_lport *lport = port->lport; struct bnx2fc_interface *interface = port->priv; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct bnx2fc_hba *hba = interface->hba; struct kwqe *kwqe_arr[4]; struct fcoe_kwqe_conn_offload1 ofld_req1; @@ -295,7 +300,7 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port, ofld_req3.flags |= (interface->vlan_enabled << FCOE_KWQE_CONN_OFFLOAD3_B_VLAN_FLAG_SHIFT); - /* C2_VALID and ACK flags are not set as they are not suppported */ + /* C2_VALID and ACK flags are not set as they are not supported */ /* Initialize offload request 4 structure */ @@ -314,13 +319,13 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port, ofld_req4.src_mac_addr_mid[1] = port->data_src_addr[2]; ofld_req4.src_mac_addr_hi[0] = port->data_src_addr[1]; ofld_req4.src_mac_addr_hi[1] = port->data_src_addr[0]; - ofld_req4.dst_mac_addr_lo[0] = interface->ctlr.dest_addr[5]; + ofld_req4.dst_mac_addr_lo[0] = ctlr->dest_addr[5]; /* fcf mac */ - ofld_req4.dst_mac_addr_lo[1] = interface->ctlr.dest_addr[4]; - ofld_req4.dst_mac_addr_mid[0] = interface->ctlr.dest_addr[3]; - ofld_req4.dst_mac_addr_mid[1] = interface->ctlr.dest_addr[2]; - ofld_req4.dst_mac_addr_hi[0] = interface->ctlr.dest_addr[1]; - ofld_req4.dst_mac_addr_hi[1] = interface->ctlr.dest_addr[0]; + ofld_req4.dst_mac_addr_lo[1] = ctlr->dest_addr[4]; + ofld_req4.dst_mac_addr_mid[0] = ctlr->dest_addr[3]; + ofld_req4.dst_mac_addr_mid[1] = ctlr->dest_addr[2]; + ofld_req4.dst_mac_addr_hi[0] = ctlr->dest_addr[1]; + ofld_req4.dst_mac_addr_hi[1] = ctlr->dest_addr[0]; ofld_req4.lcq_addr_lo = (u32) tgt->lcq_dma; ofld_req4.lcq_addr_hi = (u32)((u64) tgt->lcq_dma >> 32); @@ -346,11 +351,12 @@ int bnx2fc_send_session_ofld_req(struct fcoe_port *port, * @port: port structure pointer * @tgt: bnx2fc_rport structure pointer */ -static int bnx2fc_send_session_enable_req(struct fcoe_port *port, +int bnx2fc_send_session_enable_req(struct fcoe_port *port, struct bnx2fc_rport *tgt) { struct kwqe *kwqe_arr[2]; struct bnx2fc_interface *interface = port->priv; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct bnx2fc_hba *hba = interface->hba; struct fcoe_kwqe_conn_enable_disable enbl_req; struct fc_lport *lport = port->lport; @@ -374,12 +380,12 @@ static int bnx2fc_send_session_enable_req(struct fcoe_port *port, enbl_req.src_mac_addr_hi[1] = port->data_src_addr[0]; memcpy(tgt->src_addr, port->data_src_addr, ETH_ALEN); - enbl_req.dst_mac_addr_lo[0] = interface->ctlr.dest_addr[5]; - enbl_req.dst_mac_addr_lo[1] = interface->ctlr.dest_addr[4]; - enbl_req.dst_mac_addr_mid[0] = interface->ctlr.dest_addr[3]; - enbl_req.dst_mac_addr_mid[1] = interface->ctlr.dest_addr[2]; - enbl_req.dst_mac_addr_hi[0] = interface->ctlr.dest_addr[1]; - enbl_req.dst_mac_addr_hi[1] = interface->ctlr.dest_addr[0]; + enbl_req.dst_mac_addr_lo[0] = ctlr->dest_addr[5]; + enbl_req.dst_mac_addr_lo[1] = ctlr->dest_addr[4]; + enbl_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3]; + enbl_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2]; + enbl_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1]; + enbl_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0]; port_id = fc_host_port_id(lport->host); if (port_id != tgt->sid) { @@ -419,6 +425,7 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port, struct bnx2fc_rport *tgt) { struct bnx2fc_interface *interface = port->priv; + struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface); struct bnx2fc_hba *hba = interface->hba; struct fcoe_kwqe_conn_enable_disable disable_req; struct kwqe *kwqe_arr[2]; @@ -440,12 +447,12 @@ int bnx2fc_send_session_disable_req(struct fcoe_port *port, disable_req.src_mac_addr_hi[0] = tgt->src_addr[1]; disable_req.src_mac_addr_hi[1] = tgt->src_addr[0]; - disable_req.dst_mac_addr_lo[0] = interface->ctlr.dest_addr[5]; - disable_req.dst_mac_addr_lo[1] = interface->ctlr.dest_addr[4]; - disable_req.dst_mac_addr_mid[0] = interface->ctlr.dest_addr[3]; - disable_req.dst_mac_addr_mid[1] = interface->ctlr.dest_addr[2]; - disable_req.dst_mac_addr_hi[0] = interface->ctlr.dest_addr[1]; - disable_req.dst_mac_addr_hi[1] = interface->ctlr.dest_addr[0]; + disable_req.dst_mac_addr_lo[0] = ctlr->dest_addr[5]; + disable_req.dst_mac_addr_lo[1] = ctlr->dest_addr[4]; + disable_req.dst_mac_addr_mid[0] = ctlr->dest_addr[3]; + disable_req.dst_mac_addr_mid[1] = ctlr->dest_addr[2]; + disable_req.dst_mac_addr_hi[0] = ctlr->dest_addr[1]; + disable_req.dst_mac_addr_hi[1] = ctlr->dest_addr[0]; port_id = tgt->sid; disable_req.s_id[0] = (port_id & 0x000000FF); @@ -694,7 +701,7 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); - if (xid > BNX2FC_MAX_XID) { + if (xid > hba->max_xid) { BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid); goto ret_err_rqe; @@ -756,8 +763,6 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) case FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET: BNX2FC_TGT_DBG(tgt, "REC TOV popped for xid - 0x%x\n", xid); - memset(&io_req->err_entry, 0, - sizeof(struct fcoe_err_report_entry)); memcpy(&io_req->err_entry, err_entry, sizeof(struct fcoe_err_report_entry)); if (!test_bit(BNX2FC_FLAG_SRR_SENT, @@ -814,7 +819,7 @@ ret_err_rqe: BNX2FC_TGT_DBG(tgt, "buf_offsets - tx = 0x%x, rx = 0x%x", err_entry->data.tx_buf_off, err_entry->data.rx_buf_off); - if (xid > BNX2FC_MAX_XID) { + if (xid > hba->max_xid) { BNX2FC_TGT_DBG(tgt, "xid(0x%x) out of FW range\n", xid); goto ret_warn_rqe; } @@ -844,8 +849,6 @@ ret_err_rqe: goto ret_warn_rqe; } - memset(&io_req->err_entry, 0, - sizeof(struct fcoe_err_report_entry)); memcpy(&io_req->err_entry, err_entry, sizeof(struct fcoe_err_report_entry)); @@ -881,7 +884,7 @@ void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe) spin_lock_bh(&tgt->tgt_lock); xid = wqe & FCOE_PEND_WQ_CQE_TASK_ID; - if (xid >= BNX2FC_MAX_TASKS) { + if (xid >= hba->max_tasks) { printk(KERN_ERR PFX "ERROR:xid out of range\n"); spin_unlock_bh(&tgt->tgt_lock); return; @@ -1121,7 +1124,6 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba, struct bnx2fc_interface *interface; u32 conn_id; u32 context_id; - int rc; conn_id = ofld_kcqe->fcoe_conn_id; context_id = ofld_kcqe->fcoe_conn_context_id; @@ -1150,17 +1152,10 @@ static void bnx2fc_process_ofld_cmpl(struct bnx2fc_hba *hba, "resources\n"); set_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE, &tgt->flags); } - goto ofld_cmpl_err; } else { - - /* now enable the session */ - rc = bnx2fc_send_session_enable_req(port, tgt); - if (rc) { - printk(KERN_ERR PFX "enable session failed\n"); - goto ofld_cmpl_err; - } + /* FW offload request successfully completed */ + set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); } - return; ofld_cmpl_err: set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); wake_up_interruptible(&tgt->ofld_wait); @@ -1207,15 +1202,9 @@ static void bnx2fc_process_enable_conn_cmpl(struct bnx2fc_hba *hba, printk(KERN_ERR PFX "bnx2fc-enbl_cmpl: HBA mis-match\n"); goto enbl_cmpl_err; } - if (ofld_kcqe->completion_status) - goto enbl_cmpl_err; - else { + if (!ofld_kcqe->completion_status) /* enable successful - rport ready for issuing IOs */ - set_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); - set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); - wake_up_interruptible(&tgt->ofld_wait); - } - return; + set_bit(BNX2FC_FLAG_ENABLED, &tgt->flags); enbl_cmpl_err: set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); @@ -1241,11 +1230,14 @@ static void bnx2fc_process_conn_disable_cmpl(struct bnx2fc_hba *hba, if (disable_kcqe->completion_status) { printk(KERN_ERR PFX "Disable failed with cmpl status %d\n", disable_kcqe->completion_status); - return; + set_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags); + set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags); + wake_up_interruptible(&tgt->upld_wait); } else { /* disable successful */ BNX2FC_TGT_DBG(tgt, "disable successful\n"); clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); + clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags); set_bit(BNX2FC_FLAG_DISABLED, &tgt->flags); set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags); wake_up_interruptible(&tgt->upld_wait); @@ -1429,8 +1421,7 @@ int bnx2fc_map_doorbell(struct bnx2fc_rport *tgt) reg_base = pci_resource_start(hba->pcidev, BNX2X_DOORBELL_PCI_BAR); - reg_off = BNX2FC_5771X_DB_PAGE_SIZE * - (context_id & 0x1FFFF) + DPM_TRIGER_TYPE; + reg_off = (1 << BNX2X_DB_SHIFT) * (context_id & 0x1FFFF); tgt->ctx_base = ioremap_nocache(reg_base + reg_off, 4); if (!tgt->ctx_base) return -ENOMEM; @@ -1716,15 +1707,19 @@ void bnx2fc_init_task(struct bnx2fc_cmd *io_req, /* Tx only */ bd_count = bd_tbl->bd_valid; + cached_sge = &task->rxwr_only.union_ctx.read_info.sgl_ctx.cached_sge; if (task_type == FCOE_TASK_TYPE_WRITE) { if ((dev_type == TYPE_DISK) && (bd_count == 1)) { struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl; task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.lo = + cached_sge->cur_buf_addr.lo = fcoe_bd_tbl->buf_addr_lo; task->txwr_only.sgl_ctx.cached_sge.cur_buf_addr.hi = + cached_sge->cur_buf_addr.hi = fcoe_bd_tbl->buf_addr_hi; task->txwr_only.sgl_ctx.cached_sge.cur_buf_rem = + cached_sge->cur_buf_rem = fcoe_bd_tbl->buf_len; task->txwr_rxrd.const_ctx.init_flags |= 1 << @@ -1790,11 +1785,13 @@ void bnx2fc_init_task(struct bnx2fc_cmd *io_req, task->rxwr_txrd.var_ctx.rx_id = 0xffff; /* Rx Only */ - cached_sge = &task->rxwr_only.union_ctx.read_info.sgl_ctx.cached_sge; + if (task_type != FCOE_TASK_TYPE_READ) + return; + sgl = &task->rxwr_only.union_ctx.read_info.sgl_ctx.sgl; bd_count = bd_tbl->bd_valid; - if (task_type == FCOE_TASK_TYPE_READ && - dev_type == TYPE_DISK) { + + if (dev_type == TYPE_DISK) { if (bd_count == 1) { struct fcoe_bd_ctx *fcoe_bd_tbl = bd_tbl->bd_tbl; @@ -1848,6 +1845,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) int rc = 0; struct regpair *task_ctx_bdt; dma_addr_t addr; + int task_ctx_arr_sz; int i; /* @@ -1871,7 +1869,8 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) * Allocate task_ctx which is an array of pointers pointing to * a page containing 32 task contexts */ - hba->task_ctx = kzalloc((BNX2FC_TASK_CTX_ARR_SZ * sizeof(void *)), + task_ctx_arr_sz = (hba->max_tasks / BNX2FC_TASKS_PER_PAGE); + hba->task_ctx = kzalloc((task_ctx_arr_sz * sizeof(void *)), GFP_KERNEL); if (!hba->task_ctx) { printk(KERN_ERR PFX "unable to allocate task context array\n"); @@ -1882,7 +1881,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) /* * Allocate task_ctx_dma which is an array of dma addresses */ - hba->task_ctx_dma = kmalloc((BNX2FC_TASK_CTX_ARR_SZ * + hba->task_ctx_dma = kmalloc((task_ctx_arr_sz * sizeof(dma_addr_t)), GFP_KERNEL); if (!hba->task_ctx_dma) { printk(KERN_ERR PFX "unable to alloc context mapping array\n"); @@ -1891,7 +1890,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) } task_ctx_bdt = (struct regpair *)hba->task_ctx_bd_tbl; - for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + for (i = 0; i < task_ctx_arr_sz; i++) { hba->task_ctx[i] = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, @@ -1911,7 +1910,7 @@ int bnx2fc_setup_task_ctx(struct bnx2fc_hba *hba) return 0; out3: - for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + for (i = 0; i < task_ctx_arr_sz; i++) { if (hba->task_ctx[i]) { dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, @@ -1935,6 +1934,7 @@ out: void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba) { + int task_ctx_arr_sz; int i; if (hba->task_ctx_bd_tbl) { @@ -1944,8 +1944,9 @@ void bnx2fc_free_task_ctx(struct bnx2fc_hba *hba) hba->task_ctx_bd_tbl = NULL; } + task_ctx_arr_sz = (hba->max_tasks / BNX2FC_TASKS_PER_PAGE); if (hba->task_ctx) { - for (i = 0; i < BNX2FC_TASK_CTX_ARR_SZ; i++) { + for (i = 0; i < task_ctx_arr_sz; i++) { if (hba->task_ctx[i]) { dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, hba->task_ctx[i], @@ -1965,26 +1966,29 @@ static void bnx2fc_free_hash_table(struct bnx2fc_hba *hba) { int i; int segment_count; - int hash_table_size; u32 *pbl; - segment_count = hba->hash_tbl_segment_count; - hash_table_size = BNX2FC_NUM_MAX_SESS * BNX2FC_MAX_ROWS_IN_HASH_TBL * - sizeof(struct fcoe_hash_table_entry); + if (hba->hash_tbl_segments) { - pbl = hba->hash_tbl_pbl; - for (i = 0; i < segment_count; ++i) { - dma_addr_t dma_address; + pbl = hba->hash_tbl_pbl; + if (pbl) { + segment_count = hba->hash_tbl_segment_count; + for (i = 0; i < segment_count; ++i) { + dma_addr_t dma_address; - dma_address = le32_to_cpu(*pbl); - ++pbl; - dma_address += ((u64)le32_to_cpu(*pbl)) << 32; - ++pbl; - dma_free_coherent(&hba->pcidev->dev, - BNX2FC_HASH_TBL_CHUNK_SIZE, - hba->hash_tbl_segments[i], - dma_address); + dma_address = le32_to_cpu(*pbl); + ++pbl; + dma_address += ((u64)le32_to_cpu(*pbl)) << 32; + ++pbl; + dma_free_coherent(&hba->pcidev->dev, + BNX2FC_HASH_TBL_CHUNK_SIZE, + hba->hash_tbl_segments[i], + dma_address); + } + } + kfree(hba->hash_tbl_segments); + hba->hash_tbl_segments = NULL; } if (hba->hash_tbl_pbl) { @@ -2022,7 +2026,7 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba) dma_segment_array = kzalloc(dma_segment_array_size, GFP_KERNEL); if (!dma_segment_array) { printk(KERN_ERR PFX "hash table pointers (dma) alloc failed\n"); - return -ENOMEM; + goto cleanup_ht; } for (i = 0; i < segment_count; ++i) { @@ -2033,15 +2037,7 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba) GFP_KERNEL); if (!hba->hash_tbl_segments[i]) { printk(KERN_ERR PFX "hash segment alloc failed\n"); - while (--i >= 0) { - dma_free_coherent(&hba->pcidev->dev, - BNX2FC_HASH_TBL_CHUNK_SIZE, - hba->hash_tbl_segments[i], - dma_segment_array[i]); - hba->hash_tbl_segments[i] = NULL; - } - kfree(dma_segment_array); - return -ENOMEM; + goto cleanup_dma; } memset(hba->hash_tbl_segments[i], 0, BNX2FC_HASH_TBL_CHUNK_SIZE); @@ -2053,8 +2049,7 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba) GFP_KERNEL); if (!hba->hash_tbl_pbl) { printk(KERN_ERR PFX "hash table pbl alloc failed\n"); - kfree(dma_segment_array); - return -ENOMEM; + goto cleanup_dma; } memset(hba->hash_tbl_pbl, 0, PAGE_SIZE); @@ -2079,6 +2074,22 @@ static int bnx2fc_allocate_hash_table(struct bnx2fc_hba *hba) } kfree(dma_segment_array); return 0; + +cleanup_dma: + for (i = 0; i < segment_count; ++i) { + if (hba->hash_tbl_segments[i]) + dma_free_coherent(&hba->pcidev->dev, + BNX2FC_HASH_TBL_CHUNK_SIZE, + hba->hash_tbl_segments[i], + dma_segment_array[i]); + } + + kfree(dma_segment_array); + +cleanup_ht: + kfree(hba->hash_tbl_segments); + hba->hash_tbl_segments = NULL; + return -ENOMEM; } /** diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 0c64d184d73..7bc47fc7c68 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1,7 +1,7 @@ /* bnx2fc_io.c: Broadcom NetXtreme II Linux FCoE offload driver. * IO manager and SCSI IO processing. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -239,8 +239,7 @@ static void bnx2fc_scsi_done(struct bnx2fc_cmd *io_req, int err_code) sc_cmd->scsi_done(sc_cmd); } -struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, - u16 min_xid, u16 max_xid) +struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba) { struct bnx2fc_cmd_mgr *cmgr; struct io_bdt *bdt_info; @@ -252,6 +251,8 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, int num_ios, num_pri_ios; size_t bd_tbl_sz; int arr_sz = num_possible_cpus() + 1; + u16 min_xid = BNX2FC_MIN_XID; + u16 max_xid = hba->max_xid; if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) { printk(KERN_ERR PFX "cmd_mgr_alloc: Invalid min_xid 0x%x \ @@ -281,6 +282,8 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, arr_sz, GFP_KERNEL); if (!cmgr->free_list_lock) { printk(KERN_ERR PFX "failed to alloc free_list_lock\n"); + kfree(cmgr->free_list); + cmgr->free_list = NULL; goto mem_err; } @@ -298,7 +301,7 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, * of slow path requests. */ xid = BNX2FC_MIN_XID; - num_pri_ios = num_ios - BNX2FC_ELSTM_XIDS; + num_pri_ios = num_ios - hba->elstm_xids; for (i = 0; i < num_ios; i++) { io_req = kzalloc(sizeof(*io_req), GFP_KERNEL); @@ -367,7 +370,7 @@ void bnx2fc_cmd_mgr_free(struct bnx2fc_cmd_mgr *cmgr) struct bnx2fc_hba *hba = cmgr->hba; size_t bd_tbl_sz; u16 min_xid = BNX2FC_MIN_XID; - u16 max_xid = BNX2FC_MAX_XID; + u16 max_xid = hba->max_xid; int num_ios; int i; @@ -405,11 +408,10 @@ free_cmd_pool: goto free_cmgr; for (i = 0; i < num_possible_cpus() + 1; i++) { - struct list_head *list; - struct list_head *tmp; + struct bnx2fc_cmd *tmp, *io_req; - list_for_each_safe(list, tmp, &cmgr->free_list[i]) { - struct bnx2fc_cmd *io_req = (struct bnx2fc_cmd *)list; + list_for_each_entry_safe(io_req, tmp, + &cmgr->free_list[i], link) { list_del(&io_req->link); kfree(io_req); } @@ -594,13 +596,13 @@ static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req) mp_req->mp_resp_bd = NULL; } if (mp_req->req_buf) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, mp_req->req_buf, mp_req->req_buf_dma); mp_req->req_buf = NULL; } if (mp_req->resp_buf) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, mp_req->resp_buf, mp_req->resp_buf_dma); mp_req->resp_buf = NULL; @@ -622,7 +624,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req->req_len = sizeof(struct fcp_cmnd); io_req->data_xfer_len = mp_req->req_len; - mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &mp_req->req_buf_dma, GFP_ATOMIC); if (!mp_req->req_buf) { @@ -631,7 +633,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) return FAILED; } - mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &mp_req->resp_buf_dma, GFP_ATOMIC); if (!mp_req->resp_buf) { @@ -639,8 +641,8 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) bnx2fc_free_mp_resc(io_req); return FAILED; } - memset(mp_req->req_buf, 0, PAGE_SIZE); - memset(mp_req->resp_buf, 0, PAGE_SIZE); + memset(mp_req->req_buf, 0, CNIC_PAGE_SIZE); + memset(mp_req->resp_buf, 0, CNIC_PAGE_SIZE); /* Allocate and map mp_req_bd and mp_resp_bd */ sz = sizeof(struct fcoe_bd_ctx); @@ -655,7 +657,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req->mp_resp_bd = dma_alloc_coherent(&hba->pcidev->dev, sz, &mp_req->mp_resp_bd_dma, GFP_ATOMIC); - if (!mp_req->mp_req_bd) { + if (!mp_req->mp_resp_bd) { printk(KERN_ERR PFX "unable to alloc MP resp bd\n"); bnx2fc_free_mp_resc(io_req); return FAILED; @@ -665,7 +667,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req_bd = mp_req->mp_req_bd; mp_req_bd->buf_addr_lo = (u32)addr & 0xffffffff; mp_req_bd->buf_addr_hi = (u32)((u64)addr >> 32); - mp_req_bd->buf_len = PAGE_SIZE; + mp_req_bd->buf_len = CNIC_PAGE_SIZE; mp_req_bd->flags = 0; /* @@ -677,7 +679,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) addr = mp_req->resp_buf_dma; mp_resp_bd->buf_addr_lo = (u32)addr & 0xffffffff; mp_resp_bd->buf_addr_hi = (u32)((u64)addr >> 32); - mp_resp_bd->buf_len = PAGE_SIZE; + mp_resp_bd->buf_len = CNIC_PAGE_SIZE; mp_resp_bd->flags = 0; return SUCCESS; @@ -686,8 +688,8 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) static int bnx2fc_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags) { struct fc_lport *lport; - struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); - struct fc_rport_libfc_priv *rp = rport->dd_data; + struct fc_rport *rport; + struct fc_rport_libfc_priv *rp; struct fcoe_port *port; struct bnx2fc_interface *interface; struct bnx2fc_rport *tgt; @@ -705,6 +707,7 @@ static int bnx2fc_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags) unsigned long start = jiffies; lport = shost_priv(host); + rport = starget_to_rport(scsi_target(sc_cmd->device)); port = lport_priv(lport); interface = port->priv; @@ -713,6 +716,7 @@ static int bnx2fc_initiate_tmf(struct scsi_cmnd *sc_cmd, u8 tm_flags) rc = FAILED; goto tmf_err; } + rp = rport->dd_data; rc = fc_block_scsi_eh(sc_cmd); if (rc) @@ -810,8 +814,22 @@ retry_tmf: spin_lock_bh(&tgt->tgt_lock); io_req->wait_for_comp = 0; - if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags))) + if (!(test_bit(BNX2FC_FLAG_TM_COMPL, &io_req->req_flags))) { set_bit(BNX2FC_FLAG_TM_TIMEOUT, &io_req->req_flags); + if (io_req->on_tmf_queue) { + list_del_init(&io_req->link); + io_req->on_tmf_queue = 0; + } + io_req->wait_for_comp = 1; + bnx2fc_initiate_cleanup(io_req); + spin_unlock_bh(&tgt->tgt_lock); + rc = wait_for_completion_timeout(&io_req->tm_done, + BNX2FC_FW_TIMEOUT); + spin_lock_bh(&tgt->tgt_lock); + io_req->wait_for_comp = 0; + if (!rc) + kref_put(&io_req->refcount, bnx2fc_cmd_release); + } spin_unlock_bh(&tgt->tgt_lock); @@ -1089,6 +1107,48 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd) return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); } +int bnx2fc_expl_logo(struct fc_lport *lport, struct bnx2fc_cmd *io_req) +{ + struct bnx2fc_rport *tgt = io_req->tgt; + struct fc_rport_priv *rdata = tgt->rdata; + int logo_issued; + int rc = SUCCESS; + int wait_cnt = 0; + + BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n", + tgt->flags); + logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO, + &tgt->flags); + io_req->wait_for_comp = 1; + bnx2fc_initiate_cleanup(io_req); + + spin_unlock_bh(&tgt->tgt_lock); + + wait_for_completion(&io_req->tm_done); + + io_req->wait_for_comp = 0; + /* + * release the reference taken in eh_abort to allow the + * target to re-login after flushing IOs + */ + kref_put(&io_req->refcount, bnx2fc_cmd_release); + + if (!logo_issued) { + clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags); + mutex_lock(&lport->disc.disc_mutex); + lport->tt.rport_logoff(rdata); + mutex_unlock(&lport->disc.disc_mutex); + do { + msleep(BNX2FC_RELOGIN_WAIT_TIME); + if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) { + rc = FAILED; + break; + } + } while (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)); + } + spin_lock_bh(&tgt->tgt_lock); + return rc; +} /** * bnx2fc_eh_abort - eh_abort_handler api to abort an outstanding * SCSI command @@ -1180,26 +1240,37 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) list_add_tail(&io_req->link, &tgt->io_retire_queue); init_completion(&io_req->tm_done); - io_req->wait_for_comp = 1; - if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) { - /* Cancel the current timer running on this io_req */ + if (test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &io_req->req_flags)) { + printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " + "already in abts processing\n", io_req->xid); if (cancel_delayed_work(&io_req->timeout_work)) kref_put(&io_req->refcount, bnx2fc_cmd_release); /* drop timer hold */ - set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags); - rc = bnx2fc_initiate_abts(io_req); - } else { - printk(KERN_ERR PFX "eh_abort: io_req (xid = 0x%x) " - "already in abts processing\n", io_req->xid); - kref_put(&io_req->refcount, bnx2fc_cmd_release); - spin_unlock_bh(&tgt->tgt_lock); - return SUCCESS; + rc = bnx2fc_expl_logo(lport, io_req); + /* This only occurs when an task abort was requested while ABTS + is in progress. Setting the IO_CLEANUP flag will skip the + RRQ process in the case when the fw generated SCSI_CMD cmpl + was a result from the ABTS request rather than the CLEANUP + request */ + set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags); + goto out; } + + /* Cancel the current timer running on this io_req */ + if (cancel_delayed_work(&io_req->timeout_work)) + kref_put(&io_req->refcount, + bnx2fc_cmd_release); /* drop timer hold */ + set_bit(BNX2FC_FLAG_EH_ABORT, &io_req->req_flags); + io_req->wait_for_comp = 1; + rc = bnx2fc_initiate_abts(io_req); if (rc == FAILED) { - kref_put(&io_req->refcount, bnx2fc_cmd_release); + bnx2fc_initiate_cleanup(io_req); spin_unlock_bh(&tgt->tgt_lock); - return rc; + wait_for_completion(&io_req->tm_done); + spin_lock_bh(&tgt->tgt_lock); + io_req->wait_for_comp = 0; + goto done; } spin_unlock_bh(&tgt->tgt_lock); @@ -1207,12 +1278,16 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) spin_lock_bh(&tgt->tgt_lock); io_req->wait_for_comp = 0; - if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE, - &io_req->req_flags))) { + if (test_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags)) { + BNX2FC_IO_DBG(io_req, "IO completed in a different context\n"); + rc = SUCCESS; + } else if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE, + &io_req->req_flags))) { /* Let the scsi-ml try to recover this command */ printk(KERN_ERR PFX "abort failed, xid = 0x%x\n", io_req->xid); - rc = FAILED; + rc = bnx2fc_expl_logo(lport, io_req); + goto out; } else { /* * We come here even when there was a race condition @@ -1224,9 +1299,10 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) bnx2fc_scsi_done(io_req, DID_ABORT); kref_put(&io_req->refcount, bnx2fc_cmd_release); } - +done: /* release the reference taken in eh_abort */ kref_put(&io_req->refcount, bnx2fc_cmd_release); +out: spin_unlock_bh(&tgt->tgt_lock); return rc; } @@ -1275,6 +1351,8 @@ void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req, io_req->refcount.refcount.counter, io_req->cmd_type); bnx2fc_scsi_done(io_req, DID_ERROR); kref_put(&io_req->refcount, bnx2fc_cmd_release); + if (io_req->wait_for_comp) + complete(&io_req->tm_done); } void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req, @@ -1371,9 +1449,7 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req) { struct scsi_cmnd *sc_cmd = io_req->sc_cmd; struct bnx2fc_rport *tgt = io_req->tgt; - struct list_head *list; - struct list_head *tmp; - struct bnx2fc_cmd *cmd; + struct bnx2fc_cmd *cmd, *tmp; int tm_lun = sc_cmd->device->lun; int rc = 0; int lun; @@ -1384,9 +1460,8 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req) * Walk thru the active_ios queue and ABORT the IO * that matches with the LUN that was reset */ - list_for_each_safe(list, tmp, &tgt->active_cmd_queue) { + list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) { BNX2FC_TGT_DBG(tgt, "LUN RST cmpl: scan for pending IOs\n"); - cmd = (struct bnx2fc_cmd *)list; lun = cmd->sc_cmd->device->lun; if (lun == tm_lun) { /* Initiate ABTS on this cmd */ @@ -1411,9 +1486,7 @@ static void bnx2fc_lun_reset_cmpl(struct bnx2fc_cmd *io_req) static void bnx2fc_tgt_reset_cmpl(struct bnx2fc_cmd *io_req) { struct bnx2fc_rport *tgt = io_req->tgt; - struct list_head *list; - struct list_head *tmp; - struct bnx2fc_cmd *cmd; + struct bnx2fc_cmd *cmd, *tmp; int rc = 0; /* called with tgt_lock held */ @@ -1422,9 +1495,8 @@ static void bnx2fc_tgt_reset_cmpl(struct bnx2fc_cmd *io_req) * Walk thru the active_ios queue and ABORT the IO * that matches with the LUN that was reset */ - list_for_each_safe(list, tmp, &tgt->active_cmd_queue) { + list_for_each_entry_safe(cmd, tmp, &tgt->active_cmd_queue, link) { BNX2FC_TGT_DBG(tgt, "TGT RST cmpl: scan for pending IOs\n"); - cmd = (struct bnx2fc_cmd *)list; /* Initiate ABTS */ if (!test_and_set_bit(BNX2FC_FLAG_ISSUE_ABTS, &cmd->req_flags)) { @@ -1645,9 +1717,7 @@ void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req, memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd)); - int_to_scsilun(sc_cmd->device->lun, - (struct scsi_lun *) fcp_cmnd->fc_lun); - + int_to_scsilun(sc_cmd->device->lun, &fcp_cmnd->fc_lun); fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len); memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len); @@ -1751,7 +1821,7 @@ static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req, fcp_sns_len = SCSI_SENSE_BUFFERSIZE; } - memset(sc_cmd->sense_buffer, 0, sizeof(sc_cmd->sense_buffer)); + memset(sc_cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); if (fcp_sns_len) memcpy(sc_cmd->sense_buffer, rq_data, fcp_sns_len); @@ -1803,7 +1873,15 @@ int bnx2fc_queuecommand(struct Scsi_Host *host, rc = SCSI_MLQUEUE_TARGET_BUSY; goto exit_qcmd; } - + if (tgt->retry_delay_timestamp) { + if (time_after(jiffies, tgt->retry_delay_timestamp)) { + tgt->retry_delay_timestamp = 0; + } else { + /* If retry_delay timer is active, flow off the ML */ + rc = SCSI_MLQUEUE_TARGET_BUSY; + goto exit_qcmd; + } + } io_req = bnx2fc_cmd_alloc(tgt); if (!io_req) { rc = SCSI_MLQUEUE_HOST_BUSY; @@ -1893,6 +1971,15 @@ void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req, " fcp_resid = 0x%x\n", io_req->cdb_status, io_req->fcp_resid); sc_cmd->result = (DID_OK << 16) | io_req->cdb_status; + + if (io_req->cdb_status == SAM_STAT_TASK_SET_FULL || + io_req->cdb_status == SAM_STAT_BUSY) { + /* Set the jiffies + retry_delay_timer * 100ms + for the rport/tgt */ + tgt->retry_delay_timestamp = jiffies + + fcp_rsp->retry_delay_timer * HZ / 10; + } + } if (io_req->fcp_resid) scsi_set_resid(sc_cmd, io_req->fcp_resid); @@ -1917,7 +2004,7 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt, struct bnx2fc_interface *interface = port->priv; struct bnx2fc_hba *hba = interface->hba; struct fc_lport *lport = port->lport; - struct fcoe_dev_stats *stats; + struct fc_stats *stats; int task_idx, index; u16 xid; @@ -1928,7 +2015,7 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt, io_req->data_xfer_len = scsi_bufflen(sc_cmd); sc_cmd->SCp.ptr = (char *)io_req; - stats = per_cpu_ptr(lport->dev_stats, get_cpu()); + stats = per_cpu_ptr(lport->stats, get_cpu()); if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { io_req->io_req_flags = BNX2FC_READ; stats->InputRequests++; diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c index c1800b53127..6870cf6781d 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c +++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c @@ -2,7 +2,7 @@ * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2011 Broadcom Corporation + * Copyright (c) 2008 - 2013 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 @@ -33,6 +33,7 @@ static void bnx2fc_upld_timer(unsigned long data) BNX2FC_TGT_DBG(tgt, "upld_timer - Upload compl not received!!\n"); /* fake upload completion */ clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); + clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags); set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags); wake_up_interruptible(&tgt->upld_wait); } @@ -55,10 +56,25 @@ static void bnx2fc_ofld_timer(unsigned long data) * resources are freed up in bnx2fc_offload_session */ clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); + clear_bit(BNX2FC_FLAG_ENABLED, &tgt->flags); set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); wake_up_interruptible(&tgt->ofld_wait); } +static void bnx2fc_ofld_wait(struct bnx2fc_rport *tgt) +{ + setup_timer(&tgt->ofld_timer, bnx2fc_ofld_timer, (unsigned long)tgt); + mod_timer(&tgt->ofld_timer, jiffies + BNX2FC_FW_TIMEOUT); + + wait_event_interruptible(tgt->ofld_wait, + (test_bit( + BNX2FC_FLAG_OFLD_REQ_CMPL, + &tgt->flags))); + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&tgt->ofld_timer); +} + static void bnx2fc_offload_session(struct fcoe_port *port, struct bnx2fc_rport *tgt, struct fc_rport_priv *rdata) @@ -103,17 +119,7 @@ retry_ofld: * wait for the session is offloaded and enabled. 3 Secs * should be ample time for this process to complete. */ - setup_timer(&tgt->ofld_timer, bnx2fc_ofld_timer, (unsigned long)tgt); - mod_timer(&tgt->ofld_timer, jiffies + BNX2FC_FW_TIMEOUT); - - wait_event_interruptible(tgt->ofld_wait, - (test_bit( - BNX2FC_FLAG_OFLD_REQ_CMPL, - &tgt->flags))); - if (signal_pending(current)) - flush_signals(current); - - del_timer_sync(&tgt->ofld_timer); + bnx2fc_ofld_wait(tgt); if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) { if (test_and_clear_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE, @@ -131,14 +137,23 @@ retry_ofld: } if (bnx2fc_map_doorbell(tgt)) { printk(KERN_ERR PFX "map doorbell failed - no mem\n"); - /* upload will take care of cleaning up sess resc */ - lport->tt.rport_logoff(rdata); + goto ofld_err; } + clear_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags); + rval = bnx2fc_send_session_enable_req(port, tgt); + if (rval) { + pr_err(PFX "enable session failed\n"); + goto ofld_err; + } + bnx2fc_ofld_wait(tgt); + if (!(test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags))) + goto ofld_err; return; ofld_err: /* couldn't offload the session. log off from this rport */ BNX2FC_TGT_DBG(tgt, "bnx2fc_offload_session - offload error\n"); + clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags); /* Free session resources */ bnx2fc_free_session_resc(hba, tgt); tgt_init_err: @@ -150,8 +165,7 @@ tgt_init_err: void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt) { struct bnx2fc_cmd *io_req; - struct list_head *list; - struct list_head *tmp; + struct bnx2fc_cmd *tmp; int rc; int i = 0; BNX2FC_TGT_DBG(tgt, "Entered flush_active_ios - %d\n", @@ -160,9 +174,8 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt) spin_lock_bh(&tgt->tgt_lock); tgt->flush_in_prog = 1; - list_for_each_safe(list, tmp, &tgt->active_cmd_queue) { + list_for_each_entry_safe(io_req, tmp, &tgt->active_cmd_queue, link) { i++; - io_req = (struct bnx2fc_cmd *)list; list_del_init(&io_req->link); io_req->on_active_queue = 0; BNX2FC_IO_DBG(io_req, "cmd_queue cleanup\n"); @@ -181,13 +194,27 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt) set_bit(BNX2FC_FLAG_IO_COMPL, &io_req->req_flags); set_bit(BNX2FC_FLAG_IO_CLEANUP, &io_req->req_flags); - rc = bnx2fc_initiate_cleanup(io_req); - BUG_ON(rc); + + /* Do not issue cleanup when disable request failed */ + if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags)) + bnx2fc_process_cleanup_compl(io_req, io_req->task, 0); + else { + rc = bnx2fc_initiate_cleanup(io_req); + BUG_ON(rc); + } } - list_for_each_safe(list, tmp, &tgt->els_queue) { + list_for_each_entry_safe(io_req, tmp, &tgt->active_tm_queue, link) { + i++; + list_del_init(&io_req->link); + io_req->on_tmf_queue = 0; + BNX2FC_IO_DBG(io_req, "tm_queue cleanup\n"); + if (io_req->wait_for_comp) + complete(&io_req->tm_done); + } + + list_for_each_entry_safe(io_req, tmp, &tgt->els_queue, link) { i++; - io_req = (struct bnx2fc_cmd *)list; list_del_init(&io_req->link); io_req->on_active_queue = 0; @@ -202,19 +229,32 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt) io_req->cb_arg = NULL; } - rc = bnx2fc_initiate_cleanup(io_req); - BUG_ON(rc); + /* Do not issue cleanup when disable request failed */ + if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags)) + bnx2fc_process_cleanup_compl(io_req, io_req->task, 0); + else { + rc = bnx2fc_initiate_cleanup(io_req); + BUG_ON(rc); + } } - list_for_each_safe(list, tmp, &tgt->io_retire_queue) { + list_for_each_entry_safe(io_req, tmp, &tgt->io_retire_queue, link) { i++; - io_req = (struct bnx2fc_cmd *)list; list_del_init(&io_req->link); BNX2FC_IO_DBG(io_req, "retire_queue flush\n"); - if (cancel_delayed_work(&io_req->timeout_work)) + if (cancel_delayed_work(&io_req->timeout_work)) { + if (test_and_clear_bit(BNX2FC_FLAG_EH_ABORT, + &io_req->req_flags)) { + /* Handle eh_abort timeout */ + BNX2FC_IO_DBG(io_req, "eh_abort for IO " + "in retire_q\n"); + if (io_req->wait_for_comp) + complete(&io_req->tm_done); + } kref_put(&io_req->refcount, bnx2fc_cmd_release); + } clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags); } @@ -234,6 +274,19 @@ void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt) spin_unlock_bh(&tgt->tgt_lock); } +static void bnx2fc_upld_wait(struct bnx2fc_rport *tgt) +{ + setup_timer(&tgt->upld_timer, bnx2fc_upld_timer, (unsigned long)tgt); + mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT); + wait_event_interruptible(tgt->upld_wait, + (test_bit( + BNX2FC_FLAG_UPLD_REQ_COMPL, + &tgt->flags))); + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&tgt->upld_timer); +} + static void bnx2fc_upload_session(struct fcoe_port *port, struct bnx2fc_rport *tgt) { @@ -254,19 +307,8 @@ static void bnx2fc_upload_session(struct fcoe_port *port, * wait for upload to complete. 3 Secs * should be sufficient time for this process to complete. */ - setup_timer(&tgt->upld_timer, bnx2fc_upld_timer, (unsigned long)tgt); - mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT); - BNX2FC_TGT_DBG(tgt, "waiting for disable compl\n"); - wait_event_interruptible(tgt->upld_wait, - (test_bit( - BNX2FC_FLAG_UPLD_REQ_COMPL, - &tgt->flags))); - - if (signal_pending(current)) - flush_signals(current); - - del_timer_sync(&tgt->upld_timer); + bnx2fc_upld_wait(tgt); /* * traverse thru the active_q and tmf_q and cleanup @@ -283,28 +325,21 @@ static void bnx2fc_upload_session(struct fcoe_port *port, bnx2fc_send_session_destroy_req(hba, tgt); /* wait for destroy to complete */ - setup_timer(&tgt->upld_timer, - bnx2fc_upld_timer, (unsigned long)tgt); - mod_timer(&tgt->upld_timer, jiffies + BNX2FC_FW_TIMEOUT); - - wait_event_interruptible(tgt->upld_wait, - (test_bit( - BNX2FC_FLAG_UPLD_REQ_COMPL, - &tgt->flags))); + bnx2fc_upld_wait(tgt); if (!(test_bit(BNX2FC_FLAG_DESTROYED, &tgt->flags))) printk(KERN_ERR PFX "ERROR!! destroy timed out\n"); BNX2FC_TGT_DBG(tgt, "destroy wait complete flags = 0x%lx\n", tgt->flags); - if (signal_pending(current)) - flush_signals(current); - del_timer_sync(&tgt->upld_timer); - - } else + } else if (test_bit(BNX2FC_FLAG_DISABLE_FAILED, &tgt->flags)) { + printk(KERN_ERR PFX "ERROR!! DISABLE req failed, destroy" + " not sent to FW\n"); + } else { printk(KERN_ERR PFX "ERROR!! DISABLE req timed out, destroy" " not sent to FW\n"); + } /* Free session resources */ bnx2fc_free_session_resc(hba, tgt); @@ -351,8 +386,11 @@ static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt, tgt->rq_prod_idx = 0x8000; tgt->rq_cons_idx = 0; atomic_set(&tgt->num_active_ios, 0); + tgt->retry_delay_timestamp = 0; - if (rdata->flags & FC_RP_FLAGS_RETRY) { + if (rdata->flags & FC_RP_FLAGS_RETRY && + rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET && + !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) { tgt->dev_type = TYPE_TAPE; tgt->io_timeout = 0; /* use default ULP timeout */ } else { @@ -450,7 +488,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport, tgt = (struct bnx2fc_rport *)&rp[1]; /* This can happen when ADISC finds the same target */ - if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) { + if (test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags)) { BNX2FC_TGT_DBG(tgt, "already offloaded\n"); mutex_unlock(&hba->hba_mutex); return; @@ -465,11 +503,8 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport, BNX2FC_TGT_DBG(tgt, "OFFLOAD num_ofld_sess = %d\n", hba->num_ofld_sess); - if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) { - /* - * Session is offloaded and enabled. Map - * doorbell register for this target - */ + if (test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags)) { + /* Session is offloaded and enabled. */ BNX2FC_TGT_DBG(tgt, "sess offloaded\n"); /* This counter is protected with hba mutex */ hba->num_ofld_sess++; @@ -506,7 +541,7 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport, */ tgt = (struct bnx2fc_rport *)&rp[1]; - if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) { + if (!(test_bit(BNX2FC_FLAG_ENABLED, &tgt->flags))) { mutex_unlock(&hba->hba_mutex); break; } @@ -639,7 +674,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map SQ */ tgt->sq_mem_size = tgt->max_sqes * BNX2FC_SQ_WQE_SIZE; - tgt->sq_mem_size = (tgt->sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->sq_mem_size = (tgt->sq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->sq = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size, &tgt->sq_dma, GFP_KERNEL); @@ -652,7 +688,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map CQ */ tgt->cq_mem_size = tgt->max_cqes * BNX2FC_CQ_WQE_SIZE; - tgt->cq_mem_size = (tgt->cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->cq_mem_size = (tgt->cq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->cq = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size, &tgt->cq_dma, GFP_KERNEL); @@ -665,7 +702,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map RQ and RQ PBL */ tgt->rq_mem_size = tgt->max_rqes * BNX2FC_RQ_WQE_SIZE; - tgt->rq_mem_size = (tgt->rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->rq_mem_size = (tgt->rq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->rq = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size, &tgt->rq_dma, GFP_KERNEL); @@ -676,8 +714,9 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->rq, 0, tgt->rq_mem_size); - tgt->rq_pbl_size = (tgt->rq_mem_size / PAGE_SIZE) * sizeof(void *); - tgt->rq_pbl_size = (tgt->rq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->rq_pbl_size = (tgt->rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); + tgt->rq_pbl_size = (tgt->rq_pbl_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->rq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size, &tgt->rq_pbl_dma, GFP_KERNEL); @@ -688,7 +727,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->rq_pbl, 0, tgt->rq_pbl_size); - num_pages = tgt->rq_mem_size / PAGE_SIZE; + num_pages = tgt->rq_mem_size / CNIC_PAGE_SIZE; page = tgt->rq_dma; pbl = (u32 *)tgt->rq_pbl; @@ -697,13 +736,13 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, pbl++; *pbl = (u32)((u64)page >> 32); pbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } /* Allocate and map XFERQ */ tgt->xferq_mem_size = tgt->max_sqes * BNX2FC_XFERQ_WQE_SIZE; - tgt->xferq_mem_size = (tgt->xferq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->xferq_mem_size = (tgt->xferq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size, &tgt->xferq_dma, GFP_KERNEL); @@ -716,8 +755,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map CONFQ & CONFQ PBL */ tgt->confq_mem_size = tgt->max_sqes * BNX2FC_CONFQ_WQE_SIZE; - tgt->confq_mem_size = (tgt->confq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->confq_mem_size = (tgt->confq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->confq = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size, &tgt->confq_dma, GFP_KERNEL); @@ -729,9 +768,9 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, memset(tgt->confq, 0, tgt->confq_mem_size); tgt->confq_pbl_size = - (tgt->confq_mem_size / PAGE_SIZE) * sizeof(void *); + (tgt->confq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); tgt->confq_pbl_size = - (tgt->confq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (tgt->confq_pbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; tgt->confq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_pbl_size, @@ -743,7 +782,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->confq_pbl, 0, tgt->confq_pbl_size); - num_pages = tgt->confq_mem_size / PAGE_SIZE; + num_pages = tgt->confq_mem_size / CNIC_PAGE_SIZE; page = tgt->confq_dma; pbl = (u32 *)tgt->confq_pbl; @@ -752,7 +791,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, pbl++; *pbl = (u32)((u64)page >> 32); pbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } /* Allocate and map ConnDB */ @@ -771,8 +810,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map LCQ */ tgt->lcq_mem_size = (tgt->max_sqes + 8) * BNX2FC_SQ_WQE_SIZE; - tgt->lcq_mem_size = (tgt->lcq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->lcq_mem_size = (tgt->lcq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->lcq = dma_alloc_coherent(&hba->pcidev->dev, tgt->lcq_mem_size, &tgt->lcq_dma, GFP_KERNEL); |
