[SCSI] bnx2fc: Broadcom FCoE offload driver

This driver is for Broadcom Netxtreme II 57712 chip. The following
patch contains the driver sources for bnx2fc driver.  libfc/libfcoe
changes to enable bnx2fc have already gone through the fcoe
tree. bnx2fc is a SCSI low level driver that interfaces with SCSI
midlayer, libfc, libfcoe, cnic modules.  bnx2fc driver uses services
of libfc for slow path operations such as FIP and fabric
discovery. The fast path IO perations are performed after offloading
the session information to the underlying FCoE firmware.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Bhanu Gollapudi 2011-02-04 12:10:34 -08:00 коммит произвёл James Bottomley
Родитель d2f809528a
Коммит 853e2bd210
13 изменённых файлов: 9478 добавлений и 0 удалений

Просмотреть файл

@ -381,6 +381,7 @@ config ISCSI_BOOT_SYSFS
source "drivers/scsi/cxgbi/Kconfig"
source "drivers/scsi/bnx2i/Kconfig"
source "drivers/scsi/bnx2fc/Kconfig"
source "drivers/scsi/be2iscsi/Kconfig"
config SGIWD93_SCSI

Просмотреть файл

@ -40,6 +40,7 @@ obj-$(CONFIG_LIBFC) += libfc/
obj-$(CONFIG_LIBFCOE) += fcoe/
obj-$(CONFIG_FCOE) += fcoe/
obj-$(CONFIG_FCOE_FNIC) += fnic/
obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,11 @@
config SCSI_BNX2X_FCOE
tristate "Broadcom NetXtreme II FCoE support"
depends on PCI
select NETDEVICES
select NETDEV_1000
select LIBFC
select LIBFCOE
select CNIC
---help---
This driver supports FCoE offload for the Broadcom NetXtreme II
devices.

Просмотреть файл

@ -0,0 +1,3 @@
obj-$(CONFIG_SCSI_BNX2X_FCOE) += bnx2fc.o
bnx2fc-y := bnx2fc_els.o bnx2fc_fcoe.o bnx2fc_hwi.o bnx2fc_io.o bnx2fc_tgt.o

Просмотреть файл

@ -0,0 +1,511 @@
#ifndef _BNX2FC_H_
#define _BNX2FC_H_
/* bnx2fc.h: Broadcom NetXtreme II Linux FCoE offload driver.
*
* Copyright (c) 2008 - 2010 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/kthread.h>
#include <linux/crc32.h>
#include <linux/cpu.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/io.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_tcq.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
#include <scsi/fc_encode.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fip.h>
#include <scsi/fc/fc_fc2.h>
#include <scsi/fc_frame.h>
#include <scsi/fc/fc_fcoe.h>
#include <scsi/fc/fc_fcp.h>
#include "57xx_hsi_bnx2fc.h"
#include "bnx2fc_debug.h"
#include "../../net/cnic_if.h"
#include "bnx2fc_constants.h"
#define BNX2FC_NAME "bnx2fc"
#define BNX2FC_VERSION "1.0.0"
#define PFX "bnx2fc: "
#define BNX2X_DOORBELL_PCI_BAR 2
#define BNX2FC_MAX_BD_LEN 0xffff
#define BNX2FC_BD_SPLIT_SZ 0x8000
#define BNX2FC_MAX_BDS_PER_CMD 256
#define BNX2FC_SQ_WQES_MAX 256
#define BNX2FC_SCSI_MAX_SQES ((3 * BNX2FC_SQ_WQES_MAX) / 8)
#define BNX2FC_TM_MAX_SQES ((BNX2FC_SQ_WQES_MAX) / 2)
#define BNX2FC_ELS_MAX_SQES (BNX2FC_TM_MAX_SQES - 1)
#define BNX2FC_RQ_WQES_MAX 16
#define BNX2FC_CQ_WQES_MAX (BNX2FC_SQ_WQES_MAX + BNX2FC_RQ_WQES_MAX)
#define BNX2FC_NUM_MAX_SESS 128
#define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS))
#define BNX2FC_MAX_OUTSTANDING_CMNDS 4096
#define BNX2FC_MIN_PAYLOAD 256
#define BNX2FC_MAX_PAYLOAD 2048
#define BNX2FC_RQ_BUF_SZ 256
#define BNX2FC_RQ_BUF_LOG_SZ (ilog2(BNX2FC_RQ_BUF_SZ))
#define BNX2FC_SQ_WQE_SIZE (sizeof(struct fcoe_sqe))
#define BNX2FC_CQ_WQE_SIZE (sizeof(struct fcoe_cqe))
#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 BNX2FC_MAX_TASKS BNX2FC_MAX_OUTSTANDING_CMNDS
#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_READ (1 << 1)
#define BNX2FC_WRITE (1 << 0)
#define BNX2FC_MIN_XID 0
#define BNX2FC_MAX_XID (BNX2FC_MAX_OUTSTANDING_CMNDS - 1)
#define FCOE_MIN_XID (BNX2FC_MAX_OUTSTANDING_CMNDS)
#define FCOE_MAX_XID \
(BNX2FC_MAX_OUTSTANDING_CMNDS + (nr_cpu_ids * 256))
#define BNX2FC_MAX_LUN 0xFFFF
#define BNX2FC_MAX_FCP_TGT 256
#define BNX2FC_MAX_CMD_LEN 16
#define BNX2FC_TM_TIMEOUT 60 /* secs */
#define BNX2FC_IO_TIMEOUT 20000UL /* msecs */
#define BNX2FC_WAIT_CNT 120
#define BNX2FC_FW_TIMEOUT (3 * HZ)
#define PORT_MAX 2
#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
/* FC FCP Status */
#define FC_GOOD 0
#define BNX2FC_RNID_HBA 0x7
/* bnx2fc driver uses only one instance of fcoe_percpu_s */
extern struct fcoe_percpu_s bnx2fc_global;
extern struct workqueue_struct *bnx2fc_wq;
struct bnx2fc_percpu_s {
struct task_struct *iothread;
struct list_head work_list;
spinlock_t fp_work_lock;
};
struct bnx2fc_hba {
struct list_head link;
struct cnic_dev *cnic;
struct pci_dev *pcidev;
struct net_device *netdev;
struct net_device *phys_dev;
unsigned long reg_with_cnic;
#define BNX2FC_CNIC_REGISTERED 1
struct packet_type fcoe_packet_type;
struct packet_type fip_packet_type;
struct bnx2fc_cmd_mgr *cmd_mgr;
struct workqueue_struct *timer_work_queue;
struct kref kref;
spinlock_t hba_lock;
struct mutex hba_mutex;
unsigned long adapter_state;
#define ADAPTER_STATE_UP 0
#define ADAPTER_STATE_GOING_DOWN 1
#define ADAPTER_STATE_LINK_DOWN 2
#define ADAPTER_STATE_READY 3
u32 flags;
unsigned long init_done;
#define BNX2FC_FW_INIT_DONE 0
#define BNX2FC_CTLR_INIT_DONE 1
#define BNX2FC_CREATE_DONE 2
struct fcoe_ctlr ctlr;
u8 vlan_enabled;
int vlan_id;
u32 next_conn_id;
struct fcoe_task_ctx_entry **task_ctx;
dma_addr_t *task_ctx_dma;
struct regpair *task_ctx_bd_tbl;
dma_addr_t task_ctx_bd_dma;
int hash_tbl_segment_count;
void **hash_tbl_segments;
void *hash_tbl_pbl;
dma_addr_t hash_tbl_pbl_dma;
struct fcoe_t2_hash_table_entry *t2_hash_tbl;
dma_addr_t t2_hash_tbl_dma;
char *t2_hash_tbl_ptr;
dma_addr_t t2_hash_tbl_ptr_dma;
char *dummy_buffer;
dma_addr_t dummy_buf_dma;
struct fcoe_statistics_params *stats_buffer;
dma_addr_t stats_buf_dma;
/*
* PCI related info.
*/
u16 pci_did;
u16 pci_vid;
u16 pci_sdid;
u16 pci_svid;
u16 pci_func;
u16 pci_devno;
struct task_struct *l2_thread;
/* linkdown handling */
wait_queue_head_t shutdown_wait;
int wait_for_link_down;
/*destroy handling */
struct timer_list destroy_timer;
wait_queue_head_t destroy_wait;
/* Active list of offloaded sessions */
struct bnx2fc_rport *tgt_ofld_list[BNX2FC_NUM_MAX_SESS];
int num_ofld_sess;
/* statistics */
struct completion stat_req_done;
};
#define bnx2fc_from_ctlr(fip) container_of(fip, struct bnx2fc_hba, ctlr)
struct bnx2fc_cmd_mgr {
struct bnx2fc_hba *hba;
u16 next_idx;
struct list_head *free_list;
spinlock_t *free_list_lock;
struct io_bdt **io_bdt_pool;
struct bnx2fc_cmd **cmds;
};
struct bnx2fc_rport {
struct fcoe_port *port;
struct fc_rport *rport;
struct fc_rport_priv *rdata;
void __iomem *ctx_base;
#define DPM_TRIGER_TYPE 0x40
u32 fcoe_conn_id;
u32 context_id;
u32 sid;
unsigned long flags;
#define BNX2FC_FLAG_SESSION_READY 0x1
#define BNX2FC_FLAG_OFFLOADED 0x2
#define BNX2FC_FLAG_DISABLED 0x3
#define BNX2FC_FLAG_DESTROYED 0x4
#define BNX2FC_FLAG_OFLD_REQ_CMPL 0x5
#define BNX2FC_FLAG_DESTROY_CMPL 0x6
#define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x7
#define BNX2FC_FLAG_UPLD_REQ_COMPL 0x8
#define BNX2FC_FLAG_EXPL_LOGO 0x9
u32 max_sqes;
u32 max_rqes;
u32 max_cqes;
struct fcoe_sqe *sq;
dma_addr_t sq_dma;
u16 sq_prod_idx;
u8 sq_curr_toggle_bit;
u32 sq_mem_size;
struct fcoe_cqe *cq;
dma_addr_t cq_dma;
u32 cq_cons_idx;
u8 cq_curr_toggle_bit;
u32 cq_mem_size;
void *rq;
dma_addr_t rq_dma;
u32 rq_prod_idx;
u32 rq_cons_idx;
u32 rq_mem_size;
void *rq_pbl;
dma_addr_t rq_pbl_dma;
u32 rq_pbl_size;
struct fcoe_xfrqe *xferq;
dma_addr_t xferq_dma;
u32 xferq_mem_size;
struct fcoe_confqe *confq;
dma_addr_t confq_dma;
u32 confq_mem_size;
void *confq_pbl;
dma_addr_t confq_pbl_dma;
u32 confq_pbl_size;
struct fcoe_conn_db *conn_db;
dma_addr_t conn_db_dma;
u32 conn_db_mem_size;
struct fcoe_sqe *lcq;
dma_addr_t lcq_dma;
u32 lcq_mem_size;
void *ofld_req[4];
dma_addr_t ofld_req_dma[4];
void *enbl_req;
dma_addr_t enbl_req_dma;
spinlock_t tgt_lock;
spinlock_t cq_lock;
atomic_t num_active_ios;
u32 flush_in_prog;
unsigned long work_time_slice;
unsigned long timestamp;
struct list_head free_task_list;
struct bnx2fc_cmd *pending_queue[BNX2FC_SQ_WQES_MAX+1];
atomic_t pi;
atomic_t ci;
struct list_head active_cmd_queue;
struct list_head els_queue;
struct list_head io_retire_queue;
struct list_head active_tm_queue;
struct timer_list ofld_timer;
wait_queue_head_t ofld_wait;
struct timer_list upld_timer;
wait_queue_head_t upld_wait;
};
struct bnx2fc_mp_req {
u8 tm_flags;
u32 req_len;
void *req_buf;
dma_addr_t req_buf_dma;
struct fcoe_bd_ctx *mp_req_bd;
dma_addr_t mp_req_bd_dma;
struct fc_frame_header req_fc_hdr;
u32 resp_len;
void *resp_buf;
dma_addr_t resp_buf_dma;
struct fcoe_bd_ctx *mp_resp_bd;
dma_addr_t mp_resp_bd_dma;
struct fc_frame_header resp_fc_hdr;
};
struct bnx2fc_els_cb_arg {
struct bnx2fc_cmd *aborted_io_req;
struct bnx2fc_cmd *io_req;
u16 l2_oxid;
};
/* bnx2fc command structure */
struct bnx2fc_cmd {
struct list_head link;
u8 on_active_queue;
u8 on_tmf_queue;
u8 cmd_type;
#define BNX2FC_SCSI_CMD 1
#define BNX2FC_TASK_MGMT_CMD 2
#define BNX2FC_ABTS 3
#define BNX2FC_ELS 4
#define BNX2FC_CLEANUP 5
u8 io_req_flags;
struct kref refcount;
struct fcoe_port *port;
struct bnx2fc_rport *tgt;
struct scsi_cmnd *sc_cmd;
struct bnx2fc_cmd_mgr *cmd_mgr;
struct bnx2fc_mp_req mp_req;
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg);
struct bnx2fc_els_cb_arg *cb_arg;
struct delayed_work timeout_work; /* timer for ULP timeouts */
struct completion tm_done;
int wait_for_comp;
u16 xid;
struct fcoe_task_ctx_entry *task;
struct io_bdt *bd_tbl;
struct fcp_rsp *rsp;
size_t data_xfer_len;
unsigned long req_flags;
#define BNX2FC_FLAG_ISSUE_RRQ 0x1
#define BNX2FC_FLAG_ISSUE_ABTS 0x2
#define BNX2FC_FLAG_ABTS_DONE 0x3
#define BNX2FC_FLAG_TM_COMPL 0x4
#define BNX2FC_FLAG_TM_TIMEOUT 0x5
#define BNX2FC_FLAG_IO_CLEANUP 0x6
#define BNX2FC_FLAG_RETIRE_OXID 0x7
#define BNX2FC_FLAG_EH_ABORT 0x8
#define BNX2FC_FLAG_IO_COMPL 0x9
#define BNX2FC_FLAG_ELS_DONE 0xa
#define BNX2FC_FLAG_ELS_TIMEOUT 0xb
u32 fcp_resid;
u32 fcp_rsp_len;
u32 fcp_sns_len;
u8 cdb_status; /* SCSI IO status */
u8 fcp_status; /* FCP IO status */
u8 fcp_rsp_code;
u8 scsi_comp_flags;
};
struct io_bdt {
struct bnx2fc_cmd *io_req;
struct fcoe_bd_ctx *bd_tbl;
dma_addr_t bd_tbl_dma;
u16 bd_valid;
};
struct bnx2fc_work {
struct list_head list;
struct bnx2fc_rport *tgt;
u16 wqe;
};
struct bnx2fc_unsol_els {
struct fc_lport *lport;
struct fc_frame *fp;
struct work_struct unsol_els_work;
};
struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type);
void bnx2fc_cmd_release(struct kref *ref);
int bnx2fc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd);
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_disable_req(struct fcoe_port *port,
struct bnx2fc_rport *tgt);
int bnx2fc_send_session_destroy_req(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
int bnx2fc_map_doorbell(struct bnx2fc_rport *tgt);
void bnx2fc_indicate_kcqe(void *context, struct kcqe *kcq[],
u32 num_cqe);
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);
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);
void bnx2fc_return_rqe(struct bnx2fc_rport *tgt, u8 num_items);
int bnx2fc_get_paged_crc_eof(struct sk_buff *skb, int tlen);
int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req);
int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp);
int bnx2fc_initiate_cleanup(struct bnx2fc_cmd *io_req);
int bnx2fc_initiate_abts(struct bnx2fc_cmd *io_req);
void bnx2fc_cmd_timer_set(struct bnx2fc_cmd *io_req,
unsigned int timer_msec);
int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req);
void bnx2fc_init_cleanup_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u16 orig_xid);
void bnx2fc_init_mp_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task);
void bnx2fc_init_task(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task);
void bnx2fc_add_2_sq(struct bnx2fc_rport *tgt, u16 xid);
void bnx2fc_ring_doorbell(struct bnx2fc_rport *tgt);
int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_target_reset(struct scsi_cmnd *sc_cmd);
int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
void bnx2fc_rport_event_handler(struct fc_lport *lport,
struct fc_rport_priv *rport,
enum fc_rport_event event);
void bnx2fc_process_scsi_cmd_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_cleanup_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_abts_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_tm_compl(struct bnx2fc_cmd *io_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
struct fcoe_task_ctx_entry *task,
u8 num_rq);
void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req,
struct fcp_cmnd *fcp_cmnd);
void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt);
struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
struct fc_frame *fp, unsigned int op,
void (*resp)(struct fc_seq *,
struct fc_frame *,
void *),
void *arg, u32 timeout);
int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt);
void bnx2fc_process_cq_compl(struct bnx2fc_rport *tgt, u16 wqe);
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id);
void bnx2fc_process_l2_frame_compl(struct bnx2fc_rport *tgt,
unsigned char *buf,
u32 frame_len, u16 l2_oxid);
int bnx2fc_send_stat_req(struct bnx2fc_hba *hba);
#endif

Просмотреть файл

@ -0,0 +1,206 @@
#ifndef __BNX2FC_CONSTANTS_H_
#define __BNX2FC_CONSTANTS_H_
/**
* This file defines HSI constants for the FCoE flows
*/
/* KWQ/KCQ FCoE layer code */
#define FCOE_KWQE_LAYER_CODE (7)
/* KWQ (kernel work queue) request op codes */
#define FCOE_KWQE_OPCODE_INIT1 (0)
#define FCOE_KWQE_OPCODE_INIT2 (1)
#define FCOE_KWQE_OPCODE_INIT3 (2)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN1 (3)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN2 (4)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN3 (5)
#define FCOE_KWQE_OPCODE_OFFLOAD_CONN4 (6)
#define FCOE_KWQE_OPCODE_ENABLE_CONN (7)
#define FCOE_KWQE_OPCODE_DISABLE_CONN (8)
#define FCOE_KWQE_OPCODE_DESTROY_CONN (9)
#define FCOE_KWQE_OPCODE_DESTROY (10)
#define FCOE_KWQE_OPCODE_STAT (11)
/* KCQ (kernel completion queue) response op codes */
#define FCOE_KCQE_OPCODE_INIT_FUNC (0x10)
#define FCOE_KCQE_OPCODE_DESTROY_FUNC (0x11)
#define FCOE_KCQE_OPCODE_STAT_FUNC (0x12)
#define FCOE_KCQE_OPCODE_OFFLOAD_CONN (0x15)
#define FCOE_KCQE_OPCODE_ENABLE_CONN (0x16)
#define FCOE_KCQE_OPCODE_DISABLE_CONN (0x17)
#define FCOE_KCQE_OPCODE_DESTROY_CONN (0x18)
#define FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
#define FCOE_KCQE_OPCODE_FCOE_ERROR (0x21)
/* KCQ (kernel completion queue) completion status */
#define FCOE_KCQE_COMPLETION_STATUS_SUCCESS (0x0)
#define FCOE_KCQE_COMPLETION_STATUS_ERROR (0x1)
#define FCOE_KCQE_COMPLETION_STATUS_INVALID_OPCODE (0x2)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3)
#define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4)
#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5)
/* Unsolicited CQE type */
#define FCOE_UNSOLICITED_FRAME_CQE_TYPE 0
#define FCOE_ERROR_DETECTION_CQE_TYPE 1
#define FCOE_WARNING_DETECTION_CQE_TYPE 2
/* Task context constants */
/* After driver has initialize the task in case timer services required */
#define FCOE_TASK_TX_STATE_INIT 0
/* In case timer services are required then shall be updated by Xstorm after
* start processing the task. In case no timer facilities are required then the
* driver would initialize the state to this value */
#define FCOE_TASK_TX_STATE_NORMAL 1
/* Task is under abort procedure. Updated in order to stop processing of
* pending WQEs on this task */
#define FCOE_TASK_TX_STATE_ABORT 2
/* For E_D_T_TOV timer expiration in Xstorm (Class 2 only) */
#define FCOE_TASK_TX_STATE_ERROR 3
/* For REC_TOV timer expiration indication received from Xstorm */
#define FCOE_TASK_TX_STATE_WARNING 4
/* For completed unsolicited task */
#define FCOE_TASK_TX_STATE_UNSOLICITED_COMPLETED 5
/* For exchange cleanup request task */
#define FCOE_TASK_TX_STATE_EXCHANGE_CLEANUP 6
/* For sequence cleanup request task */
#define FCOE_TASK_TX_STATE_SEQUENCE_CLEANUP 7
/* Mark task as aborted and indicate that ABTS was not transmitted */
#define FCOE_TASK_TX_STATE_BEFORE_ABTS_TX 8
/* Mark task as aborted and indicate that ABTS was transmitted */
#define FCOE_TASK_TX_STATE_AFTER_ABTS_TX 9
/* For completion the ABTS task. */
#define FCOE_TASK_TX_STATE_ABTS_TX_COMPLETED 10
/* Mark task as aborted and indicate that Exchange cleanup was not transmitted
*/
#define FCOE_TASK_TX_STATE_BEFORE_EXCHANGE_CLEANUP_TX 11
/* Mark task as aborted and indicate that Exchange cleanup was transmitted */
#define FCOE_TASK_TX_STATE_AFTER_EXCHANGE_CLEANUP_TX 12
#define FCOE_TASK_RX_STATE_NORMAL 0
#define FCOE_TASK_RX_STATE_COMPLETED 1
/* Obsolete: Intermediate completion (middle path with local completion) */
#define FCOE_TASK_RX_STATE_INTER_COMP 2
/* For REC_TOV timer expiration indication received from Xstorm */
#define FCOE_TASK_RX_STATE_WARNING 3
/* For E_D_T_TOV timer expiration in Ustorm */
#define FCOE_TASK_RX_STATE_ERROR 4
/* ABTS ACC arrived wait for local completion to finally complete the task. */
#define FCOE_TASK_RX_STATE_ABTS_ACC_ARRIVED 5
/* local completion arrived wait for ABTS ACC to finally complete the task. */
#define FCOE_TASK_RX_STATE_ABTS_LOCAL_COMP_ARRIVED 6
/* Special completion indication in case of task was aborted. */
#define FCOE_TASK_RX_STATE_ABTS_COMPLETED 7
/* Special completion indication in case of task was cleaned. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_COMPLETED 8
/* Special completion indication (in task requested the exchange cleanup) in
* case cleaned task is in non-valid. */
#define FCOE_TASK_RX_STATE_ABORT_CLEANUP_COMPLETED 9
/* Special completion indication (in task requested the sequence cleanup) in
* case cleaned task was already returned to normal. */
#define FCOE_TASK_RX_STATE_IGNORED_SEQUENCE_CLEANUP 10
/* Exchange cleanup arrived wait until xfer will be handled to finally
* complete the task. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_ARRIVED 11
/* Xfer handled, wait for exchange cleanup to finally complete the task. */
#define FCOE_TASK_RX_STATE_EXCHANGE_CLEANUP_HANDLED_XFER 12
#define FCOE_TASK_TYPE_WRITE 0
#define FCOE_TASK_TYPE_READ 1
#define FCOE_TASK_TYPE_MIDPATH 2
#define FCOE_TASK_TYPE_UNSOLICITED 3
#define FCOE_TASK_TYPE_ABTS 4
#define FCOE_TASK_TYPE_EXCHANGE_CLEANUP 5
#define FCOE_TASK_TYPE_SEQUENCE_CLEANUP 6
#define FCOE_TASK_DEV_TYPE_DISK 0
#define FCOE_TASK_DEV_TYPE_TAPE 1
#define FCOE_TASK_CLASS_TYPE_3 0
#define FCOE_TASK_CLASS_TYPE_2 1
/* Everest FCoE connection type */
#define B577XX_FCOE_CONNECTION_TYPE 4
/* Error codes for Error Reporting in fast path flows */
/* XFER error codes */
#define FCOE_ERROR_CODE_XFER_OOO_RO 0
#define FCOE_ERROR_CODE_XFER_RO_NOT_ALIGNED 1
#define FCOE_ERROR_CODE_XFER_NULL_BURST_LEN 2
#define FCOE_ERROR_CODE_XFER_RO_GREATER_THAN_DATA2TRNS 3
#define FCOE_ERROR_CODE_XFER_INVALID_PAYLOAD_SIZE 4
#define FCOE_ERROR_CODE_XFER_TASK_TYPE_NOT_WRITE 5
#define FCOE_ERROR_CODE_XFER_PEND_XFER_SET 6
#define FCOE_ERROR_CODE_XFER_OPENED_SEQ 7
#define FCOE_ERROR_CODE_XFER_FCTL 8
/* FCP RSP error codes */
#define FCOE_ERROR_CODE_FCP_RSP_BIDI_FLAGS_SET 9
#define FCOE_ERROR_CODE_FCP_RSP_UNDERFLOW 10
#define FCOE_ERROR_CODE_FCP_RSP_OVERFLOW 11
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_LENGTH_FIELD 12
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_SNS_FIELD 13
#define FCOE_ERROR_CODE_FCP_RSP_INVALID_PAYLOAD_SIZE 14
#define FCOE_ERROR_CODE_FCP_RSP_PEND_XFER_SET 15
#define FCOE_ERROR_CODE_FCP_RSP_OPENED_SEQ 16
#define FCOE_ERROR_CODE_FCP_RSP_FCTL 17
#define FCOE_ERROR_CODE_FCP_RSP_LAST_SEQ_RESET 18
#define FCOE_ERROR_CODE_FCP_RSP_CONF_REQ_NOT_SUPPORTED_YET 19
/* FCP DATA error codes */
#define FCOE_ERROR_CODE_DATA_OOO_RO 20
#define FCOE_ERROR_CODE_DATA_EXCEEDS_DEFINED_MAX_FRAME_SIZE 21
#define FCOE_ERROR_CODE_DATA_EXCEEDS_DATA2TRNS 22
#define FCOE_ERROR_CODE_DATA_SOFI3_SEQ_ACTIVE_SET 23
#define FCOE_ERROR_CODE_DATA_SOFN_SEQ_ACTIVE_RESET 24
#define FCOE_ERROR_CODE_DATA_EOFN_END_SEQ_SET 25
#define FCOE_ERROR_CODE_DATA_EOFT_END_SEQ_RESET 26
#define FCOE_ERROR_CODE_DATA_TASK_TYPE_NOT_READ 27
#define FCOE_ERROR_CODE_DATA_FCTL 28
/* Middle path error codes */
#define FCOE_ERROR_CODE_MIDPATH_TYPE_NOT_ELS 29
#define FCOE_ERROR_CODE_MIDPATH_SOFI3_SEQ_ACTIVE_SET 30
#define FCOE_ERROR_CODE_MIDPATH_SOFN_SEQ_ACTIVE_RESET 31
#define FCOE_ERROR_CODE_MIDPATH_EOFN_END_SEQ_SET 32
#define FCOE_ERROR_CODE_MIDPATH_EOFT_END_SEQ_RESET 33
#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_FCTL 34
#define FCOE_ERROR_CODE_MIDPATH_INVALID_REPLY 35
#define FCOE_ERROR_CODE_MIDPATH_ELS_REPLY_RCTL 36
/* ABTS error codes */
#define FCOE_ERROR_CODE_ABTS_REPLY_F_CTL 37
#define FCOE_ERROR_CODE_ABTS_REPLY_DDF_RCTL_FIELD 38
#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_BLS_RCTL 39
#define FCOE_ERROR_CODE_ABTS_REPLY_INVALID_RCTL 40
#define FCOE_ERROR_CODE_ABTS_REPLY_RCTL_GENERAL_MISMATCH 41
/* Common error codes */
#define FCOE_ERROR_CODE_COMMON_MIDDLE_FRAME_WITH_PAD 42
#define FCOE_ERROR_CODE_COMMON_SEQ_INIT_IN_TCE 43
#define FCOE_ERROR_CODE_COMMON_FC_HDR_RX_ID_MISMATCH 44
#define FCOE_ERROR_CODE_COMMON_INCORRECT_SEQ_CNT 45
#define FCOE_ERROR_CODE_COMMON_DATA_FC_HDR_FCP_TYPE_MISMATCH 46
#define FCOE_ERROR_CODE_COMMON_DATA_NO_MORE_SGES 47
#define FCOE_ERROR_CODE_COMMON_OPTIONAL_FC_HDR 48
#define FCOE_ERROR_CODE_COMMON_READ_TCE_OX_ID_TOO_BIG 49
#define FCOE_ERROR_CODE_COMMON_DATA_WAS_NOT_TRANSMITTED 50
/* Unsolicited Rx error codes */
#define FCOE_ERROR_CODE_UNSOLICITED_TYPE_NOT_ELS 51
#define FCOE_ERROR_CODE_UNSOLICITED_TYPE_NOT_BLS 52
#define FCOE_ERROR_CODE_UNSOLICITED_FCTL_ELS 53
#define FCOE_ERROR_CODE_UNSOLICITED_FCTL_BLS 54
#define FCOE_ERROR_CODE_UNSOLICITED_R_CTL 55
#define FCOE_ERROR_CODE_RW_TASK_DDF_RCTL_INFO_FIELD 56
#define FCOE_ERROR_CODE_RW_TASK_INVALID_RCTL 57
#define FCOE_ERROR_CODE_RW_TASK_RCTL_GENERAL_MISMATCH 58
/* Timer error codes */
#define FCOE_ERROR_CODE_E_D_TOV_TIMER_EXPIRATION 60
#define FCOE_ERROR_CODE_REC_TOV_TIMER_EXPIRATION 61
#endif /* BNX2FC_CONSTANTS_H_ */

Просмотреть файл

@ -0,0 +1,70 @@
#ifndef __BNX2FC_DEBUG__
#define __BNX2FC_DEBUG__
/* Log level bit mask */
#define LOG_IO 0x01 /* scsi cmd error, cleanup */
#define LOG_TGT 0x02 /* Session setup, cleanup, etc' */
#define LOG_HBA 0x04 /* lport events, link, mtu, etc' */
#define LOG_ELS 0x08 /* ELS logs */
#define LOG_MISC 0x10 /* fcoe L2 frame related logs*/
#define LOG_ALL 0xff /* LOG all messages */
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_ALERT PFX fmt, ##arg))
#define BNX2FC_MISC_DBG(fmt, arg...) \
BNX2FC_CHK_LOGGING(LOG_MISC, \
printk(KERN_ALERT 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_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_IO, \
shost_printk(KERN_ALERT, \
(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_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_TGT, \
shost_printk(KERN_ALERT, \
(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_ALERT PFX "NULL " fmt, ##arg)); \
else \
BNX2FC_CHK_LOGGING(LOG_HBA, \
shost_printk(KERN_ALERT, lport->host, \
PFX fmt, ##arg)); \
} while (0)
#endif

Просмотреть файл

@ -0,0 +1,515 @@
/*
* bnx2fc_els.c: Broadcom NetXtreme II Linux FCoE offload driver.
* This file contains helper routines that handle ELS requests
* and responses.
*
* Copyright (c) 2008 - 2010 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include "bnx2fc.h"
static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg);
static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg);
static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
void *data, u32 data_len,
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec);
static void bnx2fc_rrq_compl(struct bnx2fc_els_cb_arg *cb_arg)
{
struct bnx2fc_cmd *orig_io_req;
struct bnx2fc_cmd *rrq_req;
int rc = 0;
BUG_ON(!cb_arg);
rrq_req = cb_arg->io_req;
orig_io_req = cb_arg->aborted_io_req;
BUG_ON(!orig_io_req);
BNX2FC_ELS_DBG("rrq_compl: orig xid = 0x%x, rrq_xid = 0x%x\n",
orig_io_req->xid, rrq_req->xid);
kref_put(&orig_io_req->refcount, bnx2fc_cmd_release);
if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rrq_req->req_flags)) {
/*
* els req is timed out. cleanup the IO with FW and
* drop the completion. Remove from active_cmd_queue.
*/
BNX2FC_ELS_DBG("rrq xid - 0x%x timed out, clean it up\n",
rrq_req->xid);
if (rrq_req->on_active_queue) {
list_del_init(&rrq_req->link);
rrq_req->on_active_queue = 0;
rc = bnx2fc_initiate_cleanup(rrq_req);
BUG_ON(rc);
}
}
kfree(cb_arg);
}
int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req)
{
struct fc_els_rrq rrq;
struct bnx2fc_rport *tgt = aborted_io_req->tgt;
struct fc_lport *lport = tgt->rdata->local_port;
struct bnx2fc_els_cb_arg *cb_arg = NULL;
u32 sid = tgt->sid;
u32 r_a_tov = lport->r_a_tov;
unsigned long start = jiffies;
int rc;
BNX2FC_ELS_DBG("Sending RRQ orig_xid = 0x%x\n",
aborted_io_req->xid);
memset(&rrq, 0, sizeof(rrq));
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_NOIO);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for RRQ\n");
rc = -ENOMEM;
goto rrq_err;
}
cb_arg->aborted_io_req = aborted_io_req;
rrq.rrq_cmd = ELS_RRQ;
hton24(rrq.rrq_s_id, sid);
rrq.rrq_ox_id = htons(aborted_io_req->xid);
rrq.rrq_rx_id = htons(aborted_io_req->task->rx_wr_tx_rd.rx_id);
retry_rrq:
rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq),
bnx2fc_rrq_compl, cb_arg,
r_a_tov);
if (rc == -ENOMEM) {
if (time_after(jiffies, start + (10 * HZ))) {
BNX2FC_ELS_DBG("rrq Failed\n");
rc = FAILED;
goto rrq_err;
}
msleep(20);
goto retry_rrq;
}
rrq_err:
if (rc) {
BNX2FC_ELS_DBG("RRQ failed - release orig io req 0x%x\n",
aborted_io_req->xid);
kfree(cb_arg);
spin_lock_bh(&tgt->tgt_lock);
kref_put(&aborted_io_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
}
return rc;
}
static void bnx2fc_l2_els_compl(struct bnx2fc_els_cb_arg *cb_arg)
{
struct bnx2fc_cmd *els_req;
struct bnx2fc_rport *tgt;
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
unsigned char *buf;
void *resp_buf;
u32 resp_len, hdr_len;
u16 l2_oxid;
int frame_len;
int rc = 0;
l2_oxid = cb_arg->l2_oxid;
BNX2FC_ELS_DBG("ELS COMPL - l2_oxid = 0x%x\n", l2_oxid);
els_req = cb_arg->io_req;
if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &els_req->req_flags)) {
/*
* els req is timed out. cleanup the IO with FW and
* drop the completion. libfc will handle the els timeout
*/
if (els_req->on_active_queue) {
list_del_init(&els_req->link);
els_req->on_active_queue = 0;
rc = bnx2fc_initiate_cleanup(els_req);
BUG_ON(rc);
}
goto free_arg;
}
tgt = els_req->tgt;
mp_req = &(els_req->mp_req);
fc_hdr = &(mp_req->resp_fc_hdr);
resp_len = mp_req->resp_len;
resp_buf = mp_req->resp_buf;
buf = kzalloc(PAGE_SIZE, GFP_ATOMIC);
if (!buf) {
printk(KERN_ERR PFX "Unable to alloc mp buf\n");
goto free_arg;
}
hdr_len = sizeof(*fc_hdr);
if (hdr_len + resp_len > PAGE_SIZE) {
printk(KERN_ERR PFX "l2_els_compl: resp len is "
"beyond page size\n");
goto free_buf;
}
memcpy(buf, fc_hdr, hdr_len);
memcpy(buf + hdr_len, resp_buf, resp_len);
frame_len = hdr_len + resp_len;
bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, l2_oxid);
free_buf:
kfree(buf);
free_arg:
kfree(cb_arg);
}
int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_adisc *adisc;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for ADISC\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
BNX2FC_ELS_DBG("send ADISC: l2_oxid = 0x%x\n", cb_arg->l2_oxid);
adisc = fc_frame_payload_get(fp, sizeof(*adisc));
/* adisc is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_ADISC, adisc, sizeof(*adisc),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_logo *logo;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
BNX2FC_ELS_DBG("Send LOGO: l2_oxid = 0x%x\n", cb_arg->l2_oxid);
logo = fc_frame_payload_get(fp, sizeof(*logo));
/* logo is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_LOGO, logo, sizeof(*logo),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp)
{
struct fc_els_rls *rls;
struct fc_frame_header *fh;
struct bnx2fc_els_cb_arg *cb_arg;
struct fc_lport *lport = tgt->rdata->local_port;
u32 r_a_tov = lport->r_a_tov;
int rc;
fh = fc_frame_header_get(fp);
cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC);
if (!cb_arg) {
printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n");
return -ENOMEM;
}
cb_arg->l2_oxid = ntohs(fh->fh_ox_id);
rls = fc_frame_payload_get(fp, sizeof(*rls));
/* rls is initialized by libfc */
rc = bnx2fc_initiate_els(tgt, ELS_RLS, rls, sizeof(*rls),
bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov);
if (rc)
kfree(cb_arg);
return rc;
}
static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op,
void *data, u32 data_len,
void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg),
struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec)
{
struct fcoe_port *port = tgt->port;
struct bnx2fc_hba *hba = port->priv;
struct fc_rport *rport = tgt->rport;
struct fc_lport *lport = port->lport;
struct bnx2fc_cmd *els_req;
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
struct fcoe_task_ctx_entry *task;
struct fcoe_task_ctx_entry *task_page;
int rc = 0;
int task_idx, index;
u32 did, sid;
u16 xid;
rc = fc_remote_port_chkready(rport);
if (rc) {
printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op);
rc = -EINVAL;
goto els_err;
}
if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op);
rc = -EINVAL;
goto els_err;
}
if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) ||
(test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) {
printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op);
rc = -EINVAL;
goto els_err;
}
els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS);
if (!els_req) {
rc = -ENOMEM;
goto els_err;
}
els_req->sc_cmd = NULL;
els_req->port = port;
els_req->tgt = tgt;
els_req->cb_func = cb_func;
cb_arg->io_req = els_req;
els_req->cb_arg = cb_arg;
mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req);
rc = bnx2fc_init_mp_req(els_req);
if (rc == FAILED) {
printk(KERN_ALERT PFX "ELS MP request init failed\n");
spin_lock_bh(&tgt->tgt_lock);
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
rc = -ENOMEM;
goto els_err;
} else {
/* rc SUCCESS */
rc = 0;
}
/* Set the data_xfer_len to the size of ELS payload */
mp_req->req_len = data_len;
els_req->data_xfer_len = mp_req->req_len;
/* Fill ELS Payload */
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) {
memcpy(mp_req->req_buf, data, data_len);
} else {
printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op);
els_req->cb_func = NULL;
els_req->cb_arg = NULL;
spin_lock_bh(&tgt->tgt_lock);
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
rc = -EINVAL;
}
if (rc)
goto els_err;
/* Fill FC header */
fc_hdr = &(mp_req->req_fc_hdr);
did = tgt->rport->port_id;
sid = tgt->sid;
__fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid,
FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ |
FC_FC_SEQ_INIT, 0);
/* Obtain exchange id */
xid = els_req->xid;
task_idx = xid/BNX2FC_TASKS_PER_PAGE;
index = xid % BNX2FC_TASKS_PER_PAGE;
/* Initialize task context for this IO request */
task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx];
task = &(task_page[index]);
bnx2fc_init_mp_task(els_req, task);
spin_lock_bh(&tgt->tgt_lock);
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
printk(KERN_ERR PFX "initiate_els.. session not ready\n");
els_req->cb_func = NULL;
els_req->cb_arg = NULL;
kref_put(&els_req->refcount, bnx2fc_cmd_release);
spin_unlock_bh(&tgt->tgt_lock);
return -EINVAL;
}
if (timer_msec)
bnx2fc_cmd_timer_set(els_req, timer_msec);
bnx2fc_add_2_sq(tgt, xid);
els_req->on_active_queue = 1;
list_add_tail(&els_req->link, &tgt->els_queue);
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
spin_unlock_bh(&tgt->tgt_lock);
els_err:
return rc;
}
void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req,
struct fcoe_task_ctx_entry *task, u8 num_rq)
{
struct bnx2fc_mp_req *mp_req;
struct fc_frame_header *fc_hdr;
u64 *hdr;
u64 *temp_hdr;
BNX2FC_ELS_DBG("Entered process_els_compl xid = 0x%x"
"cmd_type = %d\n", els_req->xid, els_req->cmd_type);
if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE,
&els_req->req_flags)) {
BNX2FC_ELS_DBG("Timer context finished processing this "
"els - 0x%x\n", els_req->xid);
/* This IO doesnt receive cleanup completion */
kref_put(&els_req->refcount, bnx2fc_cmd_release);
return;
}
/* Cancel the timeout_work, as we received the response */
if (cancel_delayed_work(&els_req->timeout_work))
kref_put(&els_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
if (els_req->on_active_queue) {
list_del_init(&els_req->link);
els_req->on_active_queue = 0;
}
mp_req = &(els_req->mp_req);
fc_hdr = &(mp_req->resp_fc_hdr);
hdr = (u64 *)fc_hdr;
temp_hdr = (u64 *)
&task->cmn.general.cmd_info.mp_fc_frame.fc_hdr;
hdr[0] = cpu_to_be64(temp_hdr[0]);
hdr[1] = cpu_to_be64(temp_hdr[1]);
hdr[2] = cpu_to_be64(temp_hdr[2]);
mp_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off;
/* Parse ELS response */
if ((els_req->cb_func) && (els_req->cb_arg)) {
els_req->cb_func(els_req->cb_arg);
els_req->cb_arg = NULL;
}
kref_put(&els_req->refcount, bnx2fc_cmd_release);
}
static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg)
{
struct fcoe_ctlr *fip = arg;
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))
goto done;
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) {
printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n");
fc_vport_terminate(lport->vport);
fc_frame_free(fp);
return;
}
}
if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
fc_frame_free(fp);
return;
}
}
fip->update_mac(lport, mac);
done:
fc_lport_flogi_resp(seq, fp, lport);
}
static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp,
void *arg)
{
struct fcoe_ctlr *fip = arg;
struct fc_exch *exch = fc_seq_exch(seq);
struct fc_lport *lport = exch->lp;
static u8 zero_mac[ETH_ALEN] = { 0 };
if (!IS_ERR(fp))
fip->update_mac(lport, zero_mac);
fc_lport_logo_resp(seq, fp, lport);
}
struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did,
struct fc_frame *fp, unsigned int op,
void (*resp)(struct fc_seq *,
struct fc_frame *,
void *),
void *arg, u32 timeout)
{
struct fcoe_port *port = lport_priv(lport);
struct bnx2fc_hba *hba = port->priv;
struct fcoe_ctlr *fip = &hba->ctlr;
struct fc_frame_header *fh = fc_frame_header_get(fp);
switch (op) {
case ELS_FLOGI:
case ELS_FDISC:
return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp,
fip, timeout);
case ELS_LOGO:
/* only hook onto fabric logouts, not port logouts */
if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
break;
return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp,
fip, timeout);
}
return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,844 @@
/* bnx2fc_tgt.c: Broadcom NetXtreme II Linux FCoE offload driver.
* Handles operations such as session offload/upload etc, and manages
* session resources such as connection id and qp resources.
*
* Copyright (c) 2008 - 2010 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
*/
#include "bnx2fc.h"
static void bnx2fc_upld_timer(unsigned long data);
static void bnx2fc_ofld_timer(unsigned long data);
static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
struct fcoe_port *port,
struct fc_rport_priv *rdata);
static u32 bnx2fc_alloc_conn_id(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt);
static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id);
static void bnx2fc_upld_timer(unsigned long data)
{
struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
BNX2FC_TGT_DBG(tgt, "upld_timer - Upload compl not received!!\n");
/* fake upload completion */
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
set_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
wake_up_interruptible(&tgt->upld_wait);
}
static void bnx2fc_ofld_timer(unsigned long data)
{
struct bnx2fc_rport *tgt = (struct bnx2fc_rport *)data;
BNX2FC_TGT_DBG(tgt, "entered bnx2fc_ofld_timer\n");
/* NOTE: This function should never be called, as
* offload should never timeout
*/
/*
* If the timer has expired, this session is dead
* Clear offloaded flag and logout of this device.
* Since OFFLOADED flag is cleared, this case
* will be considered as offload error and the
* port will be logged off, and conn_id, session
* resources are freed up in bnx2fc_offload_session
*/
clear_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags);
set_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
wake_up_interruptible(&tgt->ofld_wait);
}
static void bnx2fc_offload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt,
struct fc_rport_priv *rdata)
{
struct fc_lport *lport = rdata->local_port;
struct fc_rport *rport = rdata->rport;
struct bnx2fc_hba *hba = port->priv;
int rval;
int i = 0;
/* Initialize bnx2fc_rport */
/* NOTE: tgt is already bzero'd */
rval = bnx2fc_init_tgt(tgt, port, rdata);
if (rval) {
printk(KERN_ERR PFX "Failed to allocate conn id for "
"port_id (%6x)\n", rport->port_id);
goto ofld_err;
}
/* Allocate session resources */
rval = bnx2fc_alloc_session_resc(hba, tgt);
if (rval) {
printk(KERN_ERR PFX "Failed to allocate resources\n");
goto ofld_err;
}
/*
* Initialize FCoE session offload process.
* Upon completion of offload process add
* rport to list of rports
*/
retry_ofld:
clear_bit(BNX2FC_FLAG_OFLD_REQ_CMPL, &tgt->flags);
rval = bnx2fc_send_session_ofld_req(port, tgt);
if (rval) {
printk(KERN_ERR PFX "ofld_req failed\n");
goto ofld_err;
}
/*
* 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);
if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
if (test_and_clear_bit(BNX2FC_FLAG_CTX_ALLOC_FAILURE,
&tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "ctx_alloc_failure, "
"retry ofld..%d\n", i++);
msleep_interruptible(1000);
if (i > 3) {
i = 0;
goto ofld_err;
}
goto retry_ofld;
}
goto ofld_err;
}
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);
}
return;
ofld_err:
/* couldn't offload the session. log off from this rport */
BNX2FC_TGT_DBG(tgt, "bnx2fc_offload_session - offload error\n");
lport->tt.rport_logoff(rdata);
/* Free session resources */
bnx2fc_free_session_resc(hba, tgt);
if (tgt->fcoe_conn_id != -1)
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
}
void bnx2fc_flush_active_ios(struct bnx2fc_rport *tgt)
{
struct bnx2fc_cmd *io_req;
struct list_head *list;
struct list_head *tmp;
int rc;
int i = 0;
BNX2FC_TGT_DBG(tgt, "Entered flush_active_ios - %d\n",
tgt->num_active_ios.counter);
spin_lock_bh(&tgt->tgt_lock);
tgt->flush_in_prog = 1;
list_for_each_safe(list, tmp, &tgt->active_cmd_queue) {
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");
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 "
"cleaned up\n");
complete(&io_req->tm_done);
}
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
}
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);
}
list_for_each_safe(list, tmp, &tgt->els_queue) {
i++;
io_req = (struct bnx2fc_cmd *)list;
list_del_init(&io_req->link);
io_req->on_active_queue = 0;
BNX2FC_IO_DBG(io_req, "els_queue cleanup\n");
if (cancel_delayed_work(&io_req->timeout_work))
kref_put(&io_req->refcount,
bnx2fc_cmd_release); /* drop timer hold */
if ((io_req->cb_func) && (io_req->cb_arg)) {
io_req->cb_func(io_req->cb_arg);
io_req->cb_arg = NULL;
}
rc = bnx2fc_initiate_cleanup(io_req);
BUG_ON(rc);
}
list_for_each_safe(list, tmp, &tgt->io_retire_queue) {
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))
kref_put(&io_req->refcount, bnx2fc_cmd_release);
clear_bit(BNX2FC_FLAG_ISSUE_RRQ, &io_req->req_flags);
}
BNX2FC_TGT_DBG(tgt, "IOs flushed = %d\n", i);
i = 0;
spin_unlock_bh(&tgt->tgt_lock);
/* wait for active_ios to go to 0 */
while ((tgt->num_active_ios.counter != 0) && (i++ < BNX2FC_WAIT_CNT))
msleep(25);
if (tgt->num_active_ios.counter != 0)
printk(KERN_ERR PFX "CLEANUP on port 0x%x:"
" active_ios = %d\n",
tgt->rdata->ids.port_id, tgt->num_active_ios.counter);
spin_lock_bh(&tgt->tgt_lock);
tgt->flush_in_prog = 0;
spin_unlock_bh(&tgt->tgt_lock);
}
static void bnx2fc_upload_session(struct fcoe_port *port,
struct bnx2fc_rport *tgt)
{
struct bnx2fc_hba *hba = port->priv;
BNX2FC_TGT_DBG(tgt, "upload_session: active_ios = %d\n",
tgt->num_active_ios.counter);
/*
* Called with hba->hba_mutex held.
* This is a blocking call
*/
clear_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
bnx2fc_send_session_disable_req(port, tgt);
/*
* 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);
/*
* traverse thru the active_q and tmf_q and cleanup
* IOs in these lists
*/
BNX2FC_TGT_DBG(tgt, "flush/upload - disable wait flags = 0x%lx\n",
tgt->flags);
bnx2fc_flush_active_ios(tgt);
/* Issue destroy KWQE */
if (test_bit(BNX2FC_FLAG_DISABLED, &tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "send destroy req\n");
clear_bit(BNX2FC_FLAG_UPLD_REQ_COMPL, &tgt->flags);
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)));
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
printk(KERN_ERR PFX "ERROR!! DISABLE req timed out, destroy"
" not sent to FW\n");
/* Free session resources */
spin_lock_bh(&tgt->cq_lock);
bnx2fc_free_session_resc(hba, tgt);
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
spin_unlock_bh(&tgt->cq_lock);
}
static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt,
struct fcoe_port *port,
struct fc_rport_priv *rdata)
{
struct fc_rport *rport = rdata->rport;
struct bnx2fc_hba *hba = port->priv;
tgt->rport = rport;
tgt->rdata = rdata;
tgt->port = port;
if (hba->num_ofld_sess >= BNX2FC_NUM_MAX_SESS) {
BNX2FC_TGT_DBG(tgt, "exceeded max sessions. logoff this tgt\n");
tgt->fcoe_conn_id = -1;
return -1;
}
tgt->fcoe_conn_id = bnx2fc_alloc_conn_id(hba, tgt);
if (tgt->fcoe_conn_id == -1)
return -1;
BNX2FC_TGT_DBG(tgt, "init_tgt - conn_id = 0x%x\n", tgt->fcoe_conn_id);
tgt->max_sqes = BNX2FC_SQ_WQES_MAX;
tgt->max_rqes = BNX2FC_RQ_WQES_MAX;
tgt->max_cqes = BNX2FC_CQ_WQES_MAX;
/* Initialize the toggle bit */
tgt->sq_curr_toggle_bit = 1;
tgt->cq_curr_toggle_bit = 1;
tgt->sq_prod_idx = 0;
tgt->cq_cons_idx = 0;
tgt->rq_prod_idx = 0x8000;
tgt->rq_cons_idx = 0;
atomic_set(&tgt->num_active_ios, 0);
tgt->work_time_slice = 2;
spin_lock_init(&tgt->tgt_lock);
spin_lock_init(&tgt->cq_lock);
/* Initialize active_cmd_queue list */
INIT_LIST_HEAD(&tgt->active_cmd_queue);
/* Initialize IO retire queue */
INIT_LIST_HEAD(&tgt->io_retire_queue);
INIT_LIST_HEAD(&tgt->els_queue);
/* Initialize active_tm_queue list */
INIT_LIST_HEAD(&tgt->active_tm_queue);
init_waitqueue_head(&tgt->ofld_wait);
init_waitqueue_head(&tgt->upld_wait);
return 0;
}
/**
* This event_callback is called after successful completion of libfc
* initiated target login. bnx2fc can proceed with initiating the session
* establishment.
*/
void bnx2fc_rport_event_handler(struct fc_lport *lport,
struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
struct fcoe_port *port = lport_priv(lport);
struct bnx2fc_hba *hba = port->priv;
struct fc_rport *rport = rdata->rport;
struct fc_rport_libfc_priv *rp;
struct bnx2fc_rport *tgt;
u32 port_id;
BNX2FC_HBA_DBG(lport, "rport_event_hdlr: event = %d, port_id = 0x%x\n",
event, rdata->ids.port_id);
switch (event) {
case RPORT_EV_READY:
if (!rport) {
printk(KERN_ALERT PFX "rport is NULL: ERROR!\n");
break;
}
rp = rport->dd_data;
if (rport->port_id == FC_FID_DIR_SERV) {
/*
* bnx2fc_rport structure doesnt exist for
* directory server.
* We should not come here, as lport will
* take care of fabric login
*/
printk(KERN_ALERT PFX "%x - rport_event_handler ERROR\n",
rdata->ids.port_id);
break;
}
if (rdata->spp_type != FC_TYPE_FCP) {
BNX2FC_HBA_DBG(lport, "not FCP type target."
" not offloading\n");
break;
}
if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
BNX2FC_HBA_DBG(lport, "not FCP_TARGET"
" not offloading\n");
break;
}
/*
* Offlaod process is protected with hba mutex.
* Use the same mutex_lock for upload process too
*/
mutex_lock(&hba->hba_mutex);
tgt = (struct bnx2fc_rport *)&rp[1];
/* This can happen when ADISC finds the same target */
if (test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags)) {
BNX2FC_TGT_DBG(tgt, "already offloaded\n");
mutex_unlock(&hba->hba_mutex);
return;
}
/*
* Offload the session. This is a blocking call, and will
* wait until the session is offloaded.
*/
bnx2fc_offload_session(port, tgt, rdata);
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
*/
BNX2FC_TGT_DBG(tgt, "sess offloaded\n");
/* This counter is protected with hba mutex */
hba->num_ofld_sess++;
set_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
} else {
/*
* Offload or enable would have failed.
* In offload/enable completion path, the
* rport would have already been removed
*/
BNX2FC_TGT_DBG(tgt, "Port is being logged off as "
"offloaded flag not set\n");
}
mutex_unlock(&hba->hba_mutex);
break;
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
port_id = rdata->ids.port_id;
if (port_id == FC_FID_DIR_SERV)
break;
if (!rport) {
printk(KERN_ALERT PFX "%x - rport not created Yet!!\n",
port_id);
break;
}
rp = rport->dd_data;
mutex_lock(&hba->hba_mutex);
/*
* Perform session upload. Note that rdata->peers is already
* removed from disc->rports list before we get this event.
*/
tgt = (struct bnx2fc_rport *)&rp[1];
if (!(test_bit(BNX2FC_FLAG_OFFLOADED, &tgt->flags))) {
mutex_unlock(&hba->hba_mutex);
break;
}
clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags);
bnx2fc_upload_session(port, tgt);
hba->num_ofld_sess--;
BNX2FC_TGT_DBG(tgt, "UPLOAD num_ofld_sess = %d\n",
hba->num_ofld_sess);
/*
* Try to wake up the linkdown wait thread. If num_ofld_sess
* is 0, the waiting therad wakes up
*/
if ((hba->wait_for_link_down) &&
(hba->num_ofld_sess == 0)) {
wake_up_interruptible(&hba->shutdown_wait);
}
if (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags)) {
printk(KERN_ERR PFX "Relogin to the tgt\n");
mutex_lock(&lport->disc.disc_mutex);
lport->tt.rport_login(rdata);
mutex_unlock(&lport->disc.disc_mutex);
}
mutex_unlock(&hba->hba_mutex);
break;
case RPORT_EV_NONE:
break;
}
}
/**
* bnx2fc_tgt_lookup() - Lookup a bnx2fc_rport by port_id
*
* @port: fcoe_port struct to lookup the target port on
* @port_id: The remote port ID to look up
*/
struct bnx2fc_rport *bnx2fc_tgt_lookup(struct fcoe_port *port,
u32 port_id)
{
struct bnx2fc_hba *hba = port->priv;
struct bnx2fc_rport *tgt;
struct fc_rport_priv *rdata;
int i;
for (i = 0; i < BNX2FC_NUM_MAX_SESS; i++) {
tgt = hba->tgt_ofld_list[i];
if ((tgt) && (tgt->port == port)) {
rdata = tgt->rdata;
if (rdata->ids.port_id == port_id) {
if (rdata->rp_state != RPORT_ST_DELETE) {
BNX2FC_TGT_DBG(tgt, "rport "
"obtained\n");
return tgt;
} else {
printk(KERN_ERR PFX "rport 0x%x "
"is in DELETED state\n",
rdata->ids.port_id);
return NULL;
}
}
}
}
return NULL;
}
/**
* bnx2fc_alloc_conn_id - allocates FCOE Connection id
*
* @hba: pointer to adapter structure
* @tgt: pointer to bnx2fc_rport structure
*/
static u32 bnx2fc_alloc_conn_id(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
u32 conn_id, next;
/* called with hba mutex held */
/*
* tgt_ofld_list access is synchronized using
* both hba mutex and hba lock. Atleast hba mutex or
* hba lock needs to be held for read access.
*/
spin_lock_bh(&hba->hba_lock);
next = hba->next_conn_id;
conn_id = hba->next_conn_id++;
if (hba->next_conn_id == BNX2FC_NUM_MAX_SESS)
hba->next_conn_id = 0;
while (hba->tgt_ofld_list[conn_id] != NULL) {
conn_id++;
if (conn_id == BNX2FC_NUM_MAX_SESS)
conn_id = 0;
if (conn_id == next) {
/* No free conn_ids are available */
spin_unlock_bh(&hba->hba_lock);
return -1;
}
}
hba->tgt_ofld_list[conn_id] = tgt;
tgt->fcoe_conn_id = conn_id;
spin_unlock_bh(&hba->hba_lock);
return conn_id;
}
static void bnx2fc_free_conn_id(struct bnx2fc_hba *hba, u32 conn_id)
{
/* called with hba mutex held */
spin_lock_bh(&hba->hba_lock);
hba->tgt_ofld_list[conn_id] = NULL;
hba->next_conn_id = conn_id;
spin_unlock_bh(&hba->hba_lock);
}
/**
*bnx2fc_alloc_session_resc - Allocate qp resources for the session
*
*/
static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
dma_addr_t page;
int num_pages;
u32 *pbl;
/* 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
&tgt->sq_dma, GFP_KERNEL);
if (!tgt->sq) {
printk(KERN_ALERT PFX "unable to allocate SQ memory %d\n",
tgt->sq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->sq, 0, tgt->sq_mem_size);
/* 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
&tgt->cq_dma, GFP_KERNEL);
if (!tgt->cq) {
printk(KERN_ALERT PFX "unable to allocate CQ memory %d\n",
tgt->cq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->cq, 0, tgt->cq_mem_size);
/* 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
&tgt->rq_dma, GFP_KERNEL);
if (!tgt->rq) {
printk(KERN_ALERT PFX "unable to allocate RQ memory %d\n",
tgt->rq_mem_size);
goto mem_alloc_failure;
}
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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
&tgt->rq_pbl_dma, GFP_KERNEL);
if (!tgt->rq_pbl) {
printk(KERN_ALERT PFX "unable to allocate RQ PBL %d\n",
tgt->rq_pbl_size);
goto mem_alloc_failure;
}
memset(tgt->rq_pbl, 0, tgt->rq_pbl_size);
num_pages = tgt->rq_mem_size / PAGE_SIZE;
page = tgt->rq_dma;
pbl = (u32 *)tgt->rq_pbl;
while (num_pages--) {
*pbl = (u32)page;
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
page += 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
&tgt->xferq_dma, GFP_KERNEL);
if (!tgt->xferq) {
printk(KERN_ALERT PFX "unable to allocate XFERQ %d\n",
tgt->xferq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->xferq, 0, tgt->xferq_mem_size);
/* 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
&tgt->confq_dma, GFP_KERNEL);
if (!tgt->confq) {
printk(KERN_ALERT PFX "unable to allocate CONFQ %d\n",
tgt->confq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->confq, 0, tgt->confq_mem_size);
tgt->confq_pbl_size =
(tgt->confq_mem_size / PAGE_SIZE) * sizeof(void *);
tgt->confq_pbl_size =
(tgt->confq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK;
tgt->confq_pbl = dma_alloc_coherent(&hba->pcidev->dev,
tgt->confq_pbl_size,
&tgt->confq_pbl_dma, GFP_KERNEL);
if (!tgt->confq_pbl) {
printk(KERN_ALERT PFX "unable to allocate CONFQ PBL %d\n",
tgt->confq_pbl_size);
goto mem_alloc_failure;
}
memset(tgt->confq_pbl, 0, tgt->confq_pbl_size);
num_pages = tgt->confq_mem_size / PAGE_SIZE;
page = tgt->confq_dma;
pbl = (u32 *)tgt->confq_pbl;
while (num_pages--) {
*pbl = (u32)page;
pbl++;
*pbl = (u32)((u64)page >> 32);
pbl++;
page += PAGE_SIZE;
}
/* Allocate and map ConnDB */
tgt->conn_db_mem_size = sizeof(struct fcoe_conn_db);
tgt->conn_db = dma_alloc_coherent(&hba->pcidev->dev,
tgt->conn_db_mem_size,
&tgt->conn_db_dma, GFP_KERNEL);
if (!tgt->conn_db) {
printk(KERN_ALERT PFX "unable to allocate conn_db %d\n",
tgt->conn_db_mem_size);
goto mem_alloc_failure;
}
memset(tgt->conn_db, 0, tgt->conn_db_mem_size);
/* 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 = dma_alloc_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
&tgt->lcq_dma, GFP_KERNEL);
if (!tgt->lcq) {
printk(KERN_ALERT PFX "unable to allocate lcq %d\n",
tgt->lcq_mem_size);
goto mem_alloc_failure;
}
memset(tgt->lcq, 0, tgt->lcq_mem_size);
/* Arm CQ */
tgt->conn_db->cq_arm.lo = -1;
tgt->conn_db->rq_prod = 0x8000;
return 0;
mem_alloc_failure:
bnx2fc_free_session_resc(hba, tgt);
bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id);
return -ENOMEM;
}
/**
* bnx2i_free_session_resc - free qp resources for the session
*
* @hba: adapter structure pointer
* @tgt: bnx2fc_rport structure pointer
*
* Free QP resources - SQ/RQ/CQ/XFERQ memory and PBL
*/
static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
struct bnx2fc_rport *tgt)
{
BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n");
if (tgt->ctx_base) {
iounmap(tgt->ctx_base);
tgt->ctx_base = NULL;
}
/* Free LCQ */
if (tgt->lcq) {
dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
tgt->lcq, tgt->lcq_dma);
tgt->lcq = NULL;
}
/* Free connDB */
if (tgt->conn_db) {
dma_free_coherent(&hba->pcidev->dev, tgt->conn_db_mem_size,
tgt->conn_db, tgt->conn_db_dma);
tgt->conn_db = NULL;
}
/* Free confq and confq pbl */
if (tgt->confq_pbl) {
dma_free_coherent(&hba->pcidev->dev, tgt->confq_pbl_size,
tgt->confq_pbl, tgt->confq_pbl_dma);
tgt->confq_pbl = NULL;
}
if (tgt->confq) {
dma_free_coherent(&hba->pcidev->dev, tgt->confq_mem_size,
tgt->confq, tgt->confq_dma);
tgt->confq = NULL;
}
/* Free XFERQ */
if (tgt->xferq) {
dma_free_coherent(&hba->pcidev->dev, tgt->xferq_mem_size,
tgt->xferq, tgt->xferq_dma);
tgt->xferq = NULL;
}
/* Free RQ PBL and RQ */
if (tgt->rq_pbl) {
dma_free_coherent(&hba->pcidev->dev, tgt->rq_pbl_size,
tgt->rq_pbl, tgt->rq_pbl_dma);
tgt->rq_pbl = NULL;
}
if (tgt->rq) {
dma_free_coherent(&hba->pcidev->dev, tgt->rq_mem_size,
tgt->rq, tgt->rq_dma);
tgt->rq = NULL;
}
/* Free CQ */
if (tgt->cq) {
dma_free_coherent(&hba->pcidev->dev, tgt->cq_mem_size,
tgt->cq, tgt->cq_dma);
tgt->cq = NULL;
}
/* Free SQ */
if (tgt->sq) {
dma_free_coherent(&hba->pcidev->dev, tgt->sq_mem_size,
tgt->sq, tgt->sq_dma);
tgt->sq = NULL;
}
}