diff options
Diffstat (limited to 'drivers/scsi/bnx2i')
-rw-r--r-- | drivers/scsi/bnx2i/57xx_iscsi_constants.h | 161 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/57xx_iscsi_hsi.h | 1526 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/Kconfig | 13 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/Makefile | 3 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/bnx2i.h | 882 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/bnx2i_hwi.c | 2771 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/bnx2i_init.c | 604 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/bnx2i_iscsi.c | 2305 | ||||
-rw-r--r-- | drivers/scsi/bnx2i/bnx2i_sysfs.c | 145 |
9 files changed, 8410 insertions, 0 deletions
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h new file mode 100644 index 000000000..917534109 --- /dev/null +++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h @@ -0,0 +1,161 @@ +/* 57xx_iscsi_constants.h: QLogic NetXtreme II iSCSI HSI + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ +#ifndef __57XX_ISCSI_CONSTANTS_H_ +#define __57XX_ISCSI_CONSTANTS_H_ + +/** +* This file defines HSI constants for the iSCSI flows +*/ + +/* iSCSI request op codes */ +#define ISCSI_OPCODE_CLEANUP_REQUEST (7) + +/* iSCSI response/messages op codes */ +#define ISCSI_OPCODE_CLEANUP_RESPONSE (0x27) +#define ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION (0) + +/* iSCSI task types */ +#define ISCSI_TASK_TYPE_READ (0) +#define ISCSI_TASK_TYPE_WRITE (1) +#define ISCSI_TASK_TYPE_MPATH (2) + +/* initial CQ sequence numbers */ +#define ISCSI_INITIAL_SN (1) + +/* KWQ (kernel work queue) layer codes */ +#define ISCSI_KWQE_LAYER_CODE (6) + +/* KWQ (kernel work queue) request op codes */ +#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN1 (0) +#define ISCSI_KWQE_OPCODE_OFFLOAD_CONN2 (1) +#define ISCSI_KWQE_OPCODE_UPDATE_CONN (2) +#define ISCSI_KWQE_OPCODE_DESTROY_CONN (3) +#define ISCSI_KWQE_OPCODE_INIT1 (4) +#define ISCSI_KWQE_OPCODE_INIT2 (5) + +/* KCQ (kernel completion queue) response op codes */ +#define ISCSI_KCQE_OPCODE_OFFLOAD_CONN (0x10) +#define ISCSI_KCQE_OPCODE_UPDATE_CONN (0x12) +#define ISCSI_KCQE_OPCODE_DESTROY_CONN (0x13) +#define ISCSI_KCQE_OPCODE_INIT (0x14) +#define ISCSI_KCQE_OPCODE_FW_CLEAN_TASK (0x15) +#define ISCSI_KCQE_OPCODE_TCP_RESET (0x16) +#define ISCSI_KCQE_OPCODE_TCP_SYN (0x17) +#define ISCSI_KCQE_OPCODE_TCP_FIN (0X18) +#define ISCSI_KCQE_OPCODE_TCP_ERROR (0x19) +#define ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20) +#define ISCSI_KCQE_OPCODE_ISCSI_ERROR (0x21) + +/* KCQ (kernel completion queue) completion status */ +#define ISCSI_KCQE_COMPLETION_STATUS_SUCCESS (0x0) +#define ISCSI_KCQE_COMPLETION_STATUS_INVALID_OPCODE (0x1) +#define ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x2) +#define ISCSI_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x3) +#define ISCSI_KCQE_COMPLETION_STATUS_NIC_ERROR (0x4) + +#define ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR (0x5) +#define ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR (0x6) + +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_UNEXPECTED_OPCODE (0xa) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE (0xb) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN (0xc) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT (0xd) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN (0xe) + +/* Response */ +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN (0xf) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T (0x10) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_IS_ZERO (0x2c) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_TOO_BIG (0x2d) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0 (0x11) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1 (0x12) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2 (0x13) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3 (0x14) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4 (0x15) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5 (0x16) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6 (0x17) + +/* Data-In */ +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN (0x18) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN (0x19) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO (0x1a) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV (0x1b) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN (0x1c) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN (0x1d) + +/* R2T */ +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF (0x1f) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN (0x20) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN (0x21) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0 (0x22) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1 (0x23) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED (0x24) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV (0x25) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN (0x26) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO (0x27) + +/* TMF */ +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN (0x28) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN (0x29) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN (0x2a) +#define ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP (0x2b) + +/* IP/TCP processing errors: */ +#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT (0x40) +#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS (0x41) +#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG (0x42) +#define ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_MAX_RTRANS (0x43) + +/* iSCSI licensing errors */ +/* general iSCSI license not installed */ +#define ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED (0x50) +/* additional LOM specific iSCSI license not installed */ +#define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51) + +#define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80) +#define ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR (0x81) + +/* SQ/RQ/CQ DB structure sizes */ +#define ISCSI_SQ_DB_SIZE (16) +#define ISCSI_RQ_DB_SIZE (64) +#define ISCSI_CQ_DB_SIZE (80) + +#define ISCSI_SQN_TO_NOTIFY_NOT_VALID 0xFFFF + +/* Page size codes (for flags field in connection offload request) */ +#define ISCSI_PAGE_SIZE_256 (0) +#define ISCSI_PAGE_SIZE_512 (1) +#define ISCSI_PAGE_SIZE_1K (2) +#define ISCSI_PAGE_SIZE_2K (3) +#define ISCSI_PAGE_SIZE_4K (4) +#define ISCSI_PAGE_SIZE_8K (5) +#define ISCSI_PAGE_SIZE_16K (6) +#define ISCSI_PAGE_SIZE_32K (7) +#define ISCSI_PAGE_SIZE_64K (8) +#define ISCSI_PAGE_SIZE_128K (9) +#define ISCSI_PAGE_SIZE_256K (10) +#define ISCSI_PAGE_SIZE_512K (11) +#define ISCSI_PAGE_SIZE_1M (12) +#define ISCSI_PAGE_SIZE_2M (13) +#define ISCSI_PAGE_SIZE_4M (14) +#define ISCSI_PAGE_SIZE_8M (15) + +/* Iscsi PDU related defines */ +#define ISCSI_HEADER_SIZE (48) +#define ISCSI_DIGEST_SHIFT (2) +#define ISCSI_DIGEST_SIZE (4) + +#define B577XX_ISCSI_CONNECTION_TYPE 3 + +#endif /*__57XX_ISCSI_CONSTANTS_H_ */ diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h new file mode 100644 index 000000000..19b3a97db --- /dev/null +++ b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h @@ -0,0 +1,1526 @@ +/* 57xx_iscsi_hsi.h: QLogic NetXtreme II iSCSI HSI. + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ +#ifndef __57XX_ISCSI_HSI_LINUX_LE__ +#define __57XX_ISCSI_HSI_LINUX_LE__ + +/* + * iSCSI Async CQE + */ +struct bnx2i_async_msg { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 reserved1; + u8 op_code; +#endif + u32 reserved2; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved3[2]; +#if defined(__BIG_ENDIAN) + u16 reserved5; + u8 err_code; + u8 reserved4; +#elif defined(__LITTLE_ENDIAN) + u8 reserved4; + u8 err_code; + u16 reserved5; +#endif + u32 reserved6; + u32 lun[2]; +#if defined(__BIG_ENDIAN) + u8 async_event; + u8 async_vcode; + u16 param1; +#elif defined(__LITTLE_ENDIAN) + u16 param1; + u8 async_vcode; + u8 async_event; +#endif +#if defined(__BIG_ENDIAN) + u16 param2; + u16 param3; +#elif defined(__LITTLE_ENDIAN) + u16 param3; + u16 param2; +#endif + u32 reserved7[3]; + u32 cq_req_sn; +}; + + +/* + * iSCSI Buffer Descriptor (BD) + */ +struct iscsi_bd { + u32 buffer_addr_hi; + u32 buffer_addr_lo; +#if defined(__BIG_ENDIAN) + u16 reserved0; + u16 buffer_length; +#elif defined(__LITTLE_ENDIAN) + u16 buffer_length; + u16 reserved0; +#endif +#if defined(__BIG_ENDIAN) + u16 reserved3; + u16 flags; +#define ISCSI_BD_RESERVED1 (0x3F<<0) +#define ISCSI_BD_RESERVED1_SHIFT 0 +#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6) +#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6 +#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7) +#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7 +#define ISCSI_BD_RESERVED2 (0xFF<<8) +#define ISCSI_BD_RESERVED2_SHIFT 8 +#elif defined(__LITTLE_ENDIAN) + u16 flags; +#define ISCSI_BD_RESERVED1 (0x3F<<0) +#define ISCSI_BD_RESERVED1_SHIFT 0 +#define ISCSI_BD_LAST_IN_BD_CHAIN (0x1<<6) +#define ISCSI_BD_LAST_IN_BD_CHAIN_SHIFT 6 +#define ISCSI_BD_FIRST_IN_BD_CHAIN (0x1<<7) +#define ISCSI_BD_FIRST_IN_BD_CHAIN_SHIFT 7 +#define ISCSI_BD_RESERVED2 (0xFF<<8) +#define ISCSI_BD_RESERVED2_SHIFT 8 + u16 reserved3; +#endif +}; + + +/* + * iSCSI Cleanup SQ WQE + */ +struct bnx2i_cleanup_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 reserved1; + u8 op_code; +#endif + u32 reserved2[3]; +#if defined(__BIG_ENDIAN) + u16 reserved3; + u16 itt; +#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0 +#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14) +#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_CLEANUP_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_CLEANUP_REQUEST_INDEX_SHIFT 0 +#define ISCSI_CLEANUP_REQUEST_TYPE (0x3<<14) +#define ISCSI_CLEANUP_REQUEST_TYPE_SHIFT 14 + u16 reserved3; +#endif + u32 reserved4[10]; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved6; + u16 reserved5; +#elif defined(__LITTLE_ENDIAN) + u16 reserved5; + u8 reserved6; + u8 cq_index; +#endif +}; + + +/* + * iSCSI Cleanup CQE + */ +struct bnx2i_cleanup_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 status; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 status; + u8 op_code; +#endif + u32 reserved1[3]; + u32 reserved2[2]; +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 err_code; + u8 reserved3; +#elif defined(__LITTLE_ENDIAN) + u8 reserved3; + u8 err_code; + u16 reserved4; +#endif + u32 reserved5[7]; +#if defined(__BIG_ENDIAN) + u16 reserved6; + u16 itt; +#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14) +#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_CLEANUP_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_CLEANUP_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_CLEANUP_RESPONSE_TYPE (0x3<<14) +#define ISCSI_CLEANUP_RESPONSE_TYPE_SHIFT 14 + u16 reserved6; +#endif + u32 cq_req_sn; +}; + + +/* + * SCSI read/write SQ WQE + */ +struct bnx2i_cmd_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0) +#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0 +#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3) +#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3 +#define ISCSI_CMD_REQUEST_WRITE (0x1<<5) +#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5 +#define ISCSI_CMD_REQUEST_READ (0x1<<6) +#define ISCSI_CMD_REQUEST_READ_SHIFT 6 +#define ISCSI_CMD_REQUEST_FINAL (0x1<<7) +#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_attr; +#define ISCSI_CMD_REQUEST_TASK_ATTR (0x7<<0) +#define ISCSI_CMD_REQUEST_TASK_ATTR_SHIFT 0 +#define ISCSI_CMD_REQUEST_RESERVED1 (0x3<<3) +#define ISCSI_CMD_REQUEST_RESERVED1_SHIFT 3 +#define ISCSI_CMD_REQUEST_WRITE (0x1<<5) +#define ISCSI_CMD_REQUEST_WRITE_SHIFT 5 +#define ISCSI_CMD_REQUEST_READ (0x1<<6) +#define ISCSI_CMD_REQUEST_READ_SHIFT 6 +#define ISCSI_CMD_REQUEST_FINAL (0x1<<7) +#define ISCSI_CMD_REQUEST_FINAL_SHIFT 7 + u8 op_code; +#endif +#if defined(__BIG_ENDIAN) + u16 ud_buffer_offset; + u16 sd_buffer_offset; +#elif defined(__LITTLE_ENDIAN) + u16 sd_buffer_offset; + u16 ud_buffer_offset; +#endif + u32 lun[2]; +#if defined(__BIG_ENDIAN) + u16 reserved2; + u16 itt; +#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0 +#define ISCSI_CMD_REQUEST_TYPE (0x3<<14) +#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_CMD_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_CMD_REQUEST_INDEX_SHIFT 0 +#define ISCSI_CMD_REQUEST_TYPE (0x3<<14) +#define ISCSI_CMD_REQUEST_TYPE_SHIFT 14 + u16 reserved2; +#endif + u32 total_data_transfer_length; + u32 cmd_sn; + u32 reserved3; + u32 cdb[4]; + u32 zero_fill; + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 sd_start_bd_index; + u8 ud_start_bd_index; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 ud_start_bd_index; + u8 sd_start_bd_index; + u8 cq_index; +#endif +}; + + +/* + * task statistics for write response + */ +struct bnx2i_write_resp_task_stat { +#if defined(__BIG_ENDIAN) + u16 num_r2ts; + u16 num_data_outs; +#elif defined(__LITTLE_ENDIAN) + u16 num_data_outs; + u16 num_r2ts; +#endif +}; + +/* + * task statistics for read response + */ +struct bnx2i_read_resp_task_stat { +#if defined(__BIG_ENDIAN) + u16 reserved; + u16 num_data_ins; +#elif defined(__LITTLE_ENDIAN) + u16 num_data_ins; + u16 reserved; +#endif +}; + +/* + * task statistics for iSCSI cmd response + */ +union bnx2i_cmd_resp_task_stat { + struct bnx2i_write_resp_task_stat write_stat; + struct bnx2i_read_resp_task_stat read_stat; +}; + +/* + * SCSI Command CQE + */ +struct bnx2i_cmd_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 response_flags; +#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0) +#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0 +#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1) +#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1 +#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2) +#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2 +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3) +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3 +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4) +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4 +#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5) +#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5 + u8 response; + u8 status; +#elif defined(__LITTLE_ENDIAN) + u8 status; + u8 response; + u8 response_flags; +#define ISCSI_CMD_RESPONSE_RESERVED0 (0x1<<0) +#define ISCSI_CMD_RESPONSE_RESERVED0_SHIFT 0 +#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW (0x1<<1) +#define ISCSI_CMD_RESPONSE_RESIDUAL_UNDERFLOW_SHIFT 1 +#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW (0x1<<2) +#define ISCSI_CMD_RESPONSE_RESIDUAL_OVERFLOW_SHIFT 2 +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW (0x1<<3) +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_UNDERFLOW_SHIFT 3 +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW (0x1<<4) +#define ISCSI_CMD_RESPONSE_BR_RESIDUAL_OVERFLOW_SHIFT 4 +#define ISCSI_CMD_RESPONSE_RESERVED1 (0x7<<5) +#define ISCSI_CMD_RESPONSE_RESERVED1_SHIFT 5 + u8 op_code; +#endif + u32 data_length; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved2; + u32 residual_count; +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 err_code; + u8 reserved3; +#elif defined(__LITTLE_ENDIAN) + u8 reserved3; + u8 err_code; + u16 reserved4; +#endif + u32 reserved5[5]; + union bnx2i_cmd_resp_task_stat task_stat; + u32 reserved6; +#if defined(__BIG_ENDIAN) + u16 reserved7; + u16 itt; +#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14) +#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_CMD_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_CMD_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_CMD_RESPONSE_TYPE (0x3<<14) +#define ISCSI_CMD_RESPONSE_TYPE_SHIFT 14 + u16 reserved7; +#endif + u32 cq_req_sn; +}; + + + +/* + * firmware middle-path request SQ WQE + */ +struct bnx2i_fw_mp_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; + u16 hdr_opaque1; +#elif defined(__LITTLE_ENDIAN) + u16 hdr_opaque1; + u8 op_attr; + u8 op_code; +#endif + u32 data_length; + u32 hdr_opaque2[2]; +#if defined(__BIG_ENDIAN) + u16 reserved0; + u16 itt; +#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0 +#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14) +#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_FW_MP_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_FW_MP_REQUEST_INDEX_SHIFT 0 +#define ISCSI_FW_MP_REQUEST_TYPE (0x3<<14) +#define ISCSI_FW_MP_REQUEST_TYPE_SHIFT 14 + u16 reserved0; +#endif + u32 hdr_opaque3[4]; + u32 resp_bd_list_addr_lo; + u32 resp_bd_list_addr_hi; + u32 resp_buffer; +#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0) +#define ISCSI_FW_MP_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0 +#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS (0xFF<<24) +#define ISCSI_FW_MP_REQUEST_NUM_RESP_BDS_SHIFT 24 +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 reserved3; + u8 flags; +#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0) +#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1) +#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1 +#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2) +#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2 +#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3) +#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3 +#elif defined(__LITTLE_ENDIAN) + u8 flags; +#define ISCSI_FW_MP_REQUEST_RESERVED1 (0x1<<0) +#define ISCSI_FW_MP_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION (0x1<<1) +#define ISCSI_FW_MP_REQUEST_LOCAL_COMPLETION_SHIFT 1 +#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2) +#define ISCSI_FW_MP_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2 +#define ISCSI_FW_MP_REQUEST_RESERVED2 (0x1F<<3) +#define ISCSI_FW_MP_REQUEST_RESERVED2_SHIFT 3 + u8 reserved3; + u16 reserved4; +#endif + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved6; + u8 reserved5; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved5; + u8 reserved6; + u8 cq_index; +#endif +}; + + +/* + * firmware response - CQE: used only by firmware + */ +struct bnx2i_fw_response { + u32 hdr_dword1[2]; + u32 hdr_exp_cmd_sn; + u32 hdr_max_cmd_sn; + u32 hdr_ttt; + u32 hdr_res_cnt; + u32 cqe_flags; +#define ISCSI_FW_RESPONSE_RESERVED2 (0xFF<<0) +#define ISCSI_FW_RESPONSE_RESERVED2_SHIFT 0 +#define ISCSI_FW_RESPONSE_ERR_CODE (0xFF<<8) +#define ISCSI_FW_RESPONSE_ERR_CODE_SHIFT 8 +#define ISCSI_FW_RESPONSE_RESERVED3 (0xFFFF<<16) +#define ISCSI_FW_RESPONSE_RESERVED3_SHIFT 16 + u32 stat_sn; + u32 hdr_dword2[2]; + u32 hdr_dword3[2]; + u32 task_stat; + u32 reserved0; + u32 hdr_itt; + u32 cq_req_sn; +}; + + +/* + * iSCSI KCQ CQE parameters + */ +union iscsi_kcqe_params { + u32 reserved0[4]; +}; + +/* + * iSCSI KCQ CQE + */ +struct iscsi_kcqe { + u32 iscsi_conn_id; + u32 completion_status; + u32 iscsi_conn_context_id; + union iscsi_kcqe_params params; +#if defined(__BIG_ENDIAN) + u8 flags; +#define ISCSI_KCQE_RESERVED0 (0xF<<0) +#define ISCSI_KCQE_RESERVED0_SHIFT 0 +#define ISCSI_KCQE_LAYER_CODE (0x7<<4) +#define ISCSI_KCQE_LAYER_CODE_SHIFT 4 +#define ISCSI_KCQE_RESERVED1 (0x1<<7) +#define ISCSI_KCQE_RESERVED1_SHIFT 7 + u8 op_code; + u16 qe_self_seq; +#elif defined(__LITTLE_ENDIAN) + u16 qe_self_seq; + u8 op_code; + u8 flags; +#define ISCSI_KCQE_RESERVED0 (0xF<<0) +#define ISCSI_KCQE_RESERVED0_SHIFT 0 +#define ISCSI_KCQE_LAYER_CODE (0x7<<4) +#define ISCSI_KCQE_LAYER_CODE_SHIFT 4 +#define ISCSI_KCQE_RESERVED1 (0x1<<7) +#define ISCSI_KCQE_RESERVED1_SHIFT 7 +#endif +}; + + + +/* + * iSCSI KWQE header + */ +struct iscsi_kwqe_header { +#if defined(__BIG_ENDIAN) + u8 flags; +#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0) +#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0 +#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4) +#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4 +#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7) +#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7 + u8 op_code; +#elif defined(__LITTLE_ENDIAN) + u8 op_code; + u8 flags; +#define ISCSI_KWQE_HEADER_RESERVED0 (0xF<<0) +#define ISCSI_KWQE_HEADER_RESERVED0_SHIFT 0 +#define ISCSI_KWQE_HEADER_LAYER_CODE (0x7<<4) +#define ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT 4 +#define ISCSI_KWQE_HEADER_RESERVED1 (0x1<<7) +#define ISCSI_KWQE_HEADER_RESERVED1_SHIFT 7 +#endif +}; + +/* + * iSCSI firmware init request 1 + */ +struct iscsi_kwqe_init1 { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u8 reserved0; + u8 num_cqs; +#elif defined(__LITTLE_ENDIAN) + u8 num_cqs; + u8 reserved0; + struct iscsi_kwqe_header hdr; +#endif + u32 dummy_buffer_addr_lo; + u32 dummy_buffer_addr_hi; +#if defined(__BIG_ENDIAN) + u16 num_ccells_per_conn; + u16 num_tasks_per_conn; +#elif defined(__LITTLE_ENDIAN) + u16 num_tasks_per_conn; + u16 num_ccells_per_conn; +#endif +#if defined(__BIG_ENDIAN) + u16 sq_wqes_per_page; + u16 sq_num_wqes; +#elif defined(__LITTLE_ENDIAN) + u16 sq_num_wqes; + u16 sq_wqes_per_page; +#endif +#if defined(__BIG_ENDIAN) + u8 cq_log_wqes_per_page; + u8 flags; +#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0) +#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0 +#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4) +#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4 +#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5) +#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5 +#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE (0x1<<6) +#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE_SHIFT 6 +#define ISCSI_KWQE_INIT1_RESERVED1 (0x1<<7) +#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 7 + u16 cq_num_wqes; +#elif defined(__LITTLE_ENDIAN) + u16 cq_num_wqes; + u8 flags; +#define ISCSI_KWQE_INIT1_PAGE_SIZE (0xF<<0) +#define ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT 0 +#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE (0x1<<4) +#define ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE_SHIFT 4 +#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE (0x1<<5) +#define ISCSI_KWQE_INIT1_KEEP_ALIVE_ENABLE_SHIFT 5 +#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE (0x1<<6) +#define ISCSI_KWQE_INIT1_TIME_STAMPS_ENABLE_SHIFT 6 +#define ISCSI_KWQE_INIT1_RESERVED1 (0x1<<7) +#define ISCSI_KWQE_INIT1_RESERVED1_SHIFT 7 + u8 cq_log_wqes_per_page; +#endif +#if defined(__BIG_ENDIAN) + u16 cq_num_pages; + u16 sq_num_pages; +#elif defined(__LITTLE_ENDIAN) + u16 sq_num_pages; + u16 cq_num_pages; +#endif +#if defined(__BIG_ENDIAN) + u16 rq_buffer_size; + u16 rq_num_wqes; +#elif defined(__LITTLE_ENDIAN) + u16 rq_num_wqes; + u16 rq_buffer_size; +#endif +}; + +/* + * iSCSI firmware init request 2 + */ +struct iscsi_kwqe_init2 { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 max_cq_sqn; +#elif defined(__LITTLE_ENDIAN) + u16 max_cq_sqn; + struct iscsi_kwqe_header hdr; +#endif + u32 error_bit_map[2]; + u32 reserved1[5]; +}; + +/* + * Initial iSCSI connection offload request 1 + */ +struct iscsi_kwqe_conn_offload1 { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 iscsi_conn_id; +#elif defined(__LITTLE_ENDIAN) + u16 iscsi_conn_id; + struct iscsi_kwqe_header hdr; +#endif + u32 sq_page_table_addr_lo; + u32 sq_page_table_addr_hi; + u32 cq_page_table_addr_lo; + u32 cq_page_table_addr_hi; + u32 reserved0[3]; +}; + +/* + * iSCSI Page Table Entry (PTE) + */ +struct iscsi_pte { + u32 hi; + u32 lo; +}; + +/* + * Initial iSCSI connection offload request 2 + */ +struct iscsi_kwqe_conn_offload2 { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + struct iscsi_kwqe_header hdr; +#endif + u32 rq_page_table_addr_lo; + u32 rq_page_table_addr_hi; + struct iscsi_pte sq_first_pte; + struct iscsi_pte cq_first_pte; + u32 num_additional_wqes; +}; + + +/* + * Initial iSCSI connection offload request 3 + */ +struct iscsi_kwqe_conn_offload3 { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + struct iscsi_kwqe_header hdr; +#endif + u32 reserved1; + struct iscsi_pte qp_first_pte[3]; +}; + + +/* + * iSCSI connection update request + */ +struct iscsi_kwqe_conn_update { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + struct iscsi_kwqe_header hdr; +#endif +#if defined(__BIG_ENDIAN) + u8 session_error_recovery_level; + u8 max_outstanding_r2ts; + u8 reserved2; + u8 conn_flags; +#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0) +#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0 +#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1) +#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1 +#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2) +#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2 +#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3) +#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3 +#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE (0x3<<4) +#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE_SHIFT 4 +#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0x3<<6) +#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 6 +#elif defined(__LITTLE_ENDIAN) + u8 conn_flags; +#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST (0x1<<0) +#define ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST_SHIFT 0 +#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST (0x1<<1) +#define ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST_SHIFT 1 +#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T (0x1<<2) +#define ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T_SHIFT 2 +#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA (0x1<<3) +#define ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA_SHIFT 3 +#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE (0x3<<4) +#define ISCSI_KWQE_CONN_UPDATE_OOO_SUPPORT_MODE_SHIFT 4 +#define ISCSI_KWQE_CONN_UPDATE_RESERVED1 (0x3<<6) +#define ISCSI_KWQE_CONN_UPDATE_RESERVED1_SHIFT 6 + u8 reserved2; + u8 max_outstanding_r2ts; + u8 session_error_recovery_level; +#endif + u32 context_id; + u32 max_send_pdu_length; + u32 max_recv_pdu_length; + u32 first_burst_length; + u32 max_burst_length; + u32 exp_stat_sn; +}; + +/* + * iSCSI destroy connection request + */ +struct iscsi_kwqe_conn_destroy { +#if defined(__BIG_ENDIAN) + struct iscsi_kwqe_header hdr; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + struct iscsi_kwqe_header hdr; +#endif + u32 context_id; + u32 reserved1[6]; +}; + +/* + * iSCSI KWQ WQE + */ +union iscsi_kwqe { + struct iscsi_kwqe_init1 init1; + struct iscsi_kwqe_init2 init2; + struct iscsi_kwqe_conn_offload1 conn_offload1; + struct iscsi_kwqe_conn_offload2 conn_offload2; + struct iscsi_kwqe_conn_update conn_update; + struct iscsi_kwqe_conn_destroy conn_destroy; +}; + +/* + * iSCSI Login SQ WQE + */ +struct bnx2i_login_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0) +#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2) +#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2 +#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4) +#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4 +#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6) +#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6 +#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7) +#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7 + u8 version_max; + u8 version_min; +#elif defined(__LITTLE_ENDIAN) + u8 version_min; + u8 version_max; + u8 op_attr; +#define ISCSI_LOGIN_REQUEST_NEXT_STAGE (0x3<<0) +#define ISCSI_LOGIN_REQUEST_NEXT_STAGE_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE (0x3<<2) +#define ISCSI_LOGIN_REQUEST_CURRENT_STAGE_SHIFT 2 +#define ISCSI_LOGIN_REQUEST_RESERVED0 (0x3<<4) +#define ISCSI_LOGIN_REQUEST_RESERVED0_SHIFT 4 +#define ISCSI_LOGIN_REQUEST_CONT (0x1<<6) +#define ISCSI_LOGIN_REQUEST_CONT_SHIFT 6 +#define ISCSI_LOGIN_REQUEST_TRANSIT (0x1<<7) +#define ISCSI_LOGIN_REQUEST_TRANSIT_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 isid_lo; +#if defined(__BIG_ENDIAN) + u16 isid_hi; + u16 tsih; +#elif defined(__LITTLE_ENDIAN) + u16 tsih; + u16 isid_hi; +#endif +#if defined(__BIG_ENDIAN) + u16 reserved2; + u16 itt; +#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14) +#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_LOGIN_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_LOGIN_REQUEST_INDEX_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_TYPE (0x3<<14) +#define ISCSI_LOGIN_REQUEST_TYPE_SHIFT 14 + u16 reserved2; +#endif +#if defined(__BIG_ENDIAN) + u16 cid; + u16 reserved3; +#elif defined(__LITTLE_ENDIAN) + u16 reserved3; + u16 cid; +#endif + u32 cmd_sn; + u32 exp_stat_sn; + u32 reserved4; + u32 resp_bd_list_addr_lo; + u32 resp_bd_list_addr_hi; + u32 resp_buffer; +#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0) +#define ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS (0xFF<<24) +#define ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT 24 +#if defined(__BIG_ENDIAN) + u16 reserved8; + u8 reserved7; + u8 flags; +#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0) +#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2) +#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2 +#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3) +#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3 +#elif defined(__LITTLE_ENDIAN) + u8 flags; +#define ISCSI_LOGIN_REQUEST_RESERVED5 (0x3<<0) +#define ISCSI_LOGIN_REQUEST_RESERVED5_SHIFT 0 +#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN (0x1<<2) +#define ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN_SHIFT 2 +#define ISCSI_LOGIN_REQUEST_RESERVED6 (0x1F<<3) +#define ISCSI_LOGIN_REQUEST_RESERVED6_SHIFT 3 + u8 reserved7; + u16 reserved8; +#endif + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved10; + u8 reserved9; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved9; + u8 reserved10; + u8 cq_index; +#endif +}; + + +/* + * iSCSI Login CQE + */ +struct bnx2i_login_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 response_flags; +#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0) +#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0 +#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2) +#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2 +#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4) +#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4 +#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6) +#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6 +#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7) +#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7 + u8 version_max; + u8 version_active; +#elif defined(__LITTLE_ENDIAN) + u8 version_active; + u8 version_max; + u8 response_flags; +#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE (0x3<<0) +#define ISCSI_LOGIN_RESPONSE_NEXT_STAGE_SHIFT 0 +#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE (0x3<<2) +#define ISCSI_LOGIN_RESPONSE_CURRENT_STAGE_SHIFT 2 +#define ISCSI_LOGIN_RESPONSE_RESERVED0 (0x3<<4) +#define ISCSI_LOGIN_RESPONSE_RESERVED0_SHIFT 4 +#define ISCSI_LOGIN_RESPONSE_CONT (0x1<<6) +#define ISCSI_LOGIN_RESPONSE_CONT_SHIFT 6 +#define ISCSI_LOGIN_RESPONSE_TRANSIT (0x1<<7) +#define ISCSI_LOGIN_RESPONSE_TRANSIT_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved1[2]; +#if defined(__BIG_ENDIAN) + u16 reserved3; + u8 err_code; + u8 reserved2; +#elif defined(__LITTLE_ENDIAN) + u8 reserved2; + u8 err_code; + u16 reserved3; +#endif + u32 stat_sn; + u32 isid_lo; +#if defined(__BIG_ENDIAN) + u16 isid_hi; + u16 tsih; +#elif defined(__LITTLE_ENDIAN) + u16 tsih; + u16 isid_hi; +#endif +#if defined(__BIG_ENDIAN) + u8 status_class; + u8 status_detail; + u16 reserved4; +#elif defined(__LITTLE_ENDIAN) + u16 reserved4; + u8 status_detail; + u8 status_class; +#endif + u32 reserved5[3]; +#if defined(__BIG_ENDIAN) + u16 reserved6; + u16 itt; +#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14) +#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_LOGIN_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_LOGIN_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_LOGIN_RESPONSE_TYPE (0x3<<14) +#define ISCSI_LOGIN_RESPONSE_TYPE_SHIFT 14 + u16 reserved6; +#endif + u32 cq_req_sn; +}; + + +/* + * iSCSI Logout SQ WQE + */ +struct bnx2i_logout_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0) +#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0 +#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_attr; +#define ISCSI_LOGOUT_REQUEST_REASON (0x7F<<0) +#define ISCSI_LOGOUT_REQUEST_REASON_SHIFT 0 +#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_LOGOUT_REQUEST_ALWAYS_ONE_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 reserved1[2]; +#if defined(__BIG_ENDIAN) + u16 reserved2; + u16 itt; +#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14) +#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_LOGOUT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_LOGOUT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_LOGOUT_REQUEST_TYPE (0x3<<14) +#define ISCSI_LOGOUT_REQUEST_TYPE_SHIFT 14 + u16 reserved2; +#endif +#if defined(__BIG_ENDIAN) + u16 cid; + u16 reserved3; +#elif defined(__LITTLE_ENDIAN) + u16 reserved3; + u16 cid; +#endif + u32 cmd_sn; + u32 reserved4[5]; + u32 zero_fill; + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved6; + u8 reserved5; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved5; + u8 reserved6; + u8 cq_index; +#endif +}; + + +/* + * iSCSI Logout CQE + */ +struct bnx2i_logout_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u8 response; + u8 reserved0; +#elif defined(__LITTLE_ENDIAN) + u8 reserved0; + u8 response; + u8 reserved1; + u8 op_code; +#endif + u32 reserved2; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved3[2]; +#if defined(__BIG_ENDIAN) + u16 reserved5; + u8 err_code; + u8 reserved4; +#elif defined(__LITTLE_ENDIAN) + u8 reserved4; + u8 err_code; + u16 reserved5; +#endif + u32 reserved6[3]; +#if defined(__BIG_ENDIAN) + u16 time_to_wait; + u16 time_to_retain; +#elif defined(__LITTLE_ENDIAN) + u16 time_to_retain; + u16 time_to_wait; +#endif + u32 reserved7[3]; +#if defined(__BIG_ENDIAN) + u16 reserved8; + u16 itt; +#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14) +#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_LOGOUT_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_LOGOUT_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_LOGOUT_RESPONSE_TYPE (0x3<<14) +#define ISCSI_LOGOUT_RESPONSE_TYPE_SHIFT 14 + u16 reserved8; +#endif + u32 cq_req_sn; +}; + + +/* + * iSCSI Nop-In CQE + */ +struct bnx2i_nop_in_msg { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 reserved1; + u8 op_code; +#endif + u32 data_length; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 ttt; + u32 reserved2; +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 err_code; + u8 reserved3; +#elif defined(__LITTLE_ENDIAN) + u8 reserved3; + u8 err_code; + u16 reserved4; +#endif + u32 reserved5; + u32 lun[2]; + u32 reserved6[4]; +#if defined(__BIG_ENDIAN) + u16 reserved7; + u16 itt; +#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0) +#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0 +#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14) +#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_NOP_IN_MSG_INDEX (0x3FFF<<0) +#define ISCSI_NOP_IN_MSG_INDEX_SHIFT 0 +#define ISCSI_NOP_IN_MSG_TYPE (0x3<<14) +#define ISCSI_NOP_IN_MSG_TYPE_SHIFT 14 + u16 reserved7; +#endif + u32 cq_req_sn; +}; + + +/* + * iSCSI NOP-OUT SQ WQE + */ +struct bnx2i_nop_out_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0) +#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_attr; +#define ISCSI_NOP_OUT_REQUEST_RESERVED1 (0x7F<<0) +#define ISCSI_NOP_OUT_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_NOP_OUT_REQUEST_ALWAYS_ONE_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 lun[2]; +#if defined(__BIG_ENDIAN) + u16 reserved2; + u16 itt; +#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14) +#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_NOP_OUT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_NOP_OUT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_TYPE (0x3<<14) +#define ISCSI_NOP_OUT_REQUEST_TYPE_SHIFT 14 + u16 reserved2; +#endif + u32 ttt; + u32 cmd_sn; + u32 reserved3[2]; + u32 resp_bd_list_addr_lo; + u32 resp_bd_list_addr_hi; + u32 resp_buffer; +#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0) +#define ISCSI_NOP_OUT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS (0xFF<<24) +#define ISCSI_NOP_OUT_REQUEST_NUM_RESP_BDS_SHIFT 24 +#if defined(__BIG_ENDIAN) + u16 reserved7; + u8 reserved6; + u8 flags; +#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0) +#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1) +#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1 +#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2) +#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2 +#elif defined(__LITTLE_ENDIAN) + u8 flags; +#define ISCSI_NOP_OUT_REQUEST_RESERVED4 (0x1<<0) +#define ISCSI_NOP_OUT_REQUEST_RESERVED4_SHIFT 0 +#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION (0x1<<1) +#define ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION_SHIFT 1 +#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL (0x3F<<2) +#define ISCSI_NOP_OUT_REQUEST_ZERO_FILL_SHIFT 2 + u8 reserved6; + u16 reserved7; +#endif + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved9; + u8 reserved8; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved8; + u8 reserved9; + u8 cq_index; +#endif +}; + +/* + * iSCSI Reject CQE + */ +struct bnx2i_reject_msg { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u8 reason; + u8 reserved0; +#elif defined(__LITTLE_ENDIAN) + u8 reserved0; + u8 reason; + u8 reserved1; + u8 op_code; +#endif + u32 data_length; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved2[2]; +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 err_code; + u8 reserved3; +#elif defined(__LITTLE_ENDIAN) + u8 reserved3; + u8 err_code; + u16 reserved4; +#endif + u32 reserved5[8]; + u32 cq_req_sn; +}; + +/* + * bnx2i iSCSI TMF SQ WQE + */ +struct bnx2i_tmf_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0) +#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0 +#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_attr; +#define ISCSI_TMF_REQUEST_FUNCTION (0x7F<<0) +#define ISCSI_TMF_REQUEST_FUNCTION_SHIFT 0 +#define ISCSI_TMF_REQUEST_ALWAYS_ONE (0x1<<7) +#define ISCSI_TMF_REQUEST_ALWAYS_ONE_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 lun[2]; +#if defined(__BIG_ENDIAN) + u16 reserved1; + u16 itt; +#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0 +#define ISCSI_TMF_REQUEST_TYPE (0x3<<14) +#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_TMF_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_TMF_REQUEST_INDEX_SHIFT 0 +#define ISCSI_TMF_REQUEST_TYPE (0x3<<14) +#define ISCSI_TMF_REQUEST_TYPE_SHIFT 14 + u16 reserved1; +#endif + u32 ref_itt; + u32 cmd_sn; + u32 reserved2; + u32 ref_cmd_sn; + u32 reserved3[3]; + u32 zero_fill; + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved5; + u8 reserved4; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved4; + u8 reserved5; + u8 cq_index; +#endif +}; + +/* + * iSCSI Text SQ WQE + */ +struct bnx2i_text_request { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 op_attr; +#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0) +#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_TEXT_REQUEST_CONT (0x1<<6) +#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6 +#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7) +#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 op_attr; +#define ISCSI_TEXT_REQUEST_RESERVED1 (0x3F<<0) +#define ISCSI_TEXT_REQUEST_RESERVED1_SHIFT 0 +#define ISCSI_TEXT_REQUEST_CONT (0x1<<6) +#define ISCSI_TEXT_REQUEST_CONT_SHIFT 6 +#define ISCSI_TEXT_REQUEST_FINAL (0x1<<7) +#define ISCSI_TEXT_REQUEST_FINAL_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 lun[2]; +#if defined(__BIG_ENDIAN) + u16 reserved3; + u16 itt; +#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14) +#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_TEXT_REQUEST_INDEX (0x3FFF<<0) +#define ISCSI_TEXT_REQUEST_INDEX_SHIFT 0 +#define ISCSI_TEXT_REQUEST_TYPE (0x3<<14) +#define ISCSI_TEXT_REQUEST_TYPE_SHIFT 14 + u16 reserved3; +#endif + u32 ttt; + u32 cmd_sn; + u32 reserved4[2]; + u32 resp_bd_list_addr_lo; + u32 resp_bd_list_addr_hi; + u32 resp_buffer; +#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH (0xFFFFFF<<0) +#define ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH_SHIFT 0 +#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS (0xFF<<24) +#define ISCSI_TEXT_REQUEST_NUM_RESP_BDS_SHIFT 24 + u32 zero_fill; + u32 bd_list_addr_lo; + u32 bd_list_addr_hi; +#if defined(__BIG_ENDIAN) + u8 cq_index; + u8 reserved7; + u8 reserved6; + u8 num_bds; +#elif defined(__LITTLE_ENDIAN) + u8 num_bds; + u8 reserved6; + u8 reserved7; + u8 cq_index; +#endif +}; + +/* + * iSCSI SQ WQE + */ +union iscsi_request { + struct bnx2i_cmd_request cmd; + struct bnx2i_tmf_request tmf; + struct bnx2i_nop_out_request nop_out; + struct bnx2i_login_request login_req; + struct bnx2i_text_request text; + struct bnx2i_logout_request logout_req; + struct bnx2i_cleanup_request cleanup; +}; + + +/* + * iSCSI TMF CQE + */ +struct bnx2i_tmf_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 reserved1; + u8 response; + u8 reserved0; +#elif defined(__LITTLE_ENDIAN) + u8 reserved0; + u8 response; + u8 reserved1; + u8 op_code; +#endif + u32 reserved2; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 reserved3[2]; +#if defined(__BIG_ENDIAN) + u16 reserved5; + u8 err_code; + u8 reserved4; +#elif defined(__LITTLE_ENDIAN) + u8 reserved4; + u8 err_code; + u16 reserved5; +#endif + u32 reserved6[7]; +#if defined(__BIG_ENDIAN) + u16 reserved7; + u16 itt; +#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14) +#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_TMF_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_TMF_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_TMF_RESPONSE_TYPE (0x3<<14) +#define ISCSI_TMF_RESPONSE_TYPE_SHIFT 14 + u16 reserved7; +#endif + u32 cq_req_sn; +}; + +/* + * iSCSI Text CQE + */ +struct bnx2i_text_response { +#if defined(__BIG_ENDIAN) + u8 op_code; + u8 response_flags; +#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0) +#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0 +#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6) +#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6 +#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7) +#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7 + u16 reserved0; +#elif defined(__LITTLE_ENDIAN) + u16 reserved0; + u8 response_flags; +#define ISCSI_TEXT_RESPONSE_RESERVED1 (0x3F<<0) +#define ISCSI_TEXT_RESPONSE_RESERVED1_SHIFT 0 +#define ISCSI_TEXT_RESPONSE_CONT (0x1<<6) +#define ISCSI_TEXT_RESPONSE_CONT_SHIFT 6 +#define ISCSI_TEXT_RESPONSE_FINAL (0x1<<7) +#define ISCSI_TEXT_RESPONSE_FINAL_SHIFT 7 + u8 op_code; +#endif + u32 data_length; + u32 exp_cmd_sn; + u32 max_cmd_sn; + u32 ttt; + u32 reserved2; +#if defined(__BIG_ENDIAN) + u16 reserved4; + u8 err_code; + u8 reserved3; +#elif defined(__LITTLE_ENDIAN) + u8 reserved3; + u8 err_code; + u16 reserved4; +#endif + u32 reserved5; + u32 lun[2]; + u32 reserved6[4]; +#if defined(__BIG_ENDIAN) + u16 reserved7; + u16 itt; +#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14) +#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14 +#elif defined(__LITTLE_ENDIAN) + u16 itt; +#define ISCSI_TEXT_RESPONSE_INDEX (0x3FFF<<0) +#define ISCSI_TEXT_RESPONSE_INDEX_SHIFT 0 +#define ISCSI_TEXT_RESPONSE_TYPE (0x3<<14) +#define ISCSI_TEXT_RESPONSE_TYPE_SHIFT 14 + u16 reserved7; +#endif + u32 cq_req_sn; +}; + +/* + * iSCSI CQE + */ +union iscsi_response { + struct bnx2i_cmd_response cmd; + struct bnx2i_tmf_response tmf; + struct bnx2i_login_response login_resp; + struct bnx2i_text_response text; + struct bnx2i_logout_response logout_resp; + struct bnx2i_cleanup_response cleanup; + struct bnx2i_reject_msg reject; + struct bnx2i_async_msg async; + struct bnx2i_nop_in_msg nop_in; +}; + +#endif /* __57XX_ISCSI_HSI_LINUX_LE__ */ diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig new file mode 100644 index 000000000..ba30ff86d --- /dev/null +++ b/drivers/scsi/bnx2i/Kconfig @@ -0,0 +1,13 @@ +config SCSI_BNX2_ISCSI + tristate "QLogic NetXtreme II iSCSI support" + depends on NET + depends on PCI + depends on (IPV6 || IPV6=n) + select SCSI_ISCSI_ATTRS + select NETDEVICES + select ETHERNET + select NET_VENDOR_BROADCOM + select CNIC + ---help--- + This driver supports iSCSI offload for the QLogic NetXtreme II + devices. diff --git a/drivers/scsi/bnx2i/Makefile b/drivers/scsi/bnx2i/Makefile new file mode 100644 index 000000000..b5802bd2e --- /dev/null +++ b/drivers/scsi/bnx2i/Makefile @@ -0,0 +1,3 @@ +bnx2i-y := bnx2i_init.o bnx2i_hwi.o bnx2i_iscsi.o bnx2i_sysfs.o + +obj-$(CONFIG_SCSI_BNX2_ISCSI) += bnx2i.o diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h new file mode 100644 index 000000000..ed7f3228e --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -0,0 +1,882 @@ +/* bnx2i.h: QLogic NetXtreme II iSCSI driver. + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mike Christie + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ + +#ifndef _BNX2I_H_ +#define _BNX2I_H_ + +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/in.h> +#include <linux/kfifo.h> +#include <linux/netdevice.h> +#include <linux/completion.h> +#include <linux/kthread.h> +#include <linux/cpu.h> + +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi.h> +#include <scsi/iscsi_proto.h> +#include <scsi/libiscsi.h> +#include <scsi/scsi_transport_iscsi.h> + +#include "../../net/ethernet/broadcom/cnic_if.h" +#include "57xx_iscsi_hsi.h" +#include "57xx_iscsi_constants.h" + +#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h" + +#define BNX2_ISCSI_DRIVER_NAME "bnx2i" + +#define BNX2I_MAX_ADAPTERS 8 + +#define ISCSI_MAX_CONNS_PER_HBA 128 +#define ISCSI_MAX_SESS_PER_HBA ISCSI_MAX_CONNS_PER_HBA +#define ISCSI_MAX_CMDS_PER_SESS 128 + +/* Total active commands across all connections supported by devices */ +#define ISCSI_MAX_CMDS_PER_HBA_5708 (28 * (ISCSI_MAX_CMDS_PER_SESS - 1)) +#define ISCSI_MAX_CMDS_PER_HBA_5709 (128 * (ISCSI_MAX_CMDS_PER_SESS - 1)) +#define ISCSI_MAX_CMDS_PER_HBA_57710 (256 * (ISCSI_MAX_CMDS_PER_SESS - 1)) + +#define ISCSI_MAX_BDS_PER_CMD 32 + +#define MAX_PAGES_PER_CTRL_STRUCT_POOL 8 +#define BNX2I_RESERVED_SLOW_PATH_CMD_SLOTS 4 + +#define BNX2X_DB_SHIFT 3 + +/* 5706/08 hardware has limit on maximum buffer size per BD it can handle */ +#define MAX_BD_LENGTH 65535 +#define BD_SPLIT_SIZE 32768 + +/* min, max & default values for SQ/RQ/CQ size, configurable via' modparam */ +#define BNX2I_SQ_WQES_MIN 16 +#define BNX2I_570X_SQ_WQES_MAX 128 +#define BNX2I_5770X_SQ_WQES_MAX 512 +#define BNX2I_570X_SQ_WQES_DEFAULT 128 +#define BNX2I_5770X_SQ_WQES_DEFAULT 128 + +#define BNX2I_570X_CQ_WQES_MAX 128 +#define BNX2I_5770X_CQ_WQES_MAX 512 + +#define BNX2I_RQ_WQES_MIN 16 +#define BNX2I_RQ_WQES_MAX 32 +#define BNX2I_RQ_WQES_DEFAULT 16 + +/* CCELLs per conn */ +#define BNX2I_CCELLS_MIN 16 +#define BNX2I_CCELLS_MAX 96 +#define BNX2I_CCELLS_DEFAULT 64 + +#define ITT_INVALID_SIGNATURE 0xFFFF + +#define ISCSI_CMD_CLEANUP_TIMEOUT 100 + +#define BNX2I_CONN_CTX_BUF_SIZE 16384 + +#define BNX2I_SQ_WQE_SIZE 64 +#define BNX2I_RQ_WQE_SIZE 256 +#define BNX2I_CQE_SIZE 64 + +#define MB_KERNEL_CTX_SHIFT 8 +#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT) + +#define CTX_SHIFT 7 +#define GET_CID_NUM(cid_addr) ((cid_addr) >> CTX_SHIFT) + +#define CTX_OFFSET 0x10000 +#define MAX_CID_CNT 0x4000 + +#define BNX2I_570X_PAGE_SIZE_DEFAULT 4096 + +/* 5709 context registers */ +#define BNX2_MQ_CONFIG2 0x00003d00 +#define BNX2_MQ_CONFIG2_CONT_SZ (0x7L<<4) +#define BNX2_MQ_CONFIG2_FIRST_L4L5 (0x1fL<<8) + +/* 57710's BAR2 is mapped to doorbell registers */ +#define BNX2X_DOORBELL_PCI_BAR 2 +#define BNX2X_MAX_CQS 8 + +#define CNIC_ARM_CQE 1 +#define CNIC_ARM_CQE_FP 2 +#define CNIC_DISARM_CQE 0 + +#define REG_RD(__hba, offset) \ + readl(__hba->regview + offset) +#define REG_WR(__hba, offset, val) \ + writel(val, __hba->regview + offset) + +#ifdef CONFIG_32BIT +#define GET_STATS_64(__hba, dst, field) \ + do { \ + spin_lock_bh(&__hba->stat_lock); \ + dst->field##_lo = __hba->stats.field##_lo; \ + dst->field##_hi = __hba->stats.field##_hi; \ + spin_unlock_bh(&__hba->stat_lock); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + do { \ + if (spin_trylock(&__hba->stat_lock)) { \ + if (__hba->stats.field##_lo + len < \ + __hba->stats.field##_lo) \ + __hba->stats.field##_hi++; \ + __hba->stats.field##_lo += len; \ + spin_unlock(&__hba->stat_lock); \ + } \ + } while (0) + +#else +#define GET_STATS_64(__hba, dst, field) \ + do { \ + u64 val, *out; \ + \ + val = __hba->bnx2i_stats.field; \ + out = (u64 *)&__hba->stats.field##_lo; \ + *out = cpu_to_le64(val); \ + out = (u64 *)&dst->field##_lo; \ + *out = cpu_to_le64(val); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + do { \ + __hba->bnx2i_stats.field += len; \ + } while (0) +#endif + +/** + * struct generic_pdu_resc - login pdu resource structure + * + * @req_buf: driver buffer used to stage payload associated with + * the login request + * @req_dma_addr: dma address for iscsi login request payload buffer + * @req_buf_size: actual login request payload length + * @req_wr_ptr: pointer into login request buffer when next data is + * to be written + * @resp_hdr: iscsi header where iscsi login response header is to + * be recreated + * @resp_buf: buffer to stage login response payload + * @resp_dma_addr: login response payload buffer dma address + * @resp_buf_size: login response paylod length + * @resp_wr_ptr: pointer into login response buffer when next data is + * to be written + * @req_bd_tbl: iscsi login request payload BD table + * @req_bd_dma: login request BD table dma address + * @resp_bd_tbl: iscsi login response payload BD table + * @resp_bd_dma: login request BD table dma address + * + * following structure defines buffer info for generic pdus such as iSCSI Login, + * Logout and NOP + */ +struct generic_pdu_resc { + char *req_buf; + dma_addr_t req_dma_addr; + u32 req_buf_size; + char *req_wr_ptr; + struct iscsi_hdr resp_hdr; + char *resp_buf; + dma_addr_t resp_dma_addr; + u32 resp_buf_size; + char *resp_wr_ptr; + char *req_bd_tbl; + dma_addr_t req_bd_dma; + char *resp_bd_tbl; + dma_addr_t resp_bd_dma; +}; + + +/** + * struct bd_resc_page - tracks DMA'able memory allocated for BD tables + * + * @link: list head to link elements + * @max_ptrs: maximun pointers that can be stored in this page + * @num_valid: number of pointer valid in this page + * @page: base addess for page pointer array + * + * structure to track DMA'able memory allocated for command BD tables + */ +struct bd_resc_page { + struct list_head link; + u32 max_ptrs; + u32 num_valid; + void *page[1]; +}; + + +/** + * struct io_bdt - I/O buffer destricptor table + * + * @bd_tbl: BD table's virtual address + * @bd_tbl_dma: BD table's dma address + * @bd_valid: num valid BD entries + * + * IO BD table + */ +struct io_bdt { + struct iscsi_bd *bd_tbl; + dma_addr_t bd_tbl_dma; + u16 bd_valid; +}; + + +/** + * bnx2i_cmd - iscsi command structure + * + * @hdr: iSCSI header + * @conn: iscsi_conn pointer + * @scsi_cmd: SCSI-ML task pointer corresponding to this iscsi cmd + * @sg: SG list + * @io_tbl: buffer descriptor (BD) table + * @bd_tbl_dma: buffer descriptor (BD) table's dma address + * @req: bnx2i specific command request struct + */ +struct bnx2i_cmd { + struct iscsi_hdr hdr; + struct bnx2i_conn *conn; + struct scsi_cmnd *scsi_cmd; + struct scatterlist *sg; + struct io_bdt io_tbl; + dma_addr_t bd_tbl_dma; + struct bnx2i_cmd_request req; +}; + + +/** + * struct bnx2i_conn - iscsi connection structure + * + * @cls_conn: pointer to iscsi cls conn + * @hba: adapter structure pointer + * @iscsi_conn_cid: iscsi conn id + * @fw_cid: firmware iscsi context id + * @ep: endpoint structure pointer + * @gen_pdu: login/nopout/logout pdu resources + * @violation_notified: bit mask used to track iscsi error/warning messages + * already printed out + * @work_cnt: keeps track of the number of outstanding work + * + * iSCSI connection structure + */ +struct bnx2i_conn { + struct iscsi_cls_conn *cls_conn; + struct bnx2i_hba *hba; + struct completion cmd_cleanup_cmpl; + + u32 iscsi_conn_cid; +#define BNX2I_CID_RESERVED 0x5AFF + u32 fw_cid; + + struct timer_list poll_timer; + /* + * Queue Pair (QP) related structure elements. + */ + struct bnx2i_endpoint *ep; + + /* + * Buffer for login negotiation process + */ + struct generic_pdu_resc gen_pdu; + u64 violation_notified; + + atomic_t work_cnt; +}; + + + +/** + * struct iscsi_cid_queue - Per adapter iscsi cid queue + * + * @cid_que_base: queue base memory + * @cid_que: queue memory pointer + * @cid_q_prod_idx: produce index + * @cid_q_cons_idx: consumer index + * @cid_q_max_idx: max index. used to detect wrap around condition + * @cid_free_cnt: queue size + * @conn_cid_tbl: iscsi cid to conn structure mapping table + * + * Per adapter iSCSI CID Queue + */ +struct iscsi_cid_queue { + void *cid_que_base; + u32 *cid_que; + u32 cid_q_prod_idx; + u32 cid_q_cons_idx; + u32 cid_q_max_idx; + u32 cid_free_cnt; + struct bnx2i_conn **conn_cid_tbl; +}; + + +struct bnx2i_stats_info { + u64 rx_pdus; + u64 rx_bytes; + u64 tx_pdus; + u64 tx_bytes; +}; + + +/** + * struct bnx2i_hba - bnx2i adapter structure + * + * @link: list head to link elements + * @cnic: pointer to cnic device + * @pcidev: pointer to pci dev + * @netdev: pointer to netdev structure + * @regview: mapped PCI register space + * @age: age, incremented by every recovery + * @cnic_dev_type: cnic device type, 5706/5708/5709/57710 + * @mail_queue_access: mailbox queue access mode, applicable to 5709 only + * @reg_with_cnic: indicates whether the device is register with CNIC + * @adapter_state: adapter state, UP, GOING_DOWN, LINK_DOWN + * @mtu_supported: Ethernet MTU supported + * @shost: scsi host pointer + * @max_sqes: SQ size + * @max_rqes: RQ size + * @max_cqes: CQ size + * @num_ccell: number of command cells per connection + * @ofld_conns_active: active connection list + * @eh_wait: wait queue for the endpoint to shutdown + * @max_active_conns: max offload connections supported by this device + * @cid_que: iscsi cid queue + * @ep_rdwr_lock: read / write lock to synchronize various ep lists + * @ep_ofld_list: connection list for pending offload completion + * @ep_active_list: connection list for active offload endpoints + * @ep_destroy_list: connection list for pending offload completion + * @mp_bd_tbl: BD table to be used with middle path requests + * @mp_bd_dma: DMA address of 'mp_bd_tbl' memory buffer + * @dummy_buffer: Dummy buffer to be used with zero length scsicmd reqs + * @dummy_buf_dma: DMA address of 'dummy_buffer' memory buffer + * @lock: lock to synchonize access to hba structure + * @hba_shutdown_tmo: Timeout value to shutdown each connection + * @conn_teardown_tmo: Timeout value to tear down each connection + * @conn_ctx_destroy_tmo: Timeout value to destroy context of each connection + * @pci_did: PCI device ID + * @pci_vid: PCI vendor ID + * @pci_sdid: PCI subsystem device ID + * @pci_svid: PCI subsystem vendor ID + * @pci_func: PCI function number in system pci tree + * @pci_devno: PCI device number in system pci tree + * @num_wqe_sent: statistic counter, total wqe's sent + * @num_cqe_rcvd: statistic counter, total cqe's received + * @num_intr_claimed: statistic counter, total interrupts claimed + * @link_changed_count: statistic counter, num of link change notifications + * received + * @ipaddr_changed_count: statistic counter, num times IP address changed while + * at least one connection is offloaded + * @num_sess_opened: statistic counter, total num sessions opened + * @num_conn_opened: statistic counter, total num conns opened on this hba + * @ctx_ccell_tasks: captures number of ccells and tasks supported by + * currently offloaded connection, used to decode + * context memory + * @stat_lock: spin lock used by the statistic collector (32 bit) + * @stats: local iSCSI statistic collection place holder + * + * Adapter Data Structure + */ +struct bnx2i_hba { + struct list_head link; + struct cnic_dev *cnic; + struct pci_dev *pcidev; + struct net_device *netdev; + void __iomem *regview; + resource_size_t reg_base; + + u32 age; + unsigned long cnic_dev_type; + #define BNX2I_NX2_DEV_5706 0x0 + #define BNX2I_NX2_DEV_5708 0x1 + #define BNX2I_NX2_DEV_5709 0x2 + #define BNX2I_NX2_DEV_57710 0x3 + u32 mail_queue_access; + #define BNX2I_MQ_KERNEL_MODE 0x0 + #define BNX2I_MQ_KERNEL_BYPASS_MODE 0x1 + #define BNX2I_MQ_BIN_MODE 0x2 + unsigned long reg_with_cnic; + #define BNX2I_CNIC_REGISTERED 1 + + unsigned long adapter_state; + #define ADAPTER_STATE_UP 0 + #define ADAPTER_STATE_GOING_DOWN 1 + #define ADAPTER_STATE_LINK_DOWN 2 + #define ADAPTER_STATE_INIT_FAILED 31 + unsigned int mtu_supported; + #define BNX2I_MAX_MTU_SUPPORTED 9000 + + struct Scsi_Host *shost; + + u32 max_sqes; + u32 max_rqes; + u32 max_cqes; + u32 num_ccell; + + int ofld_conns_active; + wait_queue_head_t eh_wait; + + int max_active_conns; + struct iscsi_cid_queue cid_que; + + rwlock_t ep_rdwr_lock; + struct list_head ep_ofld_list; + struct list_head ep_active_list; + struct list_head ep_destroy_list; + + /* + * BD table to be used with MP (Middle Path requests. + */ + char *mp_bd_tbl; + dma_addr_t mp_bd_dma; + char *dummy_buffer; + dma_addr_t dummy_buf_dma; + + spinlock_t lock; /* protects hba structure access */ + struct mutex net_dev_lock;/* sync net device access */ + + int hba_shutdown_tmo; + int conn_teardown_tmo; + int conn_ctx_destroy_tmo; + /* + * PCI related info. + */ + u16 pci_did; + u16 pci_vid; + u16 pci_sdid; + u16 pci_svid; + u16 pci_func; + u16 pci_devno; + + /* + * Following are a bunch of statistics useful during development + * and later stage for score boarding. + */ + u32 num_wqe_sent; + u32 num_cqe_rcvd; + u32 num_intr_claimed; + u32 link_changed_count; + u32 ipaddr_changed_count; + u32 num_sess_opened; + u32 num_conn_opened; + unsigned int ctx_ccell_tasks; + +#ifdef CONFIG_32BIT + spinlock_t stat_lock; +#endif + struct bnx2i_stats_info bnx2i_stats; + struct iscsi_stats_info stats; +}; + + +/******************************************************************************* + * QP [ SQ / RQ / CQ ] info. + ******************************************************************************/ + +/* + * SQ/RQ/CQ generic structure definition + */ +struct sqe { + u8 sqe_byte[BNX2I_SQ_WQE_SIZE]; +}; + +struct rqe { + u8 rqe_byte[BNX2I_RQ_WQE_SIZE]; +}; + +struct cqe { + u8 cqe_byte[BNX2I_CQE_SIZE]; +}; + + +enum { +#if defined(__LITTLE_ENDIAN) + CNIC_EVENT_COAL_INDEX = 0x0, + CNIC_SEND_DOORBELL = 0x4, + CNIC_EVENT_CQ_ARM = 0x7, + CNIC_RECV_DOORBELL = 0x8 +#elif defined(__BIG_ENDIAN) + CNIC_EVENT_COAL_INDEX = 0x2, + CNIC_SEND_DOORBELL = 0x6, + CNIC_EVENT_CQ_ARM = 0x4, + CNIC_RECV_DOORBELL = 0xa +#endif +}; + + +/* + * CQ DB + */ +struct bnx2x_iscsi_cq_pend_cmpl { + /* CQ producer, updated by Ustorm */ + u16 ustrom_prod; + /* CQ pending completion counter */ + u16 pend_cntr; +}; + + +struct bnx2i_5771x_cq_db { + struct bnx2x_iscsi_cq_pend_cmpl qp_pend_cmpl[BNX2X_MAX_CQS]; + /* CQ pending completion ITT array */ + u16 itt[BNX2X_MAX_CQS]; + /* Cstorm CQ sequence to notify array, updated by driver */; + u16 sqn[BNX2X_MAX_CQS]; + u32 reserved[4] /* 16 byte allignment */; +}; + + +struct bnx2i_5771x_sq_rq_db { + u16 prod_idx; + u8 reserved0[62]; /* Pad structure size to 64 bytes */ +}; + + +struct bnx2i_5771x_dbell_hdr { + u8 header; + /* 1 for rx doorbell, 0 for tx doorbell */ +#define B577XX_DOORBELL_HDR_RX (0x1<<0) +#define B577XX_DOORBELL_HDR_RX_SHIFT 0 + /* 0 for normal doorbell, 1 for advertise wnd doorbell */ +#define B577XX_DOORBELL_HDR_DB_TYPE (0x1<<1) +#define B577XX_DOORBELL_HDR_DB_TYPE_SHIFT 1 + /* rdma tx only: DPM transaction size specifier (64/128/256/512B) */ +#define B577XX_DOORBELL_HDR_DPM_SIZE (0x3<<2) +#define B577XX_DOORBELL_HDR_DPM_SIZE_SHIFT 2 + /* connection type */ +#define B577XX_DOORBELL_HDR_CONN_TYPE (0xF<<4) +#define B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT 4 +}; + +struct bnx2i_5771x_dbell { + struct bnx2i_5771x_dbell_hdr dbell; + u8 pad[3]; + +}; + +/** + * struct qp_info - QP (share queue region) atrributes structure + * + * @ctx_base: ioremapped pci register base to access doorbell register + * pertaining to this offloaded connection + * @sq_virt: virtual address of send queue (SQ) region + * @sq_phys: DMA address of SQ memory region + * @sq_mem_size: SQ size + * @sq_prod_qe: SQ producer entry pointer + * @sq_cons_qe: SQ consumer entry pointer + * @sq_first_qe: virtual address of first entry in SQ + * @sq_last_qe: virtual address of last entry in SQ + * @sq_prod_idx: SQ producer index + * @sq_cons_idx: SQ consumer index + * @sqe_left: number sq entry left + * @sq_pgtbl_virt: page table describing buffer consituting SQ region + * @sq_pgtbl_phys: dma address of 'sq_pgtbl_virt' + * @sq_pgtbl_size: SQ page table size + * @cq_virt: virtual address of completion queue (CQ) region + * @cq_phys: DMA address of RQ memory region + * @cq_mem_size: CQ size + * @cq_prod_qe: CQ producer entry pointer + * @cq_cons_qe: CQ consumer entry pointer + * @cq_first_qe: virtual address of first entry in CQ + * @cq_last_qe: virtual address of last entry in CQ + * @cq_prod_idx: CQ producer index + * @cq_cons_idx: CQ consumer index + * @cqe_left: number cq entry left + * @cqe_size: size of each CQ entry + * @cqe_exp_seq_sn: next expected CQE sequence number + * @cq_pgtbl_virt: page table describing buffer consituting CQ region + * @cq_pgtbl_phys: dma address of 'cq_pgtbl_virt' + * @cq_pgtbl_size: CQ page table size + * @rq_virt: virtual address of receive queue (RQ) region + * @rq_phys: DMA address of RQ memory region + * @rq_mem_size: RQ size + * @rq_prod_qe: RQ producer entry pointer + * @rq_cons_qe: RQ consumer entry pointer + * @rq_first_qe: virtual address of first entry in RQ + * @rq_last_qe: virtual address of last entry in RQ + * @rq_prod_idx: RQ producer index + * @rq_cons_idx: RQ consumer index + * @rqe_left: number rq entry left + * @rq_pgtbl_virt: page table describing buffer consituting RQ region + * @rq_pgtbl_phys: dma address of 'rq_pgtbl_virt' + * @rq_pgtbl_size: RQ page table size + * + * queue pair (QP) is a per connection shared data structure which is used + * to send work requests (SQ), receive completion notifications (CQ) + * and receive asynchoronous / scsi sense info (RQ). 'qp_info' structure + * below holds queue memory, consumer/producer indexes and page table + * information + */ +struct qp_info { + void __iomem *ctx_base; +#define DPM_TRIGER_TYPE 0x40 + +#define BNX2I_570x_QUE_DB_SIZE 0 +#define BNX2I_5771x_QUE_DB_SIZE 16 + struct sqe *sq_virt; + dma_addr_t sq_phys; + u32 sq_mem_size; + + struct sqe *sq_prod_qe; + struct sqe *sq_cons_qe; + struct sqe *sq_first_qe; + struct sqe *sq_last_qe; + u16 sq_prod_idx; + u16 sq_cons_idx; + u32 sqe_left; + + void *sq_pgtbl_virt; + dma_addr_t sq_pgtbl_phys; + u32 sq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */ + + struct cqe *cq_virt; + dma_addr_t cq_phys; + u32 cq_mem_size; + + struct cqe *cq_prod_qe; + struct cqe *cq_cons_qe; + struct cqe *cq_first_qe; + struct cqe *cq_last_qe; + u16 cq_prod_idx; + u16 cq_cons_idx; + u32 cqe_left; + u32 cqe_size; + u32 cqe_exp_seq_sn; + + void *cq_pgtbl_virt; + dma_addr_t cq_pgtbl_phys; + u32 cq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */ + + struct rqe *rq_virt; + dma_addr_t rq_phys; + u32 rq_mem_size; + + struct rqe *rq_prod_qe; + struct rqe *rq_cons_qe; + struct rqe *rq_first_qe; + struct rqe *rq_last_qe; + u16 rq_prod_idx; + u16 rq_cons_idx; + u32 rqe_left; + + void *rq_pgtbl_virt; + dma_addr_t rq_pgtbl_phys; + u32 rq_pgtbl_size; /* set to PAGE_SIZE for 5708 & 5709 */ +}; + + + +/* + * CID handles + */ +struct ep_handles { + u32 fw_cid; + u32 drv_iscsi_cid; + u16 pg_cid; + u16 rsvd; +}; + + +enum { + EP_STATE_IDLE = 0x0, + EP_STATE_PG_OFLD_START = 0x1, + EP_STATE_PG_OFLD_COMPL = 0x2, + EP_STATE_OFLD_START = 0x4, + EP_STATE_OFLD_COMPL = 0x8, + EP_STATE_CONNECT_START = 0x10, + EP_STATE_CONNECT_COMPL = 0x20, + EP_STATE_ULP_UPDATE_START = 0x40, + EP_STATE_ULP_UPDATE_COMPL = 0x80, + EP_STATE_DISCONN_START = 0x100, + EP_STATE_DISCONN_COMPL = 0x200, + EP_STATE_CLEANUP_START = 0x400, + EP_STATE_CLEANUP_CMPL = 0x800, + EP_STATE_TCP_FIN_RCVD = 0x1000, + EP_STATE_TCP_RST_RCVD = 0x2000, + EP_STATE_LOGOUT_SENT = 0x4000, + EP_STATE_LOGOUT_RESP_RCVD = 0x8000, + EP_STATE_PG_OFLD_FAILED = 0x1000000, + EP_STATE_ULP_UPDATE_FAILED = 0x2000000, + EP_STATE_CLEANUP_FAILED = 0x4000000, + EP_STATE_OFLD_FAILED = 0x8000000, + EP_STATE_CONNECT_FAILED = 0x10000000, + EP_STATE_DISCONN_TIMEDOUT = 0x20000000, + EP_STATE_OFLD_FAILED_CID_BUSY = 0x80000000, +}; + +/** + * struct bnx2i_endpoint - representation of tcp connection in NX2 world + * + * @link: list head to link elements + * @hba: adapter to which this connection belongs + * @conn: iscsi connection this EP is linked to + * @cls_ep: associated iSCSI endpoint pointer + * @cm_sk: cnic sock struct + * @hba_age: age to detect if 'iscsid' issues ep_disconnect() + * after HBA reset is completed by bnx2i/cnic/bnx2 + * modules + * @state: tracks offload connection state machine + * @timestamp: tracks the start time when the ep begins to connect + * @num_active_cmds: tracks the number of outstanding commands for this ep + * @ec_shift: the amount of shift as part of the event coal calc + * @qp: QP information + * @ids: contains chip allocated *context id* & driver assigned + * *iscsi cid* + * @ofld_timer: offload timer to detect timeout + * @ofld_wait: wait queue + * + * Endpoint Structure - equivalent of tcp socket structure + */ +struct bnx2i_endpoint { + struct list_head link; + struct bnx2i_hba *hba; + struct bnx2i_conn *conn; + struct iscsi_endpoint *cls_ep; + struct cnic_sock *cm_sk; + u32 hba_age; + u32 state; + unsigned long timestamp; + atomic_t num_active_cmds; + u32 ec_shift; + + struct qp_info qp; + struct ep_handles ids; + #define ep_iscsi_cid ids.drv_iscsi_cid + #define ep_cid ids.fw_cid + #define ep_pg_cid ids.pg_cid + struct timer_list ofld_timer; + wait_queue_head_t ofld_wait; +}; + + +struct bnx2i_work { + struct list_head list; + struct iscsi_session *session; + struct bnx2i_conn *bnx2i_conn; + struct cqe cqe; +}; + +struct bnx2i_percpu_s { + struct task_struct *iothread; + struct list_head work_list; + spinlock_t p_work_lock; +}; + + +/* Global variables */ +extern unsigned int error_mask1, error_mask2; +extern u64 iscsi_error_mask; +extern unsigned int en_tcp_dack; +extern unsigned int event_coal_div; +extern unsigned int event_coal_min; + +extern struct scsi_transport_template *bnx2i_scsi_xport_template; +extern struct iscsi_transport bnx2i_iscsi_transport; +extern struct cnic_ulp_ops bnx2i_cnic_cb; + +extern unsigned int sq_size; +extern unsigned int rq_size; + +extern struct device_attribute *bnx2i_dev_attributes[]; + + + +/* + * Function Prototypes + */ +extern void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev); + +extern void bnx2i_ulp_init(struct cnic_dev *dev); +extern void bnx2i_ulp_exit(struct cnic_dev *dev); +extern void bnx2i_start(void *handle); +extern void bnx2i_stop(void *handle); +extern int bnx2i_get_stats(void *handle); + +extern struct bnx2i_hba *get_adapter_list_head(void); + +struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba, + u16 iscsi_cid); + +int bnx2i_alloc_ep_pool(void); +void bnx2i_release_ep_pool(void); +struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba); +struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba); + +struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic); + +struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic); +void bnx2i_free_hba(struct bnx2i_hba *hba); + +void bnx2i_get_rq_buf(struct bnx2i_conn *conn, char *ptr, int len); +void bnx2i_put_rq_buf(struct bnx2i_conn *conn, int count); + +void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd); + +void bnx2i_drop_session(struct iscsi_cls_session *session); + +extern int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba); +extern int bnx2i_send_iscsi_login(struct bnx2i_conn *conn, + struct iscsi_task *mtask); +extern int bnx2i_send_iscsi_tmf(struct bnx2i_conn *conn, + struct iscsi_task *mtask); +extern int bnx2i_send_iscsi_text(struct bnx2i_conn *conn, + struct iscsi_task *mtask); +extern int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *conn, + struct bnx2i_cmd *cmnd); +extern int bnx2i_send_iscsi_nopout(struct bnx2i_conn *conn, + struct iscsi_task *mtask, + char *datap, int data_len, int unsol); +extern int bnx2i_send_iscsi_logout(struct bnx2i_conn *conn, + struct iscsi_task *mtask); +extern void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, + struct bnx2i_cmd *cmd); +extern int bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep); +extern void bnx2i_update_iscsi_conn(struct iscsi_conn *conn); +extern int bnx2i_send_conn_destroy(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep); + +extern int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep); +extern void bnx2i_free_qp_resc(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep); +extern void bnx2i_ep_ofld_timer(unsigned long data); +extern struct bnx2i_endpoint *bnx2i_find_ep_in_ofld_list( + struct bnx2i_hba *hba, u32 iscsi_cid); +extern struct bnx2i_endpoint *bnx2i_find_ep_in_destroy_list( + struct bnx2i_hba *hba, u32 iscsi_cid); + +extern int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep); +extern int bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action); + +extern int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep); + +/* Debug related function prototypes */ +extern void bnx2i_print_pend_cmd_queue(struct bnx2i_conn *conn); +extern void bnx2i_print_active_cmd_queue(struct bnx2i_conn *conn); +extern void bnx2i_print_xmit_pdu_queue(struct bnx2i_conn *conn); +extern void bnx2i_print_recv_state(struct bnx2i_conn *conn); + +extern int bnx2i_percpu_io_thread(void *arg); +extern int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe); +#endif diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c new file mode 100644 index 000000000..fb072cc5e --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -0,0 +1,2771 @@ +/* bnx2i_hwi.c: QLogic NetXtreme II iSCSI driver. + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mike Christie + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ + +#include <linux/gfp.h> +#include <scsi/scsi_tcq.h> +#include <scsi/libiscsi.h> +#include "bnx2i.h" + +DECLARE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu); + +/** + * bnx2i_get_cid_num - get cid from ep + * @ep: endpoint pointer + * + * Only applicable to 57710 family of devices + */ +static u32 bnx2i_get_cid_num(struct bnx2i_endpoint *ep) +{ + u32 cid; + + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) + cid = ep->ep_cid; + else + cid = GET_CID_NUM(ep->ep_cid); + return cid; +} + + +/** + * bnx2i_adjust_qp_size - Adjust SQ/RQ/CQ size for 57710 device type + * @hba: Adapter for which adjustments is to be made + * + * Only applicable to 57710 family of devices + */ +static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba) +{ + u32 num_elements_per_pg; + + if (test_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type) || + test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type) || + test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) { + if (!is_power_of_2(hba->max_sqes)) + hba->max_sqes = rounddown_pow_of_two(hba->max_sqes); + + if (!is_power_of_2(hba->max_rqes)) + hba->max_rqes = rounddown_pow_of_two(hba->max_rqes); + } + + /* Adjust each queue size if the user selection does not + * yield integral num of page buffers + */ + /* adjust SQ */ + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE; + if (hba->max_sqes < num_elements_per_pg) + hba->max_sqes = num_elements_per_pg; + else if (hba->max_sqes % num_elements_per_pg) + hba->max_sqes = (hba->max_sqes + num_elements_per_pg - 1) & + ~(num_elements_per_pg - 1); + + /* adjust CQ */ + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_CQE_SIZE; + if (hba->max_cqes < num_elements_per_pg) + hba->max_cqes = num_elements_per_pg; + else if (hba->max_cqes % num_elements_per_pg) + hba->max_cqes = (hba->max_cqes + num_elements_per_pg - 1) & + ~(num_elements_per_pg - 1); + + /* adjust RQ */ + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_RQ_WQE_SIZE; + if (hba->max_rqes < num_elements_per_pg) + hba->max_rqes = num_elements_per_pg; + else if (hba->max_rqes % num_elements_per_pg) + hba->max_rqes = (hba->max_rqes + num_elements_per_pg - 1) & + ~(num_elements_per_pg - 1); +} + + +/** + * bnx2i_get_link_state - get network interface link state + * @hba: adapter instance pointer + * + * updates adapter structure flag based on netdev state + */ +static void bnx2i_get_link_state(struct bnx2i_hba *hba) +{ + if (test_bit(__LINK_STATE_NOCARRIER, &hba->netdev->state)) + set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state); + else + clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state); +} + + +/** + * bnx2i_iscsi_license_error - displays iscsi license related error message + * @hba: adapter instance pointer + * @error_code: error classification + * + * Puts out an error log when driver is unable to offload iscsi connection + * due to license restrictions + */ +static void bnx2i_iscsi_license_error(struct bnx2i_hba *hba, u32 error_code) +{ + if (error_code == ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED) + /* iSCSI offload not supported on this device */ + printk(KERN_ERR "bnx2i: iSCSI not supported, dev=%s\n", + hba->netdev->name); + if (error_code == ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED) + /* iSCSI offload not supported on this LOM device */ + printk(KERN_ERR "bnx2i: LOM is not enable to " + "offload iSCSI connections, dev=%s\n", + hba->netdev->name); + set_bit(ADAPTER_STATE_INIT_FAILED, &hba->adapter_state); +} + + +/** + * bnx2i_arm_cq_event_coalescing - arms CQ to enable EQ notification + * @ep: endpoint (transport identifier) structure + * @action: action, ARM or DISARM. For now only ARM_CQE is used + * + * Arm'ing CQ will enable chip to generate global EQ events inorder to interrupt + * the driver. EQ event is generated CQ index is hit or at least 1 CQ is + * outstanding and on chip timer expires + */ +int bnx2i_arm_cq_event_coalescing(struct bnx2i_endpoint *ep, u8 action) +{ + struct bnx2i_5771x_cq_db *cq_db; + u16 cq_index; + u16 next_index = 0; + u32 num_active_cmds; + + /* Coalesce CQ entries only on 10G devices */ + if (!test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) + return 0; + + /* Do not update CQ DB multiple times before firmware writes + * '0xFFFF' to CQDB->SQN field. Deviation may cause spurious + * interrupts and other unwanted results + */ + cq_db = (struct bnx2i_5771x_cq_db *) ep->qp.cq_pgtbl_virt; + + if (action != CNIC_ARM_CQE_FP) + if (cq_db->sqn[0] && cq_db->sqn[0] != 0xFFFF) + return 0; + + if (action == CNIC_ARM_CQE || action == CNIC_ARM_CQE_FP) { + num_active_cmds = atomic_read(&ep->num_active_cmds); + if (num_active_cmds <= event_coal_min) + next_index = 1; + else { + next_index = num_active_cmds >> ep->ec_shift; + if (next_index > num_active_cmds - event_coal_min) + next_index = num_active_cmds - event_coal_min; + } + if (!next_index) + next_index = 1; + cq_index = ep->qp.cqe_exp_seq_sn + next_index - 1; + if (cq_index > ep->qp.cqe_size * 2) + cq_index -= ep->qp.cqe_size * 2; + if (!cq_index) + cq_index = 1; + + cq_db->sqn[0] = cq_index; + } + return next_index; +} + + +/** + * bnx2i_get_rq_buf - copy RQ buffer contents to driver buffer + * @conn: iscsi connection on which RQ event occurred + * @ptr: driver buffer to which RQ buffer contents is to + * be copied + * @len: length of valid data inside RQ buf + * + * Copies RQ buffer contents from shared (DMA'able) memory region to + * driver buffer. RQ is used to DMA unsolicitated iscsi pdu's and + * scsi sense info + */ +void bnx2i_get_rq_buf(struct bnx2i_conn *bnx2i_conn, char *ptr, int len) +{ + if (!bnx2i_conn->ep->qp.rqe_left) + return; + + bnx2i_conn->ep->qp.rqe_left--; + memcpy(ptr, (u8 *) bnx2i_conn->ep->qp.rq_cons_qe, len); + if (bnx2i_conn->ep->qp.rq_cons_qe == bnx2i_conn->ep->qp.rq_last_qe) { + bnx2i_conn->ep->qp.rq_cons_qe = bnx2i_conn->ep->qp.rq_first_qe; + bnx2i_conn->ep->qp.rq_cons_idx = 0; + } else { + bnx2i_conn->ep->qp.rq_cons_qe++; + bnx2i_conn->ep->qp.rq_cons_idx++; + } +} + + +static void bnx2i_ring_577xx_doorbell(struct bnx2i_conn *conn) +{ + struct bnx2i_5771x_dbell dbell; + u32 msg; + + memset(&dbell, 0, sizeof(dbell)); + dbell.dbell.header = (B577XX_ISCSI_CONNECTION_TYPE << + B577XX_DOORBELL_HDR_CONN_TYPE_SHIFT); + msg = *((u32 *)&dbell); + /* TODO : get doorbell register mapping */ + writel(cpu_to_le32(msg), conn->ep->qp.ctx_base); +} + + +/** + * bnx2i_put_rq_buf - Replenish RQ buffer, if required ring on chip doorbell + * @conn: iscsi connection on which event to post + * @count: number of RQ buffer being posted to chip + * + * No need to ring hardware doorbell for 57710 family of devices + */ +void bnx2i_put_rq_buf(struct bnx2i_conn *bnx2i_conn, int count) +{ + struct bnx2i_5771x_sq_rq_db *rq_db; + u16 hi_bit = (bnx2i_conn->ep->qp.rq_prod_idx & 0x8000); + struct bnx2i_endpoint *ep = bnx2i_conn->ep; + + ep->qp.rqe_left += count; + ep->qp.rq_prod_idx &= 0x7FFF; + ep->qp.rq_prod_idx += count; + + if (ep->qp.rq_prod_idx > bnx2i_conn->hba->max_rqes) { + ep->qp.rq_prod_idx %= bnx2i_conn->hba->max_rqes; + if (!hi_bit) + ep->qp.rq_prod_idx |= 0x8000; + } else + ep->qp.rq_prod_idx |= hi_bit; + + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) { + rq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.rq_pgtbl_virt; + rq_db->prod_idx = ep->qp.rq_prod_idx; + /* no need to ring hardware doorbell for 57710 */ + } else { + writew(ep->qp.rq_prod_idx, + ep->qp.ctx_base + CNIC_RECV_DOORBELL); + } + mmiowb(); +} + + +/** + * bnx2i_ring_sq_dbell - Ring SQ doorbell to wake-up the processing engine + * @conn: iscsi connection to which new SQ entries belong + * @count: number of SQ WQEs to post + * + * SQ DB is updated in host memory and TX Doorbell is rung for 57710 family + * of devices. For 5706/5708/5709 new SQ WQE count is written into the + * doorbell register + */ +static void bnx2i_ring_sq_dbell(struct bnx2i_conn *bnx2i_conn, int count) +{ + struct bnx2i_5771x_sq_rq_db *sq_db; + struct bnx2i_endpoint *ep = bnx2i_conn->ep; + + atomic_inc(&ep->num_active_cmds); + wmb(); /* flush SQ WQE memory before the doorbell is rung */ + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) { + sq_db = (struct bnx2i_5771x_sq_rq_db *) ep->qp.sq_pgtbl_virt; + sq_db->prod_idx = ep->qp.sq_prod_idx; + bnx2i_ring_577xx_doorbell(bnx2i_conn); + } else + writew(count, ep->qp.ctx_base + CNIC_SEND_DOORBELL); + + mmiowb(); /* flush posted PCI writes */ +} + + +/** + * bnx2i_ring_dbell_update_sq_params - update SQ driver parameters + * @conn: iscsi connection to which new SQ entries belong + * @count: number of SQ WQEs to post + * + * this routine will update SQ driver parameters and ring the doorbell + */ +static void bnx2i_ring_dbell_update_sq_params(struct bnx2i_conn *bnx2i_conn, + int count) +{ + int tmp_cnt; + + if (count == 1) { + if (bnx2i_conn->ep->qp.sq_prod_qe == + bnx2i_conn->ep->qp.sq_last_qe) + bnx2i_conn->ep->qp.sq_prod_qe = + bnx2i_conn->ep->qp.sq_first_qe; + else + bnx2i_conn->ep->qp.sq_prod_qe++; + } else { + if ((bnx2i_conn->ep->qp.sq_prod_qe + count) <= + bnx2i_conn->ep->qp.sq_last_qe) + bnx2i_conn->ep->qp.sq_prod_qe += count; + else { + tmp_cnt = bnx2i_conn->ep->qp.sq_last_qe - + bnx2i_conn->ep->qp.sq_prod_qe; + bnx2i_conn->ep->qp.sq_prod_qe = + &bnx2i_conn->ep->qp.sq_first_qe[count - + (tmp_cnt + 1)]; + } + } + bnx2i_conn->ep->qp.sq_prod_idx += count; + /* Ring the doorbell */ + bnx2i_ring_sq_dbell(bnx2i_conn, bnx2i_conn->ep->qp.sq_prod_idx); +} + + +/** + * bnx2i_send_iscsi_login - post iSCSI login request MP WQE to hardware + * @conn: iscsi connection + * @cmd: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepare and post an iSCSI Login request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn, + struct iscsi_task *task) +{ + struct bnx2i_cmd *bnx2i_cmd; + struct bnx2i_login_request *login_wqe; + struct iscsi_login_req *login_hdr; + u32 dword; + + bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data; + login_hdr = (struct iscsi_login_req *)task->hdr; + login_wqe = (struct bnx2i_login_request *) + bnx2i_conn->ep->qp.sq_prod_qe; + + login_wqe->op_code = login_hdr->opcode; + login_wqe->op_attr = login_hdr->flags; + login_wqe->version_max = login_hdr->max_version; + login_wqe->version_min = login_hdr->min_version; + login_wqe->data_length = ntoh24(login_hdr->dlength); + login_wqe->isid_lo = *((u32 *) login_hdr->isid); + login_wqe->isid_hi = *((u16 *) login_hdr->isid + 2); + login_wqe->tsih = login_hdr->tsih; + login_wqe->itt = task->itt | + (ISCSI_TASK_TYPE_MPATH << ISCSI_LOGIN_REQUEST_TYPE_SHIFT); + login_wqe->cid = login_hdr->cid; + + login_wqe->cmd_sn = be32_to_cpu(login_hdr->cmdsn); + login_wqe->exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn); + login_wqe->flags = ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN; + + login_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma; + login_wqe->resp_bd_list_addr_hi = + (u32) ((u64) bnx2i_conn->gen_pdu.resp_bd_dma >> 32); + + dword = ((1 << ISCSI_LOGIN_REQUEST_NUM_RESP_BDS_SHIFT) | + (bnx2i_conn->gen_pdu.resp_buf_size << + ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT)); + login_wqe->resp_buffer = dword; + login_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma; + login_wqe->bd_list_addr_hi = + (u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32); + login_wqe->num_bds = 1; + login_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + +/** + * bnx2i_send_iscsi_tmf - post iSCSI task management request MP WQE to hardware + * @conn: iscsi connection + * @mtask: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepare and post an iSCSI Login request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_tmf(struct bnx2i_conn *bnx2i_conn, + struct iscsi_task *mtask) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_tm *tmfabort_hdr; + struct scsi_cmnd *ref_sc; + struct iscsi_task *ctask; + struct bnx2i_cmd *bnx2i_cmd; + struct bnx2i_tmf_request *tmfabort_wqe; + u32 dword; + u32 scsi_lun[2]; + + bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data; + tmfabort_hdr = (struct iscsi_tm *)mtask->hdr; + tmfabort_wqe = (struct bnx2i_tmf_request *) + bnx2i_conn->ep->qp.sq_prod_qe; + + tmfabort_wqe->op_code = tmfabort_hdr->opcode; + tmfabort_wqe->op_attr = tmfabort_hdr->flags; + + tmfabort_wqe->itt = (mtask->itt | (ISCSI_TASK_TYPE_MPATH << 14)); + tmfabort_wqe->reserved2 = 0; + tmfabort_wqe->cmd_sn = be32_to_cpu(tmfabort_hdr->cmdsn); + + switch (tmfabort_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) { + case ISCSI_TM_FUNC_ABORT_TASK: + case ISCSI_TM_FUNC_TASK_REASSIGN: + ctask = iscsi_itt_to_task(conn, tmfabort_hdr->rtt); + if (!ctask || !ctask->sc) + /* + * the iscsi layer must have completed the cmd while + * was starting up. + * + * Note: In the case of a SCSI cmd timeout, the task's + * sc is still active; hence ctask->sc != 0 + * In this case, the task must be aborted + */ + return 0; + + ref_sc = ctask->sc; + if (ref_sc->sc_data_direction == DMA_TO_DEVICE) + dword = (ISCSI_TASK_TYPE_WRITE << + ISCSI_CMD_REQUEST_TYPE_SHIFT); + else + dword = (ISCSI_TASK_TYPE_READ << + ISCSI_CMD_REQUEST_TYPE_SHIFT); + tmfabort_wqe->ref_itt = (dword | + (tmfabort_hdr->rtt & ISCSI_ITT_MASK)); + break; + default: + tmfabort_wqe->ref_itt = RESERVED_ITT; + } + memcpy(scsi_lun, &tmfabort_hdr->lun, sizeof(struct scsi_lun)); + tmfabort_wqe->lun[0] = be32_to_cpu(scsi_lun[0]); + tmfabort_wqe->lun[1] = be32_to_cpu(scsi_lun[1]); + + tmfabort_wqe->ref_cmd_sn = be32_to_cpu(tmfabort_hdr->refcmdsn); + + tmfabort_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma; + tmfabort_wqe->bd_list_addr_hi = (u32) + ((u64) bnx2i_conn->hba->mp_bd_dma >> 32); + tmfabort_wqe->num_bds = 1; + tmfabort_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + +/** + * bnx2i_send_iscsi_text - post iSCSI text WQE to hardware + * @conn: iscsi connection + * @mtask: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepare and post an iSCSI Text request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_text(struct bnx2i_conn *bnx2i_conn, + struct iscsi_task *mtask) +{ + struct bnx2i_cmd *bnx2i_cmd; + struct bnx2i_text_request *text_wqe; + struct iscsi_text *text_hdr; + u32 dword; + + bnx2i_cmd = (struct bnx2i_cmd *)mtask->dd_data; + text_hdr = (struct iscsi_text *)mtask->hdr; + text_wqe = (struct bnx2i_text_request *) bnx2i_conn->ep->qp.sq_prod_qe; + + memset(text_wqe, 0, sizeof(struct bnx2i_text_request)); + + text_wqe->op_code = text_hdr->opcode; + text_wqe->op_attr = text_hdr->flags; + text_wqe->data_length = ntoh24(text_hdr->dlength); + text_wqe->itt = mtask->itt | + (ISCSI_TASK_TYPE_MPATH << ISCSI_TEXT_REQUEST_TYPE_SHIFT); + text_wqe->ttt = be32_to_cpu(text_hdr->ttt); + + text_wqe->cmd_sn = be32_to_cpu(text_hdr->cmdsn); + + text_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma; + text_wqe->resp_bd_list_addr_hi = + (u32) ((u64) bnx2i_conn->gen_pdu.resp_bd_dma >> 32); + + dword = ((1 << ISCSI_TEXT_REQUEST_NUM_RESP_BDS_SHIFT) | + (bnx2i_conn->gen_pdu.resp_buf_size << + ISCSI_TEXT_REQUEST_RESP_BUFFER_LENGTH_SHIFT)); + text_wqe->resp_buffer = dword; + text_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma; + text_wqe->bd_list_addr_hi = + (u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32); + text_wqe->num_bds = 1; + text_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + + +/** + * bnx2i_send_iscsi_scsicmd - post iSCSI scsicmd request WQE to hardware + * @conn: iscsi connection + * @cmd: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepare and post an iSCSI SCSI-CMD request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_scsicmd(struct bnx2i_conn *bnx2i_conn, + struct bnx2i_cmd *cmd) +{ + struct bnx2i_cmd_request *scsi_cmd_wqe; + + scsi_cmd_wqe = (struct bnx2i_cmd_request *) + bnx2i_conn->ep->qp.sq_prod_qe; + memcpy(scsi_cmd_wqe, &cmd->req, sizeof(struct bnx2i_cmd_request)); + scsi_cmd_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + +/** + * bnx2i_send_iscsi_nopout - post iSCSI NOPOUT request WQE to hardware + * @conn: iscsi connection + * @cmd: driver command structure which is requesting + * a WQE to sent to chip for further processing + * @datap: payload buffer pointer + * @data_len: payload data length + * @unsol: indicated whether nopout pdu is unsolicited pdu or + * in response to target's NOPIN w/ TTT != FFFFFFFF + * + * prepare and post a nopout request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_nopout(struct bnx2i_conn *bnx2i_conn, + struct iscsi_task *task, + char *datap, int data_len, int unsol) +{ + struct bnx2i_endpoint *ep = bnx2i_conn->ep; + struct bnx2i_cmd *bnx2i_cmd; + struct bnx2i_nop_out_request *nopout_wqe; + struct iscsi_nopout *nopout_hdr; + + bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data; + nopout_hdr = (struct iscsi_nopout *)task->hdr; + nopout_wqe = (struct bnx2i_nop_out_request *)ep->qp.sq_prod_qe; + + memset(nopout_wqe, 0x00, sizeof(struct bnx2i_nop_out_request)); + + nopout_wqe->op_code = nopout_hdr->opcode; + nopout_wqe->op_attr = ISCSI_FLAG_CMD_FINAL; + memcpy(nopout_wqe->lun, &nopout_hdr->lun, 8); + + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) { + u32 tmp = nopout_wqe->lun[0]; + /* 57710 requires LUN field to be swapped */ + nopout_wqe->lun[0] = nopout_wqe->lun[1]; + nopout_wqe->lun[1] = tmp; + } + + nopout_wqe->itt = ((u16)task->itt | + (ISCSI_TASK_TYPE_MPATH << + ISCSI_TMF_REQUEST_TYPE_SHIFT)); + nopout_wqe->ttt = be32_to_cpu(nopout_hdr->ttt); + nopout_wqe->flags = 0; + if (!unsol) + nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION; + else if (nopout_hdr->itt == RESERVED_ITT) + nopout_wqe->flags = ISCSI_NOP_OUT_REQUEST_LOCAL_COMPLETION; + + nopout_wqe->cmd_sn = be32_to_cpu(nopout_hdr->cmdsn); + nopout_wqe->data_length = data_len; + if (data_len) { + /* handle payload data, not required in first release */ + printk(KERN_ALERT "NOPOUT: WARNING!! payload len != 0\n"); + } else { + nopout_wqe->bd_list_addr_lo = (u32) + bnx2i_conn->hba->mp_bd_dma; + nopout_wqe->bd_list_addr_hi = + (u32) ((u64) bnx2i_conn->hba->mp_bd_dma >> 32); + nopout_wqe->num_bds = 1; + } + nopout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + + +/** + * bnx2i_send_iscsi_logout - post iSCSI logout request WQE to hardware + * @conn: iscsi connection + * @cmd: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepare and post logout request WQE to CNIC firmware + */ +int bnx2i_send_iscsi_logout(struct bnx2i_conn *bnx2i_conn, + struct iscsi_task *task) +{ + struct bnx2i_cmd *bnx2i_cmd; + struct bnx2i_logout_request *logout_wqe; + struct iscsi_logout *logout_hdr; + + bnx2i_cmd = (struct bnx2i_cmd *)task->dd_data; + logout_hdr = (struct iscsi_logout *)task->hdr; + + logout_wqe = (struct bnx2i_logout_request *) + bnx2i_conn->ep->qp.sq_prod_qe; + memset(logout_wqe, 0x00, sizeof(struct bnx2i_logout_request)); + + logout_wqe->op_code = logout_hdr->opcode; + logout_wqe->cmd_sn = be32_to_cpu(logout_hdr->cmdsn); + logout_wqe->op_attr = + logout_hdr->flags | ISCSI_LOGOUT_REQUEST_ALWAYS_ONE; + logout_wqe->itt = ((u16)task->itt | + (ISCSI_TASK_TYPE_MPATH << + ISCSI_LOGOUT_REQUEST_TYPE_SHIFT)); + logout_wqe->data_length = 0; + logout_wqe->cid = 0; + + logout_wqe->bd_list_addr_lo = (u32) bnx2i_conn->hba->mp_bd_dma; + logout_wqe->bd_list_addr_hi = (u32) + ((u64) bnx2i_conn->hba->mp_bd_dma >> 32); + logout_wqe->num_bds = 1; + logout_wqe->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_conn->ep->state = EP_STATE_LOGOUT_SENT; + + bnx2i_ring_dbell_update_sq_params(bnx2i_conn, 1); + return 0; +} + + +/** + * bnx2i_update_iscsi_conn - post iSCSI logout request WQE to hardware + * @conn: iscsi connection which requires iscsi parameter update + * + * sends down iSCSI Conn Update request to move iSCSI conn to FFP + */ +void bnx2i_update_iscsi_conn(struct iscsi_conn *conn) +{ + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; + struct kwqe *kwqe_arr[2]; + struct iscsi_kwqe_conn_update *update_wqe; + struct iscsi_kwqe_conn_update conn_update_kwqe; + + update_wqe = &conn_update_kwqe; + + update_wqe->hdr.op_code = ISCSI_KWQE_OPCODE_UPDATE_CONN; + update_wqe->hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + /* 5771x requires conn context id to be passed as is */ + if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_conn->ep->hba->cnic_dev_type)) + update_wqe->context_id = bnx2i_conn->ep->ep_cid; + else + update_wqe->context_id = (bnx2i_conn->ep->ep_cid >> 7); + update_wqe->conn_flags = 0; + if (conn->hdrdgst_en) + update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_HEADER_DIGEST; + if (conn->datadgst_en) + update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_DATA_DIGEST; + if (conn->session->initial_r2t_en) + update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_INITIAL_R2T; + if (conn->session->imm_data_en) + update_wqe->conn_flags |= ISCSI_KWQE_CONN_UPDATE_IMMEDIATE_DATA; + + update_wqe->max_send_pdu_length = conn->max_xmit_dlength; + update_wqe->max_recv_pdu_length = conn->max_recv_dlength; + update_wqe->first_burst_length = conn->session->first_burst; + update_wqe->max_burst_length = conn->session->max_burst; + update_wqe->exp_stat_sn = conn->exp_statsn; + update_wqe->max_outstanding_r2ts = conn->session->max_r2t; + update_wqe->session_error_recovery_level = conn->session->erl; + iscsi_conn_printk(KERN_ALERT, conn, + "bnx2i: conn update - MBL 0x%x FBL 0x%x" + "MRDSL_I 0x%x MRDSL_T 0x%x \n", + update_wqe->max_burst_length, + update_wqe->first_burst_length, + update_wqe->max_recv_pdu_length, + update_wqe->max_send_pdu_length); + + kwqe_arr[0] = (struct kwqe *) update_wqe; + if (hba->cnic && hba->cnic->submit_kwqes) + hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1); +} + + +/** + * bnx2i_ep_ofld_timer - post iSCSI logout request WQE to hardware + * @data: endpoint (transport handle) structure pointer + * + * routine to handle connection offload/destroy request timeout + */ +void bnx2i_ep_ofld_timer(unsigned long data) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) data; + + if (ep->state == EP_STATE_OFLD_START) { + printk(KERN_ALERT "ofld_timer: CONN_OFLD timeout\n"); + ep->state = EP_STATE_OFLD_FAILED; + } else if (ep->state == EP_STATE_DISCONN_START) { + printk(KERN_ALERT "ofld_timer: CONN_DISCON timeout\n"); + ep->state = EP_STATE_DISCONN_TIMEDOUT; + } else if (ep->state == EP_STATE_CLEANUP_START) { + printk(KERN_ALERT "ofld_timer: CONN_CLEANUP timeout\n"); + ep->state = EP_STATE_CLEANUP_FAILED; + } + + wake_up_interruptible(&ep->ofld_wait); +} + + +static int bnx2i_power_of2(u32 val) +{ + u32 power = 0; + if (val & (val - 1)) + return power; + val--; + while (val) { + val = val >> 1; + power++; + } + return power; +} + + +/** + * bnx2i_send_cmd_cleanup_req - send iscsi cmd context clean-up request + * @hba: adapter structure pointer + * @cmd: driver command structure which is requesting + * a WQE to sent to chip for further processing + * + * prepares and posts CONN_OFLD_REQ1/2 KWQE + */ +void bnx2i_send_cmd_cleanup_req(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd) +{ + struct bnx2i_cleanup_request *cmd_cleanup; + + cmd_cleanup = + (struct bnx2i_cleanup_request *)cmd->conn->ep->qp.sq_prod_qe; + memset(cmd_cleanup, 0x00, sizeof(struct bnx2i_cleanup_request)); + + cmd_cleanup->op_code = ISCSI_OPCODE_CLEANUP_REQUEST; + cmd_cleanup->itt = cmd->req.itt; + cmd_cleanup->cq_index = 0; /* CQ# used for completion, 5771x only */ + + bnx2i_ring_dbell_update_sq_params(cmd->conn, 1); +} + + +/** + * bnx2i_send_conn_destroy - initiates iscsi connection teardown process + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE to initiate + * iscsi connection context clean-up process + */ +int bnx2i_send_conn_destroy(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) +{ + struct kwqe *kwqe_arr[2]; + struct iscsi_kwqe_conn_destroy conn_cleanup; + int rc = -EINVAL; + + memset(&conn_cleanup, 0x00, sizeof(struct iscsi_kwqe_conn_destroy)); + + conn_cleanup.hdr.op_code = ISCSI_KWQE_OPCODE_DESTROY_CONN; + conn_cleanup.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + /* 5771x requires conn context id to be passed as is */ + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) + conn_cleanup.context_id = ep->ep_cid; + else + conn_cleanup.context_id = (ep->ep_cid >> 7); + + conn_cleanup.reserved0 = (u16)ep->ep_iscsi_cid; + + kwqe_arr[0] = (struct kwqe *) &conn_cleanup; + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 1); + + return rc; +} + + +/** + * bnx2i_570x_send_conn_ofld_req - initiates iscsi conn context setup process + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * 5706/5708/5709 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE + */ +static int bnx2i_570x_send_conn_ofld_req(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + struct kwqe *kwqe_arr[2]; + struct iscsi_kwqe_conn_offload1 ofld_req1; + struct iscsi_kwqe_conn_offload2 ofld_req2; + dma_addr_t dma_addr; + int num_kwqes = 2; + u32 *ptbl; + int rc = -EINVAL; + + ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1; + ofld_req1.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid; + + dma_addr = ep->qp.sq_pgtbl_phys; + ofld_req1.sq_page_table_addr_lo = (u32) dma_addr; + ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + dma_addr = ep->qp.cq_pgtbl_phys; + ofld_req1.cq_page_table_addr_lo = (u32) dma_addr; + ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2; + ofld_req2.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + dma_addr = ep->qp.rq_pgtbl_phys; + ofld_req2.rq_page_table_addr_lo = (u32) dma_addr; + ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + ptbl = (u32 *) ep->qp.sq_pgtbl_virt; + + ofld_req2.sq_first_pte.hi = *ptbl++; + ofld_req2.sq_first_pte.lo = *ptbl; + + ptbl = (u32 *) ep->qp.cq_pgtbl_virt; + ofld_req2.cq_first_pte.hi = *ptbl++; + ofld_req2.cq_first_pte.lo = *ptbl; + + kwqe_arr[0] = (struct kwqe *) &ofld_req1; + kwqe_arr[1] = (struct kwqe *) &ofld_req2; + ofld_req2.num_additional_wqes = 0; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + + +/** + * bnx2i_5771x_send_conn_ofld_req - initiates iscsi connection context creation + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * 57710 specific - prepares and posts CONN_OFLD_REQ1/2 KWQE + */ +static int bnx2i_5771x_send_conn_ofld_req(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + struct kwqe *kwqe_arr[5]; + struct iscsi_kwqe_conn_offload1 ofld_req1; + struct iscsi_kwqe_conn_offload2 ofld_req2; + struct iscsi_kwqe_conn_offload3 ofld_req3[1]; + dma_addr_t dma_addr; + int num_kwqes = 2; + u32 *ptbl; + int rc = -EINVAL; + + ofld_req1.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN1; + ofld_req1.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + ofld_req1.iscsi_conn_id = (u16) ep->ep_iscsi_cid; + + dma_addr = ep->qp.sq_pgtbl_phys + ISCSI_SQ_DB_SIZE; + ofld_req1.sq_page_table_addr_lo = (u32) dma_addr; + ofld_req1.sq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + dma_addr = ep->qp.cq_pgtbl_phys + ISCSI_CQ_DB_SIZE; + ofld_req1.cq_page_table_addr_lo = (u32) dma_addr; + ofld_req1.cq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + ofld_req2.hdr.op_code = ISCSI_KWQE_OPCODE_OFFLOAD_CONN2; + ofld_req2.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + dma_addr = ep->qp.rq_pgtbl_phys + ISCSI_RQ_DB_SIZE; + ofld_req2.rq_page_table_addr_lo = (u32) dma_addr; + ofld_req2.rq_page_table_addr_hi = (u32) ((u64) dma_addr >> 32); + + ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE); + ofld_req2.sq_first_pte.hi = *ptbl++; + ofld_req2.sq_first_pte.lo = *ptbl; + + ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE); + ofld_req2.cq_first_pte.hi = *ptbl++; + ofld_req2.cq_first_pte.lo = *ptbl; + + kwqe_arr[0] = (struct kwqe *) &ofld_req1; + kwqe_arr[1] = (struct kwqe *) &ofld_req2; + + ofld_req2.num_additional_wqes = 1; + memset(ofld_req3, 0x00, sizeof(ofld_req3[0])); + ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE); + ofld_req3[0].qp_first_pte[0].hi = *ptbl++; + ofld_req3[0].qp_first_pte[0].lo = *ptbl; + + kwqe_arr[2] = (struct kwqe *) ofld_req3; + /* need if we decide to go with multiple KCQE's per conn */ + num_kwqes += 1; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, num_kwqes); + + return rc; +} + +/** + * bnx2i_send_conn_ofld_req - initiates iscsi connection context setup process + * + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * this routine prepares and posts CONN_OFLD_REQ1/2 KWQE + */ +int bnx2i_send_conn_ofld_req(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) +{ + int rc; + + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) + rc = bnx2i_5771x_send_conn_ofld_req(hba, ep); + else + rc = bnx2i_570x_send_conn_ofld_req(hba, ep); + + return rc; +} + + +/** + * setup_qp_page_tables - iscsi QP page table setup function + * @ep: endpoint (transport identifier) structure + * + * Sets up page tables for SQ/RQ/CQ, 1G/sec (5706/5708/5709) devices requires + * 64-bit address in big endian format. Whereas 10G/sec (57710) requires + * PT in little endian format + */ +static void setup_qp_page_tables(struct bnx2i_endpoint *ep) +{ + int num_pages; + u32 *ptbl; + dma_addr_t page; + int cnic_dev_10g; + + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) + cnic_dev_10g = 1; + else + cnic_dev_10g = 0; + + /* SQ page table */ + memset(ep->qp.sq_pgtbl_virt, 0, ep->qp.sq_pgtbl_size); + num_pages = ep->qp.sq_mem_size / CNIC_PAGE_SIZE; + page = ep->qp.sq_phys; + + if (cnic_dev_10g) + ptbl = (u32 *)((u8 *)ep->qp.sq_pgtbl_virt + ISCSI_SQ_DB_SIZE); + else + ptbl = (u32 *) ep->qp.sq_pgtbl_virt; + while (num_pages--) { + if (cnic_dev_10g) { + /* PTE is written in little endian format for 57710 */ + *ptbl = (u32) page; + ptbl++; + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + page += CNIC_PAGE_SIZE; + } else { + /* PTE is written in big endian format for + * 5706/5708/5709 devices */ + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + *ptbl = (u32) page; + ptbl++; + page += CNIC_PAGE_SIZE; + } + } + + /* RQ page table */ + memset(ep->qp.rq_pgtbl_virt, 0, ep->qp.rq_pgtbl_size); + num_pages = ep->qp.rq_mem_size / CNIC_PAGE_SIZE; + page = ep->qp.rq_phys; + + if (cnic_dev_10g) + ptbl = (u32 *)((u8 *)ep->qp.rq_pgtbl_virt + ISCSI_RQ_DB_SIZE); + else + ptbl = (u32 *) ep->qp.rq_pgtbl_virt; + while (num_pages--) { + if (cnic_dev_10g) { + /* PTE is written in little endian format for 57710 */ + *ptbl = (u32) page; + ptbl++; + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + page += CNIC_PAGE_SIZE; + } else { + /* PTE is written in big endian format for + * 5706/5708/5709 devices */ + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + *ptbl = (u32) page; + ptbl++; + page += CNIC_PAGE_SIZE; + } + } + + /* CQ page table */ + memset(ep->qp.cq_pgtbl_virt, 0, ep->qp.cq_pgtbl_size); + num_pages = ep->qp.cq_mem_size / CNIC_PAGE_SIZE; + page = ep->qp.cq_phys; + + if (cnic_dev_10g) + ptbl = (u32 *)((u8 *)ep->qp.cq_pgtbl_virt + ISCSI_CQ_DB_SIZE); + else + ptbl = (u32 *) ep->qp.cq_pgtbl_virt; + while (num_pages--) { + if (cnic_dev_10g) { + /* PTE is written in little endian format for 57710 */ + *ptbl = (u32) page; + ptbl++; + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + page += CNIC_PAGE_SIZE; + } else { + /* PTE is written in big endian format for + * 5706/5708/5709 devices */ + *ptbl = (u32) ((u64) page >> 32); + ptbl++; + *ptbl = (u32) page; + ptbl++; + page += CNIC_PAGE_SIZE; + } + } +} + + +/** + * bnx2i_alloc_qp_resc - allocates required resources for QP. + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * Allocate QP (transport layer for iSCSI connection) resources, DMA'able + * memory for SQ/RQ/CQ and page tables. EP structure elements such + * as producer/consumer indexes/pointers, queue sizes and page table + * contents are setup + */ +int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) +{ + struct bnx2i_5771x_cq_db *cq_db; + + ep->hba = hba; + ep->conn = NULL; + ep->ep_cid = ep->ep_iscsi_cid = ep->ep_pg_cid = 0; + + /* Allocate page table memory for SQ which is page aligned */ + ep->qp.sq_mem_size = hba->max_sqes * BNX2I_SQ_WQE_SIZE; + ep->qp.sq_mem_size = + (ep->qp.sq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + ep->qp.sq_pgtbl_size = + (ep->qp.sq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); + ep->qp.sq_pgtbl_size = + (ep->qp.sq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + + ep->qp.sq_pgtbl_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size, + &ep->qp.sq_pgtbl_phys, GFP_KERNEL); + if (!ep->qp.sq_pgtbl_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc SQ PT mem (%d)\n", + ep->qp.sq_pgtbl_size); + goto mem_alloc_err; + } + + /* Allocate memory area for actual SQ element */ + ep->qp.sq_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size, + &ep->qp.sq_phys, GFP_KERNEL); + if (!ep->qp.sq_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc SQ BD memory %d\n", + ep->qp.sq_mem_size); + goto mem_alloc_err; + } + + memset(ep->qp.sq_virt, 0x00, ep->qp.sq_mem_size); + ep->qp.sq_first_qe = ep->qp.sq_virt; + ep->qp.sq_prod_qe = ep->qp.sq_first_qe; + ep->qp.sq_cons_qe = ep->qp.sq_first_qe; + ep->qp.sq_last_qe = &ep->qp.sq_first_qe[hba->max_sqes - 1]; + ep->qp.sq_prod_idx = 0; + ep->qp.sq_cons_idx = 0; + ep->qp.sqe_left = hba->max_sqes; + + /* Allocate page table memory for CQ which is page aligned */ + ep->qp.cq_mem_size = hba->max_cqes * BNX2I_CQE_SIZE; + ep->qp.cq_mem_size = + (ep->qp.cq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + ep->qp.cq_pgtbl_size = + (ep->qp.cq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); + ep->qp.cq_pgtbl_size = + (ep->qp.cq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + + ep->qp.cq_pgtbl_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size, + &ep->qp.cq_pgtbl_phys, GFP_KERNEL); + if (!ep->qp.cq_pgtbl_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc CQ PT memory %d\n", + ep->qp.cq_pgtbl_size); + goto mem_alloc_err; + } + + /* Allocate memory area for actual CQ element */ + ep->qp.cq_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size, + &ep->qp.cq_phys, GFP_KERNEL); + if (!ep->qp.cq_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc CQ BD memory %d\n", + ep->qp.cq_mem_size); + goto mem_alloc_err; + } + memset(ep->qp.cq_virt, 0x00, ep->qp.cq_mem_size); + + ep->qp.cq_first_qe = ep->qp.cq_virt; + ep->qp.cq_prod_qe = ep->qp.cq_first_qe; + ep->qp.cq_cons_qe = ep->qp.cq_first_qe; + ep->qp.cq_last_qe = &ep->qp.cq_first_qe[hba->max_cqes - 1]; + ep->qp.cq_prod_idx = 0; + ep->qp.cq_cons_idx = 0; + ep->qp.cqe_left = hba->max_cqes; + ep->qp.cqe_exp_seq_sn = ISCSI_INITIAL_SN; + ep->qp.cqe_size = hba->max_cqes; + + /* Invalidate all EQ CQE index, req only for 57710 */ + cq_db = (struct bnx2i_5771x_cq_db *) ep->qp.cq_pgtbl_virt; + memset(cq_db->sqn, 0xFF, sizeof(cq_db->sqn[0]) * BNX2X_MAX_CQS); + + /* Allocate page table memory for RQ which is page aligned */ + ep->qp.rq_mem_size = hba->max_rqes * BNX2I_RQ_WQE_SIZE; + ep->qp.rq_mem_size = + (ep->qp.rq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + ep->qp.rq_pgtbl_size = + (ep->qp.rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); + ep->qp.rq_pgtbl_size = + (ep->qp.rq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; + + ep->qp.rq_pgtbl_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size, + &ep->qp.rq_pgtbl_phys, GFP_KERNEL); + if (!ep->qp.rq_pgtbl_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc RQ PT mem %d\n", + ep->qp.rq_pgtbl_size); + goto mem_alloc_err; + } + + /* Allocate memory area for actual RQ element */ + ep->qp.rq_virt = + dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size, + &ep->qp.rq_phys, GFP_KERNEL); + if (!ep->qp.rq_virt) { + printk(KERN_ALERT "bnx2i: unable to alloc RQ BD memory %d\n", + ep->qp.rq_mem_size); + goto mem_alloc_err; + } + + ep->qp.rq_first_qe = ep->qp.rq_virt; + ep->qp.rq_prod_qe = ep->qp.rq_first_qe; + ep->qp.rq_cons_qe = ep->qp.rq_first_qe; + ep->qp.rq_last_qe = &ep->qp.rq_first_qe[hba->max_rqes - 1]; + ep->qp.rq_prod_idx = 0x8000; + ep->qp.rq_cons_idx = 0; + ep->qp.rqe_left = hba->max_rqes; + + setup_qp_page_tables(ep); + + return 0; + +mem_alloc_err: + bnx2i_free_qp_resc(hba, ep); + return -ENOMEM; +} + + + +/** + * bnx2i_free_qp_resc - free memory resources held by QP + * @hba: adapter structure pointer + * @ep: endpoint (transport identifier) structure + * + * Free QP resources - SQ/RQ/CQ memory and page tables. + */ +void bnx2i_free_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) +{ + if (ep->qp.ctx_base) { + iounmap(ep->qp.ctx_base); + ep->qp.ctx_base = NULL; + } + /* Free SQ mem */ + if (ep->qp.sq_pgtbl_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size, + ep->qp.sq_pgtbl_virt, ep->qp.sq_pgtbl_phys); + ep->qp.sq_pgtbl_virt = NULL; + ep->qp.sq_pgtbl_phys = 0; + } + if (ep->qp.sq_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.sq_mem_size, + ep->qp.sq_virt, ep->qp.sq_phys); + ep->qp.sq_virt = NULL; + ep->qp.sq_phys = 0; + } + + /* Free RQ mem */ + if (ep->qp.rq_pgtbl_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size, + ep->qp.rq_pgtbl_virt, ep->qp.rq_pgtbl_phys); + ep->qp.rq_pgtbl_virt = NULL; + ep->qp.rq_pgtbl_phys = 0; + } + if (ep->qp.rq_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.rq_mem_size, + ep->qp.rq_virt, ep->qp.rq_phys); + ep->qp.rq_virt = NULL; + ep->qp.rq_phys = 0; + } + + /* Free CQ mem */ + if (ep->qp.cq_pgtbl_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size, + ep->qp.cq_pgtbl_virt, ep->qp.cq_pgtbl_phys); + ep->qp.cq_pgtbl_virt = NULL; + ep->qp.cq_pgtbl_phys = 0; + } + if (ep->qp.cq_virt) { + dma_free_coherent(&hba->pcidev->dev, ep->qp.cq_mem_size, + ep->qp.cq_virt, ep->qp.cq_phys); + ep->qp.cq_virt = NULL; + ep->qp.cq_phys = 0; + } +} + + +/** + * bnx2i_send_fw_iscsi_init_msg - initiates initial handshake with iscsi f/w + * @hba: adapter structure pointer + * + * Send down iscsi_init KWQEs which initiates the initial handshake with the f/w + * This results in iSCSi support validation and on-chip context manager + * initialization. Firmware completes this handshake with a CQE carrying + * the result of iscsi support validation. Parameter carried by + * iscsi init request determines the number of offloaded connection and + * tolerance level for iscsi protocol violation this hba/chip can support + */ +int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) +{ + struct kwqe *kwqe_arr[3]; + struct iscsi_kwqe_init1 iscsi_init; + struct iscsi_kwqe_init2 iscsi_init2; + int rc = 0; + u64 mask64; + + memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1)); + memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2)); + + bnx2i_adjust_qp_size(hba); + + iscsi_init.flags = + (CNIC_PAGE_BITS - 8) << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT; + if (en_tcp_dack) + iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE; + iscsi_init.reserved0 = 0; + iscsi_init.num_cqs = 1; + iscsi_init.hdr.op_code = ISCSI_KWQE_OPCODE_INIT1; + iscsi_init.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + + iscsi_init.dummy_buffer_addr_lo = (u32) hba->dummy_buf_dma; + iscsi_init.dummy_buffer_addr_hi = + (u32) ((u64) hba->dummy_buf_dma >> 32); + + hba->num_ccell = hba->max_sqes >> 1; + hba->ctx_ccell_tasks = + ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16)); + iscsi_init.num_ccells_per_conn = hba->num_ccell; + iscsi_init.num_tasks_per_conn = hba->max_sqes; + iscsi_init.sq_wqes_per_page = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE; + iscsi_init.sq_num_wqes = hba->max_sqes; + iscsi_init.cq_log_wqes_per_page = + (u8) bnx2i_power_of2(CNIC_PAGE_SIZE / BNX2I_CQE_SIZE); + iscsi_init.cq_num_wqes = hba->max_cqes; + iscsi_init.cq_num_pages = (hba->max_cqes * BNX2I_CQE_SIZE + + (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE; + iscsi_init.sq_num_pages = (hba->max_sqes * BNX2I_SQ_WQE_SIZE + + (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE; + iscsi_init.rq_buffer_size = BNX2I_RQ_WQE_SIZE; + iscsi_init.rq_num_wqes = hba->max_rqes; + + + iscsi_init2.hdr.op_code = ISCSI_KWQE_OPCODE_INIT2; + iscsi_init2.hdr.flags = + (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT); + iscsi_init2.max_cq_sqn = hba->max_cqes * 2 + 1; + mask64 = 0x0ULL; + mask64 |= ( + /* CISCO MDS */ + (1UL << + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV) | + /* HP MSA1510i */ + (1UL << + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN) | + /* EMC */ + (1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN)); + if (error_mask1) { + iscsi_init2.error_bit_map[0] = error_mask1; + mask64 ^= (u32)(mask64); + mask64 |= error_mask1; + } else + iscsi_init2.error_bit_map[0] = (u32) mask64; + + if (error_mask2) { + iscsi_init2.error_bit_map[1] = error_mask2; + mask64 &= 0xffffffff; + mask64 |= ((u64)error_mask2 << 32); + } else + iscsi_init2.error_bit_map[1] = (u32) (mask64 >> 32); + + iscsi_error_mask = mask64; + + kwqe_arr[0] = (struct kwqe *) &iscsi_init; + kwqe_arr[1] = (struct kwqe *) &iscsi_init2; + + if (hba->cnic && hba->cnic->submit_kwqes) + rc = hba->cnic->submit_kwqes(hba->cnic, kwqe_arr, 2); + return rc; +} + + +/** + * bnx2i_process_scsi_cmd_resp - this function handles scsi cmd completion. + * @session: iscsi session + * @bnx2i_conn: bnx2i connection + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process SCSI CMD Response CQE & complete the request to SCSI-ML + */ +int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; + struct bnx2i_cmd_response *resp_cqe; + struct bnx2i_cmd *bnx2i_cmd; + struct iscsi_task *task; + struct iscsi_scsi_rsp *hdr; + u32 datalen = 0; + + resp_cqe = (struct bnx2i_cmd_response *)cqe; + spin_lock_bh(&session->back_lock); + task = iscsi_itt_to_task(conn, + resp_cqe->itt & ISCSI_CMD_RESPONSE_INDEX); + if (!task) + goto fail; + + bnx2i_cmd = task->dd_data; + + if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) { + conn->datain_pdus_cnt += + resp_cqe->task_stat.read_stat.num_data_ins; + conn->rxdata_octets += + bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.read_stat.num_data_ins); + ADD_STATS_64(hba, rx_bytes, + bnx2i_cmd->req.total_data_transfer_length); + } else { + conn->dataout_pdus_cnt += + resp_cqe->task_stat.write_stat.num_data_outs; + conn->r2t_pdus_cnt += + resp_cqe->task_stat.write_stat.num_r2ts; + conn->txdata_octets += + bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, tx_pdus, + resp_cqe->task_stat.write_stat.num_data_outs); + ADD_STATS_64(hba, tx_bytes, + bnx2i_cmd->req.total_data_transfer_length); + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.write_stat.num_r2ts); + } + bnx2i_iscsi_unmap_sg_list(bnx2i_cmd); + + hdr = (struct iscsi_scsi_rsp *)task->hdr; + resp_cqe = (struct bnx2i_cmd_response *)cqe; + hdr->opcode = resp_cqe->op_code; + hdr->max_cmdsn = cpu_to_be32(resp_cqe->max_cmd_sn); + hdr->exp_cmdsn = cpu_to_be32(resp_cqe->exp_cmd_sn); + hdr->response = resp_cqe->response; + hdr->cmd_status = resp_cqe->status; + hdr->flags = resp_cqe->response_flags; + hdr->residual_count = cpu_to_be32(resp_cqe->residual_count); + + if (resp_cqe->op_code == ISCSI_OP_SCSI_DATA_IN) + goto done; + + if (resp_cqe->status == SAM_STAT_CHECK_CONDITION) { + datalen = resp_cqe->data_length; + if (datalen < 2) + goto done; + + if (datalen > BNX2I_RQ_WQE_SIZE) { + iscsi_conn_printk(KERN_ERR, conn, + "sense data len %d > RQ sz\n", + datalen); + datalen = BNX2I_RQ_WQE_SIZE; + } else if (datalen > ISCSI_DEF_MAX_RECV_SEG_LEN) { + iscsi_conn_printk(KERN_ERR, conn, + "sense data len %d > conn data\n", + datalen); + datalen = ISCSI_DEF_MAX_RECV_SEG_LEN; + } + + bnx2i_get_rq_buf(bnx2i_cmd->conn, conn->data, datalen); + bnx2i_put_rq_buf(bnx2i_cmd->conn, 1); + } + +done: + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, + conn->data, datalen); +fail: + spin_unlock_bh(&session->back_lock); + return 0; +} + + +/** + * bnx2i_process_login_resp - this function handles iscsi login response + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process Login Response CQE & complete it to open-iscsi user daemon + */ +static int bnx2i_process_login_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + struct bnx2i_login_response *login; + struct iscsi_login_rsp *resp_hdr; + int pld_len; + int pad_len; + + login = (struct bnx2i_login_response *) cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, + login->itt & ISCSI_LOGIN_RESPONSE_INDEX); + if (!task) + goto done; + + resp_hdr = (struct iscsi_login_rsp *) &bnx2i_conn->gen_pdu.resp_hdr; + memset(resp_hdr, 0, sizeof(struct iscsi_hdr)); + resp_hdr->opcode = login->op_code; + resp_hdr->flags = login->response_flags; + resp_hdr->max_version = login->version_max; + resp_hdr->active_version = login->version_active; + resp_hdr->hlength = 0; + + hton24(resp_hdr->dlength, login->data_length); + memcpy(resp_hdr->isid, &login->isid_lo, 6); + resp_hdr->tsih = cpu_to_be16(login->tsih); + resp_hdr->itt = task->hdr->itt; + resp_hdr->statsn = cpu_to_be32(login->stat_sn); + resp_hdr->exp_cmdsn = cpu_to_be32(login->exp_cmd_sn); + resp_hdr->max_cmdsn = cpu_to_be32(login->max_cmd_sn); + resp_hdr->status_class = login->status_class; + resp_hdr->status_detail = login->status_detail; + pld_len = login->data_length; + bnx2i_conn->gen_pdu.resp_wr_ptr = + bnx2i_conn->gen_pdu.resp_buf + pld_len; + + pad_len = 0; + if (pld_len & 0x3) + pad_len = 4 - (pld_len % 4); + + if (pad_len) { + int i = 0; + for (i = 0; i < pad_len; i++) { + bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0; + bnx2i_conn->gen_pdu.resp_wr_ptr++; + } + } + + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, + bnx2i_conn->gen_pdu.resp_buf, + bnx2i_conn->gen_pdu.resp_wr_ptr - bnx2i_conn->gen_pdu.resp_buf); +done: + spin_unlock(&session->back_lock); + return 0; +} + + +/** + * bnx2i_process_text_resp - this function handles iscsi text response + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI Text Response CQE& complete it to open-iscsi user daemon + */ +static int bnx2i_process_text_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + struct bnx2i_text_response *text; + struct iscsi_text_rsp *resp_hdr; + int pld_len; + int pad_len; + + text = (struct bnx2i_text_response *) cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, text->itt & ISCSI_LOGIN_RESPONSE_INDEX); + if (!task) + goto done; + + resp_hdr = (struct iscsi_text_rsp *)&bnx2i_conn->gen_pdu.resp_hdr; + memset(resp_hdr, 0, sizeof(struct iscsi_hdr)); + resp_hdr->opcode = text->op_code; + resp_hdr->flags = text->response_flags; + resp_hdr->hlength = 0; + + hton24(resp_hdr->dlength, text->data_length); + resp_hdr->itt = task->hdr->itt; + resp_hdr->ttt = cpu_to_be32(text->ttt); + resp_hdr->statsn = task->hdr->exp_statsn; + resp_hdr->exp_cmdsn = cpu_to_be32(text->exp_cmd_sn); + resp_hdr->max_cmdsn = cpu_to_be32(text->max_cmd_sn); + pld_len = text->data_length; + bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf + + pld_len; + pad_len = 0; + if (pld_len & 0x3) + pad_len = 4 - (pld_len % 4); + + if (pad_len) { + int i = 0; + for (i = 0; i < pad_len; i++) { + bnx2i_conn->gen_pdu.resp_wr_ptr[0] = 0; + bnx2i_conn->gen_pdu.resp_wr_ptr++; + } + } + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, + bnx2i_conn->gen_pdu.resp_buf, + bnx2i_conn->gen_pdu.resp_wr_ptr - + bnx2i_conn->gen_pdu.resp_buf); +done: + spin_unlock(&session->back_lock); + return 0; +} + + +/** + * bnx2i_process_tmf_resp - this function handles iscsi TMF response + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI TMF Response CQE and wake up the driver eh thread. + */ +static int bnx2i_process_tmf_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + struct bnx2i_tmf_response *tmf_cqe; + struct iscsi_tm_rsp *resp_hdr; + + tmf_cqe = (struct bnx2i_tmf_response *)cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, + tmf_cqe->itt & ISCSI_TMF_RESPONSE_INDEX); + if (!task) + goto done; + + resp_hdr = (struct iscsi_tm_rsp *) &bnx2i_conn->gen_pdu.resp_hdr; + memset(resp_hdr, 0, sizeof(struct iscsi_hdr)); + resp_hdr->opcode = tmf_cqe->op_code; + resp_hdr->max_cmdsn = cpu_to_be32(tmf_cqe->max_cmd_sn); + resp_hdr->exp_cmdsn = cpu_to_be32(tmf_cqe->exp_cmd_sn); + resp_hdr->itt = task->hdr->itt; + resp_hdr->response = tmf_cqe->response; + + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0); +done: + spin_unlock(&session->back_lock); + return 0; +} + +/** + * bnx2i_process_logout_resp - this function handles iscsi logout response + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI Logout Response CQE & make function call to + * notify the user daemon. + */ +static int bnx2i_process_logout_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + struct bnx2i_logout_response *logout; + struct iscsi_logout_rsp *resp_hdr; + + logout = (struct bnx2i_logout_response *) cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, + logout->itt & ISCSI_LOGOUT_RESPONSE_INDEX); + if (!task) + goto done; + + resp_hdr = (struct iscsi_logout_rsp *) &bnx2i_conn->gen_pdu.resp_hdr; + memset(resp_hdr, 0, sizeof(struct iscsi_hdr)); + resp_hdr->opcode = logout->op_code; + resp_hdr->flags = logout->response; + resp_hdr->hlength = 0; + + resp_hdr->itt = task->hdr->itt; + resp_hdr->statsn = task->hdr->exp_statsn; + resp_hdr->exp_cmdsn = cpu_to_be32(logout->exp_cmd_sn); + resp_hdr->max_cmdsn = cpu_to_be32(logout->max_cmd_sn); + + resp_hdr->t2wait = cpu_to_be32(logout->time_to_wait); + resp_hdr->t2retain = cpu_to_be32(logout->time_to_retain); + + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)resp_hdr, NULL, 0); + + bnx2i_conn->ep->state = EP_STATE_LOGOUT_RESP_RCVD; +done: + spin_unlock(&session->back_lock); + return 0; +} + +/** + * bnx2i_process_nopin_local_cmpl - this function handles iscsi nopin CQE + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI NOPIN local completion CQE, frees IIT and command structures + */ +static void bnx2i_process_nopin_local_cmpl(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct bnx2i_nop_in_msg *nop_in; + struct iscsi_task *task; + + nop_in = (struct bnx2i_nop_in_msg *)cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, + nop_in->itt & ISCSI_NOP_IN_MSG_INDEX); + if (task) + __iscsi_put_task(task); + spin_unlock(&session->back_lock); +} + +/** + * bnx2i_unsol_pdu_adjust_rq - makes adjustments to RQ after unsol pdu is recvd + * @conn: iscsi connection + * + * Firmware advances RQ producer index for every unsolicited PDU even if + * payload data length is '0'. This function makes corresponding + * adjustments on the driver side to match this f/w behavior + */ +static void bnx2i_unsol_pdu_adjust_rq(struct bnx2i_conn *bnx2i_conn) +{ + char dummy_rq_data[2]; + bnx2i_get_rq_buf(bnx2i_conn, dummy_rq_data, 1); + bnx2i_put_rq_buf(bnx2i_conn, 1); +} + + +/** + * bnx2i_process_nopin_mesg - this function handles iscsi nopin CQE + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI target's proactive iSCSI NOPIN request + */ +static int bnx2i_process_nopin_mesg(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + struct bnx2i_nop_in_msg *nop_in; + struct iscsi_nopin *hdr; + int tgt_async_nop = 0; + + nop_in = (struct bnx2i_nop_in_msg *)cqe; + + spin_lock(&session->back_lock); + hdr = (struct iscsi_nopin *)&bnx2i_conn->gen_pdu.resp_hdr; + memset(hdr, 0, sizeof(struct iscsi_hdr)); + hdr->opcode = nop_in->op_code; + hdr->max_cmdsn = cpu_to_be32(nop_in->max_cmd_sn); + hdr->exp_cmdsn = cpu_to_be32(nop_in->exp_cmd_sn); + hdr->ttt = cpu_to_be32(nop_in->ttt); + + if (nop_in->itt == (u16) RESERVED_ITT) { + bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); + hdr->itt = RESERVED_ITT; + tgt_async_nop = 1; + goto done; + } + + /* this is a response to one of our nop-outs */ + task = iscsi_itt_to_task(conn, + (itt_t) (nop_in->itt & ISCSI_NOP_IN_MSG_INDEX)); + if (task) { + hdr->flags = ISCSI_FLAG_CMD_FINAL; + hdr->itt = task->hdr->itt; + hdr->ttt = cpu_to_be32(nop_in->ttt); + memcpy(&hdr->lun, nop_in->lun, 8); + } +done: + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); + spin_unlock(&session->back_lock); + + return tgt_async_nop; +} + + +/** + * bnx2i_process_async_mesg - this function handles iscsi async message + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI ASYNC Message + */ +static void bnx2i_process_async_mesg(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct bnx2i_async_msg *async_cqe; + struct iscsi_async *resp_hdr; + u8 async_event; + + bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); + + async_cqe = (struct bnx2i_async_msg *)cqe; + async_event = async_cqe->async_event; + + if (async_event == ISCSI_ASYNC_MSG_SCSI_EVENT) { + iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data, + "async: scsi events not supported\n"); + return; + } + + spin_lock(&session->back_lock); + resp_hdr = (struct iscsi_async *) &bnx2i_conn->gen_pdu.resp_hdr; + memset(resp_hdr, 0, sizeof(struct iscsi_hdr)); + resp_hdr->opcode = async_cqe->op_code; + resp_hdr->flags = 0x80; + + memcpy(&resp_hdr->lun, async_cqe->lun, 8); + resp_hdr->exp_cmdsn = cpu_to_be32(async_cqe->exp_cmd_sn); + resp_hdr->max_cmdsn = cpu_to_be32(async_cqe->max_cmd_sn); + + resp_hdr->async_event = async_cqe->async_event; + resp_hdr->async_vcode = async_cqe->async_vcode; + + resp_hdr->param1 = cpu_to_be16(async_cqe->param1); + resp_hdr->param2 = cpu_to_be16(async_cqe->param2); + resp_hdr->param3 = cpu_to_be16(async_cqe->param3); + + __iscsi_complete_pdu(bnx2i_conn->cls_conn->dd_data, + (struct iscsi_hdr *)resp_hdr, NULL, 0); + spin_unlock(&session->back_lock); +} + + +/** + * bnx2i_process_reject_mesg - process iscsi reject pdu + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process iSCSI REJECT message + */ +static void bnx2i_process_reject_mesg(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct bnx2i_reject_msg *reject; + struct iscsi_reject *hdr; + + reject = (struct bnx2i_reject_msg *) cqe; + if (reject->data_length) { + bnx2i_get_rq_buf(bnx2i_conn, conn->data, reject->data_length); + bnx2i_put_rq_buf(bnx2i_conn, 1); + } else + bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); + + spin_lock(&session->back_lock); + hdr = (struct iscsi_reject *) &bnx2i_conn->gen_pdu.resp_hdr; + memset(hdr, 0, sizeof(struct iscsi_hdr)); + hdr->opcode = reject->op_code; + hdr->reason = reject->reason; + hton24(hdr->dlength, reject->data_length); + hdr->max_cmdsn = cpu_to_be32(reject->max_cmd_sn); + hdr->exp_cmdsn = cpu_to_be32(reject->exp_cmd_sn); + hdr->ffffffff = cpu_to_be32(RESERVED_ITT); + __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, conn->data, + reject->data_length); + spin_unlock(&session->back_lock); +} + +/** + * bnx2i_process_cmd_cleanup_resp - process scsi command clean-up completion + * @session: iscsi session pointer + * @bnx2i_conn: iscsi connection pointer + * @cqe: pointer to newly DMA'ed CQE entry for processing + * + * process command cleanup response CQE during conn shutdown or error recovery + */ +static void bnx2i_process_cmd_cleanup_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct cqe *cqe) +{ + struct bnx2i_cleanup_response *cmd_clean_rsp; + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_task *task; + + cmd_clean_rsp = (struct bnx2i_cleanup_response *)cqe; + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(conn, + cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX); + if (!task) + printk(KERN_ALERT "bnx2i: cmd clean ITT %x not active\n", + cmd_clean_rsp->itt & ISCSI_CLEANUP_RESPONSE_INDEX); + spin_unlock(&session->back_lock); + complete(&bnx2i_conn->cmd_cleanup_cmpl); +} + + +/** + * bnx2i_percpu_io_thread - thread per cpu for ios + * + * @arg: ptr to bnx2i_percpu_info structure + */ +int bnx2i_percpu_io_thread(void *arg) +{ + struct bnx2i_percpu_s *p = arg; + struct bnx2i_work *work, *tmp; + LIST_HEAD(work_list); + + set_user_nice(current, MIN_NICE); + + while (!kthread_should_stop()) { + spin_lock_bh(&p->p_work_lock); + while (!list_empty(&p->work_list)) { + list_splice_init(&p->work_list, &work_list); + spin_unlock_bh(&p->p_work_lock); + + list_for_each_entry_safe(work, tmp, &work_list, list) { + list_del_init(&work->list); + /* work allocated in the bh, freed here */ + bnx2i_process_scsi_cmd_resp(work->session, + work->bnx2i_conn, + &work->cqe); + atomic_dec(&work->bnx2i_conn->work_cnt); + kfree(work); + } + spin_lock_bh(&p->p_work_lock); + } + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_bh(&p->p_work_lock); + schedule(); + } + __set_current_state(TASK_RUNNING); + + return 0; +} + + +/** + * bnx2i_queue_scsi_cmd_resp - queue cmd completion to the percpu thread + * @bnx2i_conn: bnx2i connection + * + * this function is called by generic KCQ handler to queue all pending cmd + * completion CQEs + * + * The implementation is to queue the cmd response based on the + * last recorded command for the given connection. The + * cpu_id gets recorded upon task_xmit. No out-of-order completion! + */ +static int bnx2i_queue_scsi_cmd_resp(struct iscsi_session *session, + struct bnx2i_conn *bnx2i_conn, + struct bnx2i_nop_in_msg *cqe) +{ + struct bnx2i_work *bnx2i_work = NULL; + struct bnx2i_percpu_s *p = NULL; + struct iscsi_task *task; + struct scsi_cmnd *sc; + int rc = 0; + int cpu; + + spin_lock(&session->back_lock); + task = iscsi_itt_to_task(bnx2i_conn->cls_conn->dd_data, + cqe->itt & ISCSI_CMD_RESPONSE_INDEX); + if (!task || !task->sc) { + spin_unlock(&session->back_lock); + return -EINVAL; + } + sc = task->sc; + + if (!blk_rq_cpu_valid(sc->request)) + cpu = smp_processor_id(); + else + cpu = sc->request->cpu; + + spin_unlock(&session->back_lock); + + p = &per_cpu(bnx2i_percpu, cpu); + spin_lock(&p->p_work_lock); + if (unlikely(!p->iothread)) { + rc = -EINVAL; + goto err; + } + /* Alloc and copy to the cqe */ + bnx2i_work = kzalloc(sizeof(struct bnx2i_work), GFP_ATOMIC); + if (bnx2i_work) { + INIT_LIST_HEAD(&bnx2i_work->list); + bnx2i_work->session = session; + bnx2i_work->bnx2i_conn = bnx2i_conn; + memcpy(&bnx2i_work->cqe, cqe, sizeof(struct cqe)); + list_add_tail(&bnx2i_work->list, &p->work_list); + atomic_inc(&bnx2i_conn->work_cnt); + wake_up_process(p->iothread); + spin_unlock(&p->p_work_lock); + goto done; + } else + rc = -ENOMEM; +err: + spin_unlock(&p->p_work_lock); + bnx2i_process_scsi_cmd_resp(session, bnx2i_conn, (struct cqe *)cqe); +done: + return rc; +} + + +/** + * bnx2i_process_new_cqes - process newly DMA'ed CQE's + * @bnx2i_conn: bnx2i connection + * + * this function is called by generic KCQ handler to process all pending CQE's + */ +static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) +{ + struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct iscsi_session *session = conn->session; + struct bnx2i_hba *hba = bnx2i_conn->hba; + struct qp_info *qp; + struct bnx2i_nop_in_msg *nopin; + int tgt_async_msg; + int cqe_cnt = 0; + + if (bnx2i_conn->ep == NULL) + return 0; + + qp = &bnx2i_conn->ep->qp; + + if (!qp->cq_virt) { + printk(KERN_ALERT "bnx2i (%s): cq resr freed in bh execution!", + hba->netdev->name); + goto out; + } + while (1) { + nopin = (struct bnx2i_nop_in_msg *) qp->cq_cons_qe; + if (nopin->cq_req_sn != qp->cqe_exp_seq_sn) + break; + + if (unlikely(test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx))) { + if (nopin->op_code == ISCSI_OP_NOOP_IN && + nopin->itt == (u16) RESERVED_ITT) { + printk(KERN_ALERT "bnx2i: Unsolicited " + "NOP-In detected for suspended " + "connection dev=%s!\n", + hba->netdev->name); + bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); + goto cqe_out; + } + break; + } + tgt_async_msg = 0; + + switch (nopin->op_code) { + case ISCSI_OP_SCSI_CMD_RSP: + case ISCSI_OP_SCSI_DATA_IN: + /* Run the kthread engine only for data cmds + All other cmds will be completed in this bh! */ + bnx2i_queue_scsi_cmd_resp(session, bnx2i_conn, nopin); + goto done; + case ISCSI_OP_LOGIN_RSP: + bnx2i_process_login_resp(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OP_SCSI_TMFUNC_RSP: + bnx2i_process_tmf_resp(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OP_TEXT_RSP: + bnx2i_process_text_resp(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OP_LOGOUT_RSP: + bnx2i_process_logout_resp(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OP_NOOP_IN: + if (bnx2i_process_nopin_mesg(session, bnx2i_conn, + qp->cq_cons_qe)) + tgt_async_msg = 1; + break; + case ISCSI_OPCODE_NOPOUT_LOCAL_COMPLETION: + bnx2i_process_nopin_local_cmpl(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OP_ASYNC_EVENT: + bnx2i_process_async_mesg(session, bnx2i_conn, + qp->cq_cons_qe); + tgt_async_msg = 1; + break; + case ISCSI_OP_REJECT: + bnx2i_process_reject_mesg(session, bnx2i_conn, + qp->cq_cons_qe); + break; + case ISCSI_OPCODE_CLEANUP_RESPONSE: + bnx2i_process_cmd_cleanup_resp(session, bnx2i_conn, + qp->cq_cons_qe); + break; + default: + printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n", + nopin->op_code); + } + + ADD_STATS_64(hba, rx_pdus, 1); + ADD_STATS_64(hba, rx_bytes, nopin->data_length); +done: + if (!tgt_async_msg) { + if (!atomic_read(&bnx2i_conn->ep->num_active_cmds)) + printk(KERN_ALERT "bnx2i (%s): no active cmd! " + "op 0x%x\n", + hba->netdev->name, + nopin->op_code); + else + atomic_dec(&bnx2i_conn->ep->num_active_cmds); + } +cqe_out: + /* clear out in production version only, till beta keep opcode + * field intact, will be helpful in debugging (context dump) + * nopin->op_code = 0; + */ + cqe_cnt++; + qp->cqe_exp_seq_sn++; + if (qp->cqe_exp_seq_sn == (qp->cqe_size * 2 + 1)) + qp->cqe_exp_seq_sn = ISCSI_INITIAL_SN; + + if (qp->cq_cons_qe == qp->cq_last_qe) { + qp->cq_cons_qe = qp->cq_first_qe; + qp->cq_cons_idx = 0; + } else { + qp->cq_cons_qe++; + qp->cq_cons_idx++; + } + } +out: + return cqe_cnt; +} + +/** + * bnx2i_fastpath_notification - process global event queue (KCQ) + * @hba: adapter structure pointer + * @new_cqe_kcqe: pointer to newly DMA'ed KCQE entry + * + * Fast path event notification handler, KCQ entry carries context id + * of the connection that has 1 or more pending CQ entries + */ +static void bnx2i_fastpath_notification(struct bnx2i_hba *hba, + struct iscsi_kcqe *new_cqe_kcqe) +{ + struct bnx2i_conn *bnx2i_conn; + u32 iscsi_cid; + int nxt_idx; + + iscsi_cid = new_cqe_kcqe->iscsi_conn_id; + bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid); + + if (!bnx2i_conn) { + printk(KERN_ALERT "cid #%x not valid\n", iscsi_cid); + return; + } + if (!bnx2i_conn->ep) { + printk(KERN_ALERT "cid #%x - ep not bound\n", iscsi_cid); + return; + } + + bnx2i_process_new_cqes(bnx2i_conn); + nxt_idx = bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, + CNIC_ARM_CQE_FP); + if (nxt_idx && nxt_idx == bnx2i_process_new_cqes(bnx2i_conn)) + bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE_FP); +} + + +/** + * bnx2i_process_update_conn_cmpl - process iscsi conn update completion KCQE + * @hba: adapter structure pointer + * @update_kcqe: kcqe pointer + * + * CONN_UPDATE completion handler, this completes iSCSI connection FFP migration + */ +static void bnx2i_process_update_conn_cmpl(struct bnx2i_hba *hba, + struct iscsi_kcqe *update_kcqe) +{ + struct bnx2i_conn *conn; + u32 iscsi_cid; + + iscsi_cid = update_kcqe->iscsi_conn_id; + conn = bnx2i_get_conn_from_id(hba, iscsi_cid); + + if (!conn) { + printk(KERN_ALERT "conn_update: cid %x not valid\n", iscsi_cid); + return; + } + if (!conn->ep) { + printk(KERN_ALERT "cid %x does not have ep bound\n", iscsi_cid); + return; + } + + if (update_kcqe->completion_status) { + printk(KERN_ALERT "request failed cid %x\n", iscsi_cid); + conn->ep->state = EP_STATE_ULP_UPDATE_FAILED; + } else + conn->ep->state = EP_STATE_ULP_UPDATE_COMPL; + + wake_up_interruptible(&conn->ep->ofld_wait); +} + + +/** + * bnx2i_recovery_que_add_conn - add connection to recovery queue + * @hba: adapter structure pointer + * @bnx2i_conn: iscsi connection + * + * Add connection to recovery queue and schedule adapter eh worker + */ +static void bnx2i_recovery_que_add_conn(struct bnx2i_hba *hba, + struct bnx2i_conn *bnx2i_conn) +{ + iscsi_conn_failure(bnx2i_conn->cls_conn->dd_data, + ISCSI_ERR_CONN_FAILED); +} + + +/** + * bnx2i_process_tcp_error - process error notification on a given connection + * + * @hba: adapter structure pointer + * @tcp_err: tcp error kcqe pointer + * + * handles tcp level error notifications from FW. + */ +static void bnx2i_process_tcp_error(struct bnx2i_hba *hba, + struct iscsi_kcqe *tcp_err) +{ + struct bnx2i_conn *bnx2i_conn; + u32 iscsi_cid; + + iscsi_cid = tcp_err->iscsi_conn_id; + bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid); + + if (!bnx2i_conn) { + printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid); + return; + } + + printk(KERN_ALERT "bnx2i - cid 0x%x had TCP errors, error code 0x%x\n", + iscsi_cid, tcp_err->completion_status); + bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn); +} + + +/** + * bnx2i_process_iscsi_error - process error notification on a given connection + * @hba: adapter structure pointer + * @iscsi_err: iscsi error kcqe pointer + * + * handles iscsi error notifications from the FW. Firmware based in initial + * handshake classifies iscsi protocol / TCP rfc violation into either + * warning or error indications. If indication is of "Error" type, driver + * will initiate session recovery for that connection/session. For + * "Warning" type indication, driver will put out a system log message + * (there will be only one message for each type for the life of the + * session, this is to avoid un-necessarily overloading the system) + */ +static void bnx2i_process_iscsi_error(struct bnx2i_hba *hba, + struct iscsi_kcqe *iscsi_err) +{ + struct bnx2i_conn *bnx2i_conn; + u32 iscsi_cid; + char warn_notice[] = "iscsi_warning"; + char error_notice[] = "iscsi_error"; + char additional_notice[64]; + char *message; + int need_recovery; + u64 err_mask64; + + iscsi_cid = iscsi_err->iscsi_conn_id; + bnx2i_conn = bnx2i_get_conn_from_id(hba, iscsi_cid); + if (!bnx2i_conn) { + printk(KERN_ALERT "bnx2i - cid 0x%x not valid\n", iscsi_cid); + return; + } + + err_mask64 = (0x1ULL << iscsi_err->completion_status); + + if (err_mask64 & iscsi_error_mask) { + need_recovery = 0; + message = warn_notice; + } else { + need_recovery = 1; + message = error_notice; + } + + switch (iscsi_err->completion_status) { + case ISCSI_KCQE_COMPLETION_STATUS_HDR_DIG_ERR: + strcpy(additional_notice, "hdr digest err"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_DATA_DIG_ERR: + strcpy(additional_notice, "data digest err"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_OPCODE: + strcpy(additional_notice, "wrong opcode rcvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_AHS_LEN: + strcpy(additional_notice, "AHS len > 0 rcvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ITT: + strcpy(additional_notice, "invalid ITT rcvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_STATSN: + strcpy(additional_notice, "wrong StatSN rcvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN: + strcpy(additional_notice, "wrong DataSN rcvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T: + strcpy(additional_notice, "pend R2T violation"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_0: + strcpy(additional_notice, "ERL0, UO"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_1: + strcpy(additional_notice, "ERL0, U1"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_2: + strcpy(additional_notice, "ERL0, U2"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_3: + strcpy(additional_notice, "ERL0, U3"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_4: + strcpy(additional_notice, "ERL0, U4"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_5: + strcpy(additional_notice, "ERL0, U5"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_O_U_6: + strcpy(additional_notice, "ERL0, U6"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_RCV_LEN: + strcpy(additional_notice, "invalid resi len"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_RCV_PDU_LEN: + strcpy(additional_notice, "MRDSL violation"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_F_BIT_ZERO: + strcpy(additional_notice, "F-bit not set"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_NOT_RSRV: + strcpy(additional_notice, "invalid TTT"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATASN: + strcpy(additional_notice, "invalid DataSN"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REMAIN_BURST_LEN: + strcpy(additional_notice, "burst len violation"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_BUFFER_OFF: + strcpy(additional_notice, "buf offset violation"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN: + strcpy(additional_notice, "invalid LUN field"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_R2TSN: + strcpy(additional_notice, "invalid R2TSN field"); + break; +#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0 \ + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_0 + case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_0: + strcpy(additional_notice, "invalid cmd len1"); + break; +#define BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1 \ + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DESIRED_DATA_TRNS_LEN_1 + case BNX2I_ERR_DESIRED_DATA_TRNS_LEN_1: + strcpy(additional_notice, "invalid cmd len2"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_EXCEED: + strcpy(additional_notice, + "pend r2t exceeds MaxOutstandingR2T value"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_TTT_IS_RSRV: + strcpy(additional_notice, "TTT is rsvd"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_MAX_BURST_LEN: + strcpy(additional_notice, "MBL violation"); + break; +#define BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO \ + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_DATA_SEG_LEN_NOT_ZERO + case BNX2I_ERR_DATA_SEG_LEN_NOT_ZERO: + strcpy(additional_notice, "data seg len != 0"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_REJECT_PDU_LEN: + strcpy(additional_notice, "reject pdu len error"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_ASYNC_PDU_LEN: + strcpy(additional_notice, "async pdu len error"); + break; + case ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_NOPIN_PDU_LEN: + strcpy(additional_notice, "nopin pdu len error"); + break; +#define BNX2_ERR_PEND_R2T_IN_CLEANUP \ + ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_PEND_R2T_IN_CLEANUP + case BNX2_ERR_PEND_R2T_IN_CLEANUP: + strcpy(additional_notice, "pend r2t in cleanup"); + break; + + case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_FRAGMENT: + strcpy(additional_notice, "IP fragments rcvd"); + break; + case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_IP_OPTIONS: + strcpy(additional_notice, "IP options error"); + break; + case ISCI_KCQE_COMPLETION_STATUS_TCP_ERROR_URGENT_FLAG: + strcpy(additional_notice, "urgent flag error"); + break; + default: + printk(KERN_ALERT "iscsi_err - unknown err %x\n", + iscsi_err->completion_status); + } + + if (need_recovery) { + iscsi_conn_printk(KERN_ALERT, + bnx2i_conn->cls_conn->dd_data, + "bnx2i: %s - %s\n", + message, additional_notice); + + iscsi_conn_printk(KERN_ALERT, + bnx2i_conn->cls_conn->dd_data, + "conn_err - hostno %d conn %p, " + "iscsi_cid %x cid %x\n", + bnx2i_conn->hba->shost->host_no, + bnx2i_conn, bnx2i_conn->ep->ep_iscsi_cid, + bnx2i_conn->ep->ep_cid); + bnx2i_recovery_que_add_conn(bnx2i_conn->hba, bnx2i_conn); + } else + if (!test_and_set_bit(iscsi_err->completion_status, + (void *) &bnx2i_conn->violation_notified)) + iscsi_conn_printk(KERN_ALERT, + bnx2i_conn->cls_conn->dd_data, + "bnx2i: %s - %s\n", + message, additional_notice); +} + + +/** + * bnx2i_process_conn_destroy_cmpl - process iscsi conn destroy completion + * @hba: adapter structure pointer + * @conn_destroy: conn destroy kcqe pointer + * + * handles connection destroy completion request. + */ +static void bnx2i_process_conn_destroy_cmpl(struct bnx2i_hba *hba, + struct iscsi_kcqe *conn_destroy) +{ + struct bnx2i_endpoint *ep; + + ep = bnx2i_find_ep_in_destroy_list(hba, conn_destroy->iscsi_conn_id); + if (!ep) { + printk(KERN_ALERT "bnx2i_conn_destroy_cmpl: no pending " + "offload request, unexpected complection\n"); + return; + } + + if (hba != ep->hba) { + printk(KERN_ALERT "conn destroy- error hba mis-match\n"); + return; + } + + if (conn_destroy->completion_status) { + printk(KERN_ALERT "conn_destroy_cmpl: op failed\n"); + ep->state = EP_STATE_CLEANUP_FAILED; + } else + ep->state = EP_STATE_CLEANUP_CMPL; + wake_up_interruptible(&ep->ofld_wait); +} + + +/** + * bnx2i_process_ofld_cmpl - process initial iscsi conn offload completion + * @hba: adapter structure pointer + * @ofld_kcqe: conn offload kcqe pointer + * + * handles initial connection offload completion, ep_connect() thread is + * woken-up to continue with LLP connect process + */ +static void bnx2i_process_ofld_cmpl(struct bnx2i_hba *hba, + struct iscsi_kcqe *ofld_kcqe) +{ + u32 cid_addr; + struct bnx2i_endpoint *ep; + u32 cid_num; + + ep = bnx2i_find_ep_in_ofld_list(hba, ofld_kcqe->iscsi_conn_id); + if (!ep) { + printk(KERN_ALERT "ofld_cmpl: no pend offload request\n"); + return; + } + + if (hba != ep->hba) { + printk(KERN_ALERT "ofld_cmpl: error hba mis-match\n"); + return; + } + + if (ofld_kcqe->completion_status) { + ep->state = EP_STATE_OFLD_FAILED; + if (ofld_kcqe->completion_status == + ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE) + printk(KERN_ALERT "bnx2i (%s): ofld1 cmpl - unable " + "to allocate iSCSI context resources\n", + hba->netdev->name); + else if (ofld_kcqe->completion_status == + ISCSI_KCQE_COMPLETION_STATUS_INVALID_OPCODE) + printk(KERN_ALERT "bnx2i (%s): ofld1 cmpl - invalid " + "opcode\n", hba->netdev->name); + else if (ofld_kcqe->completion_status == + ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY) + /* error status code valid only for 5771x chipset */ + ep->state = EP_STATE_OFLD_FAILED_CID_BUSY; + else + printk(KERN_ALERT "bnx2i (%s): ofld1 cmpl - invalid " + "error code %d\n", hba->netdev->name, + ofld_kcqe->completion_status); + } else { + ep->state = EP_STATE_OFLD_COMPL; + cid_addr = ofld_kcqe->iscsi_conn_context_id; + cid_num = bnx2i_get_cid_num(ep); + ep->ep_cid = cid_addr; + ep->qp.ctx_base = NULL; + } + wake_up_interruptible(&ep->ofld_wait); +} + +/** + * bnx2i_indicate_kcqe - process iscsi conn update completion KCQE + * @hba: adapter structure pointer + * @update_kcqe: kcqe pointer + * + * Generic KCQ event handler/dispatcher + */ +static void bnx2i_indicate_kcqe(void *context, struct kcqe *kcqe[], + u32 num_cqe) +{ + struct bnx2i_hba *hba = context; + int i = 0; + struct iscsi_kcqe *ikcqe = NULL; + + while (i < num_cqe) { + ikcqe = (struct iscsi_kcqe *) kcqe[i++]; + + if (ikcqe->op_code == + ISCSI_KCQE_OPCODE_CQ_EVENT_NOTIFICATION) + bnx2i_fastpath_notification(hba, ikcqe); + else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_OFFLOAD_CONN) + bnx2i_process_ofld_cmpl(hba, ikcqe); + else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_UPDATE_CONN) + bnx2i_process_update_conn_cmpl(hba, ikcqe); + else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_INIT) { + if (ikcqe->completion_status != + ISCSI_KCQE_COMPLETION_STATUS_SUCCESS) + bnx2i_iscsi_license_error(hba, ikcqe->\ + completion_status); + else { + set_bit(ADAPTER_STATE_UP, &hba->adapter_state); + bnx2i_get_link_state(hba); + printk(KERN_INFO "bnx2i [%.2x:%.2x.%.2x]: " + "ISCSI_INIT passed\n", + (u8)hba->pcidev->bus->number, + hba->pci_devno, + (u8)hba->pci_func); + + + } + } else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_DESTROY_CONN) + bnx2i_process_conn_destroy_cmpl(hba, ikcqe); + else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_ISCSI_ERROR) + bnx2i_process_iscsi_error(hba, ikcqe); + else if (ikcqe->op_code == ISCSI_KCQE_OPCODE_TCP_ERROR) + bnx2i_process_tcp_error(hba, ikcqe); + else + printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n", + ikcqe->op_code); + } +} + + +/** + * bnx2i_indicate_netevent - Generic netdev event handler + * @context: adapter structure pointer + * @event: event type + * @vlan_id: vlans id - associated vlan id with this event + * + * Handles four netdev events, NETDEV_UP, NETDEV_DOWN, + * NETDEV_GOING_DOWN and NETDEV_CHANGE + */ +static void bnx2i_indicate_netevent(void *context, unsigned long event, + u16 vlan_id) +{ + struct bnx2i_hba *hba = context; + + /* Ignore all netevent coming from vlans */ + if (vlan_id != 0) + return; + + switch (event) { + case NETDEV_UP: + if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state)) + bnx2i_send_fw_iscsi_init_msg(hba); + break; + case NETDEV_DOWN: + clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state); + clear_bit(ADAPTER_STATE_UP, &hba->adapter_state); + break; + case NETDEV_GOING_DOWN: + set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state); + iscsi_host_for_each_session(hba->shost, + bnx2i_drop_session); + break; + case NETDEV_CHANGE: + bnx2i_get_link_state(hba); + break; + default: + ; + } +} + + +/** + * bnx2i_cm_connect_cmpl - process iscsi conn establishment completion + * @cm_sk: cnic sock structure pointer + * + * function callback exported via bnx2i - cnic driver interface to + * indicate completion of option-2 TCP connect request. + */ +static void bnx2i_cm_connect_cmpl(struct cnic_sock *cm_sk) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + + if (test_bit(ADAPTER_STATE_GOING_DOWN, &ep->hba->adapter_state)) + ep->state = EP_STATE_CONNECT_FAILED; + else if (test_bit(SK_F_OFFLD_COMPLETE, &cm_sk->flags)) + ep->state = EP_STATE_CONNECT_COMPL; + else + ep->state = EP_STATE_CONNECT_FAILED; + + wake_up_interruptible(&ep->ofld_wait); +} + + +/** + * bnx2i_cm_close_cmpl - process tcp conn close completion + * @cm_sk: cnic sock structure pointer + * + * function callback exported via bnx2i - cnic driver interface to + * indicate completion of option-2 graceful TCP connect shutdown + */ +static void bnx2i_cm_close_cmpl(struct cnic_sock *cm_sk) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + + ep->state = EP_STATE_DISCONN_COMPL; + wake_up_interruptible(&ep->ofld_wait); +} + + +/** + * bnx2i_cm_abort_cmpl - process abortive tcp conn teardown completion + * @cm_sk: cnic sock structure pointer + * + * function callback exported via bnx2i - cnic driver interface to + * indicate completion of option-2 abortive TCP connect termination + */ +static void bnx2i_cm_abort_cmpl(struct cnic_sock *cm_sk) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + + ep->state = EP_STATE_DISCONN_COMPL; + wake_up_interruptible(&ep->ofld_wait); +} + + +/** + * bnx2i_cm_remote_close - process received TCP FIN + * @hba: adapter structure pointer + * @update_kcqe: kcqe pointer + * + * function callback exported via bnx2i - cnic driver interface to indicate + * async TCP events such as FIN + */ +static void bnx2i_cm_remote_close(struct cnic_sock *cm_sk) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + + ep->state = EP_STATE_TCP_FIN_RCVD; + if (ep->conn) + bnx2i_recovery_que_add_conn(ep->hba, ep->conn); +} + +/** + * bnx2i_cm_remote_abort - process TCP RST and start conn cleanup + * @hba: adapter structure pointer + * @update_kcqe: kcqe pointer + * + * function callback exported via bnx2i - cnic driver interface to + * indicate async TCP events (RST) sent by the peer. + */ +static void bnx2i_cm_remote_abort(struct cnic_sock *cm_sk) +{ + struct bnx2i_endpoint *ep = (struct bnx2i_endpoint *) cm_sk->context; + u32 old_state = ep->state; + + ep->state = EP_STATE_TCP_RST_RCVD; + if (old_state == EP_STATE_DISCONN_START) + wake_up_interruptible(&ep->ofld_wait); + else + if (ep->conn) + bnx2i_recovery_que_add_conn(ep->hba, ep->conn); +} + + +static int bnx2i_send_nl_mesg(void *context, u32 msg_type, + char *buf, u16 buflen) +{ + struct bnx2i_hba *hba = context; + int rc; + + if (!hba) + return -ENODEV; + + rc = iscsi_offload_mesg(hba->shost, &bnx2i_iscsi_transport, + msg_type, buf, buflen); + if (rc) + printk(KERN_ALERT "bnx2i: private nl message send error\n"); + + return rc; +} + + +/** + * bnx2i_cnic_cb - global template of bnx2i - cnic driver interface structure + * carrying callback function pointers + * + */ +struct cnic_ulp_ops bnx2i_cnic_cb = { + .cnic_init = bnx2i_ulp_init, + .cnic_exit = bnx2i_ulp_exit, + .cnic_start = bnx2i_start, + .cnic_stop = bnx2i_stop, + .indicate_kcqes = bnx2i_indicate_kcqe, + .indicate_netevent = bnx2i_indicate_netevent, + .cm_connect_complete = bnx2i_cm_connect_cmpl, + .cm_close_complete = bnx2i_cm_close_cmpl, + .cm_abort_complete = bnx2i_cm_abort_cmpl, + .cm_remote_close = bnx2i_cm_remote_close, + .cm_remote_abort = bnx2i_cm_remote_abort, + .iscsi_nl_send_msg = bnx2i_send_nl_mesg, + .cnic_get_stats = bnx2i_get_stats, + .owner = THIS_MODULE +}; + + +/** + * bnx2i_map_ep_dbell_regs - map connection doorbell registers + * @ep: bnx2i endpoint + * + * maps connection's SQ and RQ doorbell registers, 5706/5708/5709 hosts these + * register in BAR #0. Whereas in 57710 these register are accessed by + * mapping BAR #1 + */ +int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) +{ + u32 cid_num; + u32 reg_off; + u32 first_l4l5; + u32 ctx_sz; + u32 config2; + resource_size_t reg_base; + + cid_num = bnx2i_get_cid_num(ep); + + if (test_bit(BNX2I_NX2_DEV_57710, &ep->hba->cnic_dev_type)) { + reg_base = pci_resource_start(ep->hba->pcidev, + BNX2X_DOORBELL_PCI_BAR); + reg_off = (1 << BNX2X_DB_SHIFT) * (cid_num & 0x1FFFF); + ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off, 4); + goto arm_cq; + } + + if ((test_bit(BNX2I_NX2_DEV_5709, &ep->hba->cnic_dev_type)) && + (ep->hba->mail_queue_access == BNX2I_MQ_BIN_MODE)) { + config2 = REG_RD(ep->hba, BNX2_MQ_CONFIG2); + first_l4l5 = config2 & BNX2_MQ_CONFIG2_FIRST_L4L5; + ctx_sz = (config2 & BNX2_MQ_CONFIG2_CONT_SZ) >> 3; + if (ctx_sz) + reg_off = CTX_OFFSET + MAX_CID_CNT * MB_KERNEL_CTX_SIZE + + BNX2I_570X_PAGE_SIZE_DEFAULT * + (((cid_num - first_l4l5) / ctx_sz) + 256); + else + reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num); + } else + /* 5709 device in normal node and 5706/5708 devices */ + reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num); + + ep->qp.ctx_base = ioremap_nocache(ep->hba->reg_base + reg_off, + MB_KERNEL_CTX_SIZE); + if (!ep->qp.ctx_base) + return -ENOMEM; + +arm_cq: + bnx2i_arm_cq_event_coalescing(ep, CNIC_ARM_CQE); + return 0; +} diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c new file mode 100644 index 000000000..c8b410c24 --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -0,0 +1,604 @@ +/* bnx2i.c: QLogic NetXtreme II iSCSI driver. + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mike Christie + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ + +#include "bnx2i.h" + +static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list); +static u32 adapter_count; + +#define DRV_MODULE_NAME "bnx2i" +#define DRV_MODULE_VERSION "2.7.10.1" +#define DRV_MODULE_RELDATE "Jul 16, 2014" + +static char version[] = + "QLogic NetXtreme II iSCSI Driver " DRV_MODULE_NAME \ + " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + + +MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com> and " + "Eddie Wai <eddie.wai@broadcom.com>"); + +MODULE_DESCRIPTION("QLogic NetXtreme II BCM5706/5708/5709/57710/57711/57712" + "/57800/57810/57840 iSCSI Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +static DEFINE_MUTEX(bnx2i_dev_lock); + +unsigned int event_coal_min = 24; +module_param(event_coal_min, int, 0664); +MODULE_PARM_DESC(event_coal_min, "Event Coalescing Minimum Commands"); + +unsigned int event_coal_div = 2; +module_param(event_coal_div, int, 0664); +MODULE_PARM_DESC(event_coal_div, "Event Coalescing Divide Factor"); + +unsigned int en_tcp_dack = 1; +module_param(en_tcp_dack, int, 0664); +MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK"); + +unsigned int error_mask1 = 0x00; +module_param(error_mask1, uint, 0664); +MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1"); + +unsigned int error_mask2 = 0x00; +module_param(error_mask2, uint, 0664); +MODULE_PARM_DESC(error_mask2, "Config FW iSCSI Error Mask #2"); + +unsigned int sq_size; +module_param(sq_size, int, 0664); +MODULE_PARM_DESC(sq_size, "Configure SQ size"); + +unsigned int rq_size = BNX2I_RQ_WQES_DEFAULT; +module_param(rq_size, int, 0664); +MODULE_PARM_DESC(rq_size, "Configure RQ size"); + +u64 iscsi_error_mask = 0x00; + +DEFINE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu); + +static int bnx2i_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu); +/* notification function for CPU hotplug events */ +static struct notifier_block bnx2i_cpu_notifier = { + .notifier_call = bnx2i_cpu_callback, +}; + + +/** + * bnx2i_identify_device - identifies NetXtreme II device type + * @hba: Adapter structure pointer + * @cnic: Corresponding cnic device + * + * This function identifies the NX2 device type and sets appropriate + * queue mailbox register access method, 5709 requires driver to + * access MBOX regs using *bin* mode + */ +void bnx2i_identify_device(struct bnx2i_hba *hba, struct cnic_dev *dev) +{ + hba->cnic_dev_type = 0; + if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { + if (hba->pci_did == PCI_DEVICE_ID_NX2_5706 || + hba->pci_did == PCI_DEVICE_ID_NX2_5706S) { + set_bit(BNX2I_NX2_DEV_5706, &hba->cnic_dev_type); + } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5708 || + hba->pci_did == PCI_DEVICE_ID_NX2_5708S) { + set_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type); + } else if (hba->pci_did == PCI_DEVICE_ID_NX2_5709 || + hba->pci_did == PCI_DEVICE_ID_NX2_5709S) { + set_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type); + hba->mail_queue_access = BNX2I_MQ_BIN_MODE; + } + } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) { + set_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type); + } else { + printk(KERN_ALERT "bnx2i: unknown device, 0x%x\n", + hba->pci_did); + } +} + + +/** + * get_adapter_list_head - returns head of adapter list + */ +struct bnx2i_hba *get_adapter_list_head(void) +{ + struct bnx2i_hba *hba = NULL; + struct bnx2i_hba *tmp_hba; + + if (!adapter_count) + goto hba_not_found; + + mutex_lock(&bnx2i_dev_lock); + list_for_each_entry(tmp_hba, &adapter_list, link) { + if (tmp_hba->cnic && tmp_hba->cnic->cm_select_dev) { + hba = tmp_hba; + break; + } + } + mutex_unlock(&bnx2i_dev_lock); +hba_not_found: + return hba; +} + + +/** + * bnx2i_find_hba_for_cnic - maps cnic device instance to bnx2i adapter instance + * @cnic: pointer to cnic device instance + * + */ +struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic) +{ + struct bnx2i_hba *hba, *temp; + + mutex_lock(&bnx2i_dev_lock); + list_for_each_entry_safe(hba, temp, &adapter_list, link) { + if (hba->cnic == cnic) { + mutex_unlock(&bnx2i_dev_lock); + return hba; + } + } + mutex_unlock(&bnx2i_dev_lock); + return NULL; +} + + +/** + * bnx2i_start - cnic callback to initialize & start adapter instance + * @handle: transparent handle pointing to adapter structure + * + * This function maps adapter structure to pcidev structure and initiates + * firmware handshake to enable/initialize on chip iscsi components + * This bnx2i - cnic interface api callback is issued after following + * 2 conditions are met - + * a) underlying network interface is up (marked by event 'NETDEV_UP' + * from netdev + * b) bnx2i adapter instance is registered + */ +void bnx2i_start(void *handle) +{ +#define BNX2I_INIT_POLL_TIME (1000 / HZ) + struct bnx2i_hba *hba = handle; + int i = HZ; + + /* On some bnx2x devices, it is possible that iSCSI is no + * longer supported after firmware is downloaded. In that + * case, the iscsi_init_msg will return failure. + */ + + bnx2i_send_fw_iscsi_init_msg(hba); + while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && + !test_bit(ADAPTER_STATE_INIT_FAILED, &hba->adapter_state) && i--) + msleep(BNX2I_INIT_POLL_TIME); +} + + +/** + * bnx2i_chip_cleanup - local routine to handle chip cleanup + * @hba: Adapter instance to register + * + * Driver checks if adapter still has any active connections before + * executing the cleanup process + */ +static void bnx2i_chip_cleanup(struct bnx2i_hba *hba) +{ + struct bnx2i_endpoint *bnx2i_ep; + struct list_head *pos, *tmp; + + if (hba->ofld_conns_active) { + /* Stage to force the disconnection + * This is the case where the daemon is either slow or + * not present + */ + printk(KERN_ALERT "bnx2i: (%s) chip cleanup for %d active " + "connections\n", hba->netdev->name, + hba->ofld_conns_active); + mutex_lock(&hba->net_dev_lock); + list_for_each_safe(pos, tmp, &hba->ep_active_list) { + bnx2i_ep = list_entry(pos, struct bnx2i_endpoint, link); + /* Clean up the chip only */ + bnx2i_hw_ep_disconnect(bnx2i_ep); + bnx2i_ep->cm_sk = NULL; + } + mutex_unlock(&hba->net_dev_lock); + } +} + + +/** + * bnx2i_stop - cnic callback to shutdown adapter instance + * @handle: transparent handle pointing to adapter structure + * + * driver checks if adapter is already in shutdown mode, if not start + * the shutdown process + */ +void bnx2i_stop(void *handle) +{ + struct bnx2i_hba *hba = handle; + int conns_active; + int wait_delay = 1 * HZ; + + /* check if cleanup happened in GOING_DOWN context */ + if (!test_and_set_bit(ADAPTER_STATE_GOING_DOWN, + &hba->adapter_state)) { + iscsi_host_for_each_session(hba->shost, + bnx2i_drop_session); + wait_delay = hba->hba_shutdown_tmo; + } + /* Wait for inflight offload connection tasks to complete before + * proceeding. Forcefully terminate all connection recovery in + * progress at the earliest, either in bind(), send_pdu(LOGIN), + * or conn_start() + */ + wait_event_interruptible_timeout(hba->eh_wait, + (list_empty(&hba->ep_ofld_list) && + list_empty(&hba->ep_destroy_list)), + 2 * HZ); + /* Wait for all endpoints to be torn down, Chip will be reset once + * control returns to network driver. So it is required to cleanup and + * release all connection resources before returning from this routine. + */ + while (hba->ofld_conns_active) { + conns_active = hba->ofld_conns_active; + wait_event_interruptible_timeout(hba->eh_wait, + (hba->ofld_conns_active != conns_active), + wait_delay); + if (hba->ofld_conns_active == conns_active) + break; + } + bnx2i_chip_cleanup(hba); + + /* This flag should be cleared last so that ep_disconnect() gracefully + * cleans up connection context + */ + clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state); + clear_bit(ADAPTER_STATE_UP, &hba->adapter_state); +} + + +/** + * bnx2i_init_one - initialize an adapter instance and allocate memory resources + * @hba: bnx2i adapter instance + * @cnic: cnic device handle + * + * Global resource lock is held during critical sections below. This routine is + * called from either cnic_register_driver() or device hot plug context and + * and does majority of device specific initialization + */ +static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic) +{ + int rc; + + mutex_lock(&bnx2i_dev_lock); + if (!cnic->max_iscsi_conn) { + printk(KERN_ALERT "bnx2i: dev %s does not support " + "iSCSI\n", hba->netdev->name); + rc = -EOPNOTSUPP; + goto out; + } + + hba->cnic = cnic; + rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba); + if (!rc) { + hba->age++; + set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic); + list_add_tail(&hba->link, &adapter_list); + adapter_count++; + } else if (rc == -EBUSY) /* duplicate registration */ + printk(KERN_ALERT "bnx2i, duplicate registration" + "hba=%p, cnic=%p\n", hba, cnic); + else if (rc == -EAGAIN) + printk(KERN_ERR "bnx2i, driver not registered\n"); + else if (rc == -EINVAL) + printk(KERN_ERR "bnx2i, invalid type %d\n", CNIC_ULP_ISCSI); + else + printk(KERN_ERR "bnx2i dev reg, unknown error, %d\n", rc); + +out: + mutex_unlock(&bnx2i_dev_lock); + + return rc; +} + + +/** + * bnx2i_ulp_init - initialize an adapter instance + * @dev: cnic device handle + * + * Called from cnic_register_driver() context to initialize all enumerated + * cnic devices. This routine allocate adapter structure and other + * device specific resources. + */ +void bnx2i_ulp_init(struct cnic_dev *dev) +{ + struct bnx2i_hba *hba; + + /* Allocate a HBA structure for this device */ + hba = bnx2i_alloc_hba(dev); + if (!hba) { + printk(KERN_ERR "bnx2i init: hba initialization failed\n"); + return; + } + + /* Get PCI related information and update hba struct members */ + clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic); + if (bnx2i_init_one(hba, dev)) { + printk(KERN_ERR "bnx2i - hba %p init failed\n", hba); + bnx2i_free_hba(hba); + } +} + + +/** + * bnx2i_ulp_exit - shuts down adapter instance and frees all resources + * @dev: cnic device handle + * + */ +void bnx2i_ulp_exit(struct cnic_dev *dev) +{ + struct bnx2i_hba *hba; + + hba = bnx2i_find_hba_for_cnic(dev); + if (!hba) { + printk(KERN_INFO "bnx2i_ulp_exit: hba not " + "found, dev 0x%p\n", dev); + return; + } + mutex_lock(&bnx2i_dev_lock); + list_del_init(&hba->link); + adapter_count--; + + if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) { + hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI); + clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic); + } + mutex_unlock(&bnx2i_dev_lock); + + bnx2i_free_hba(hba); +} + + +/** + * bnx2i_get_stats - Retrieve various statistic from iSCSI offload + * @handle: bnx2i_hba + * + * function callback exported via bnx2i - cnic driver interface to + * retrieve various iSCSI offload related statistics. + */ +int bnx2i_get_stats(void *handle) +{ + struct bnx2i_hba *hba = handle; + struct iscsi_stats_info *stats; + + if (!hba) + return -EINVAL; + + stats = (struct iscsi_stats_info *)hba->cnic->stats_addr; + + if (!stats) + return -ENOMEM; + + strlcpy(stats->version, DRV_MODULE_VERSION, sizeof(stats->version)); + memcpy(stats->mac_add1 + 2, hba->cnic->mac_addr, ETH_ALEN); + + stats->max_frame_size = hba->netdev->mtu; + stats->txq_size = hba->max_sqes; + stats->rxq_size = hba->max_cqes; + + stats->txq_avg_depth = 0; + stats->rxq_avg_depth = 0; + + GET_STATS_64(hba, stats, rx_pdus); + GET_STATS_64(hba, stats, rx_bytes); + + GET_STATS_64(hba, stats, tx_pdus); + GET_STATS_64(hba, stats, tx_bytes); + + return 0; +} + + +/** + * bnx2i_percpu_thread_create - Create a receive thread for an + * online CPU + * + * @cpu: cpu index for the online cpu + */ +static void bnx2i_percpu_thread_create(unsigned int cpu) +{ + struct bnx2i_percpu_s *p; + struct task_struct *thread; + + p = &per_cpu(bnx2i_percpu, cpu); + + thread = kthread_create_on_node(bnx2i_percpu_io_thread, (void *)p, + cpu_to_node(cpu), + "bnx2i_thread/%d", cpu); + /* bind thread to the cpu */ + if (likely(!IS_ERR(thread))) { + kthread_bind(thread, cpu); + p->iothread = thread; + wake_up_process(thread); + } +} + + +static void bnx2i_percpu_thread_destroy(unsigned int cpu) +{ + struct bnx2i_percpu_s *p; + struct task_struct *thread; + struct bnx2i_work *work, *tmp; + + /* Prevent any new work from being queued for this CPU */ + p = &per_cpu(bnx2i_percpu, cpu); + spin_lock_bh(&p->p_work_lock); + thread = p->iothread; + p->iothread = NULL; + + /* Free all work in the list */ + list_for_each_entry_safe(work, tmp, &p->work_list, list) { + list_del_init(&work->list); + bnx2i_process_scsi_cmd_resp(work->session, + work->bnx2i_conn, &work->cqe); + kfree(work); + } + + spin_unlock_bh(&p->p_work_lock); + if (thread) + kthread_stop(thread); +} + + +/** + * bnx2i_cpu_callback - Handler for CPU hotplug events + * + * @nfb: The callback data block + * @action: The event triggering the callback + * @hcpu: The index of the CPU that the event is for + * + * This creates or destroys per-CPU data for iSCSI + * + * Returns NOTIFY_OK always. + */ +static int bnx2i_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned cpu = (unsigned long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + printk(KERN_INFO "bnx2i: CPU %x online: Create Rx thread\n", + cpu); + bnx2i_percpu_thread_create(cpu); + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + printk(KERN_INFO "CPU %x offline: Remove Rx thread\n", cpu); + bnx2i_percpu_thread_destroy(cpu); + break; + default: + break; + } + return NOTIFY_OK; +} + + +/** + * bnx2i_mod_init - module init entry point + * + * initialize any driver wide global data structures such as endpoint pool, + * tcp port manager/queue, sysfs. finally driver will register itself + * with the cnic module + */ +static int __init bnx2i_mod_init(void) +{ + int err; + unsigned cpu = 0; + struct bnx2i_percpu_s *p; + + printk(KERN_INFO "%s", version); + + if (sq_size && !is_power_of_2(sq_size)) + sq_size = roundup_pow_of_two(sq_size); + + mutex_init(&bnx2i_dev_lock); + + bnx2i_scsi_xport_template = + iscsi_register_transport(&bnx2i_iscsi_transport); + if (!bnx2i_scsi_xport_template) { + printk(KERN_ERR "Could not register bnx2i transport.\n"); + err = -ENOMEM; + goto out; + } + + err = cnic_register_driver(CNIC_ULP_ISCSI, &bnx2i_cnic_cb); + if (err) { + printk(KERN_ERR "Could not register bnx2i cnic driver.\n"); + goto unreg_xport; + } + + /* Create percpu kernel threads to handle iSCSI I/O completions */ + for_each_possible_cpu(cpu) { + p = &per_cpu(bnx2i_percpu, cpu); + INIT_LIST_HEAD(&p->work_list); + spin_lock_init(&p->p_work_lock); + p->iothread = NULL; + } + + cpu_notifier_register_begin(); + + for_each_online_cpu(cpu) + bnx2i_percpu_thread_create(cpu); + + /* Initialize per CPU interrupt thread */ + __register_hotcpu_notifier(&bnx2i_cpu_notifier); + + cpu_notifier_register_done(); + + return 0; + +unreg_xport: + iscsi_unregister_transport(&bnx2i_iscsi_transport); +out: + return err; +} + + +/** + * bnx2i_mod_exit - module cleanup/exit entry point + * + * Global resource lock and host adapter lock is held during critical sections + * in this function. Driver will browse through the adapter list, cleans-up + * each instance, unregisters iscsi transport name and finally driver will + * unregister itself with the cnic module + */ +static void __exit bnx2i_mod_exit(void) +{ + struct bnx2i_hba *hba; + unsigned cpu = 0; + + mutex_lock(&bnx2i_dev_lock); + while (!list_empty(&adapter_list)) { + hba = list_entry(adapter_list.next, struct bnx2i_hba, link); + list_del(&hba->link); + adapter_count--; + + if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) { + bnx2i_chip_cleanup(hba); + hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI); + clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic); + } + + bnx2i_free_hba(hba); + } + mutex_unlock(&bnx2i_dev_lock); + + cpu_notifier_register_begin(); + + for_each_online_cpu(cpu) + bnx2i_percpu_thread_destroy(cpu); + + __unregister_hotcpu_notifier(&bnx2i_cpu_notifier); + + cpu_notifier_register_done(); + + iscsi_unregister_transport(&bnx2i_iscsi_transport); + cnic_unregister_driver(CNIC_ULP_ISCSI); +} + +module_init(bnx2i_mod_init); +module_exit(bnx2i_mod_exit); diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c new file mode 100644 index 000000000..e53078d03 --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -0,0 +1,2305 @@ +/* + * bnx2i_iscsi.c: QLogic NetXtreme II iSCSI driver. + * + * Copyright (c) 2006 - 2013 Broadcom Corporation + * Copyright (c) 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (c) 2007, 2008 Mike Christie + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ + +#include <linux/slab.h> +#include <scsi/scsi_tcq.h> +#include <scsi/libiscsi.h> +#include "bnx2i.h" + +struct scsi_transport_template *bnx2i_scsi_xport_template; +struct iscsi_transport bnx2i_iscsi_transport; +static struct scsi_host_template bnx2i_host_template; + +/* + * Global endpoint resource info + */ +static DEFINE_SPINLOCK(bnx2i_resc_lock); /* protects global resources */ + +DECLARE_PER_CPU(struct bnx2i_percpu_s, bnx2i_percpu); + +static int bnx2i_adapter_ready(struct bnx2i_hba *hba) +{ + int retval = 0; + + if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) || + test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) || + test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state)) + retval = -EPERM; + return retval; +} + +/** + * bnx2i_get_write_cmd_bd_idx - identifies various BD bookmarks + * @cmd: iscsi cmd struct pointer + * @buf_off: absolute buffer offset + * @start_bd_off: u32 pointer to return the offset within the BD + * indicated by 'start_bd_idx' on which 'buf_off' falls + * @start_bd_idx: index of the BD on which 'buf_off' falls + * + * identifies & marks various bd info for scsi command's imm data, + * unsolicited data and the first solicited data seq. + */ +static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off, + u32 *start_bd_off, u32 *start_bd_idx) +{ + struct iscsi_bd *bd_tbl = cmd->io_tbl.bd_tbl; + u32 cur_offset = 0; + u32 cur_bd_idx = 0; + + if (buf_off) { + while (buf_off >= (cur_offset + bd_tbl->buffer_length)) { + cur_offset += bd_tbl->buffer_length; + cur_bd_idx++; + bd_tbl++; + } + } + + *start_bd_off = buf_off - cur_offset; + *start_bd_idx = cur_bd_idx; +} + +/** + * bnx2i_setup_write_cmd_bd_info - sets up BD various information + * @task: transport layer's cmd struct pointer + * + * identifies & marks various bd info for scsi command's immediate data, + * unsolicited data and first solicited data seq which includes BD start + * index & BD buf off. his function takes into account iscsi parameter such + * as immediate data and unsolicited data is support on this connection. + */ +static void bnx2i_setup_write_cmd_bd_info(struct iscsi_task *task) +{ + struct bnx2i_cmd *cmd = task->dd_data; + u32 start_bd_offset; + u32 start_bd_idx; + u32 buffer_offset = 0; + u32 cmd_len = cmd->req.total_data_transfer_length; + + /* if ImmediateData is turned off & IntialR2T is turned on, + * there will be no immediate or unsolicited data, just return. + */ + if (!iscsi_task_has_unsol_data(task) && !task->imm_count) + return; + + /* Immediate data */ + buffer_offset += task->imm_count; + if (task->imm_count == cmd_len) + return; + + if (iscsi_task_has_unsol_data(task)) { + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset, + &start_bd_offset, &start_bd_idx); + cmd->req.ud_buffer_offset = start_bd_offset; + cmd->req.ud_start_bd_index = start_bd_idx; + buffer_offset += task->unsol_r2t.data_length; + } + + if (buffer_offset != cmd_len) { + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset, + &start_bd_offset, &start_bd_idx); + if ((start_bd_offset > task->conn->session->first_burst) || + (start_bd_idx > scsi_sg_count(cmd->scsi_cmd))) { + int i = 0; + + iscsi_conn_printk(KERN_ALERT, task->conn, + "bnx2i- error, buf offset 0x%x " + "bd_valid %d use_sg %d\n", + buffer_offset, cmd->io_tbl.bd_valid, + scsi_sg_count(cmd->scsi_cmd)); + for (i = 0; i < cmd->io_tbl.bd_valid; i++) + iscsi_conn_printk(KERN_ALERT, task->conn, + "bnx2i err, bd[%d]: len %x\n", + i, cmd->io_tbl.bd_tbl[i].\ + buffer_length); + } + cmd->req.sd_buffer_offset = start_bd_offset; + cmd->req.sd_start_bd_index = start_bd_idx; + } +} + + + +/** + * bnx2i_map_scsi_sg - maps IO buffer and prepares the BD table + * @hba: adapter instance + * @cmd: iscsi cmd struct pointer + * + * map SG list + */ +static int bnx2i_map_scsi_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd) +{ + struct scsi_cmnd *sc = cmd->scsi_cmd; + struct iscsi_bd *bd = cmd->io_tbl.bd_tbl; + struct scatterlist *sg; + int byte_count = 0; + int bd_count = 0; + int sg_count; + int sg_len; + u64 addr; + int i; + + BUG_ON(scsi_sg_count(sc) > ISCSI_MAX_BDS_PER_CMD); + + sg_count = scsi_dma_map(sc); + + scsi_for_each_sg(sc, sg, sg_count, i) { + sg_len = sg_dma_len(sg); + addr = (u64) sg_dma_address(sg); + bd[bd_count].buffer_addr_lo = addr & 0xffffffff; + bd[bd_count].buffer_addr_hi = addr >> 32; + bd[bd_count].buffer_length = sg_len; + bd[bd_count].flags = 0; + if (bd_count == 0) + bd[bd_count].flags = ISCSI_BD_FIRST_IN_BD_CHAIN; + + byte_count += sg_len; + bd_count++; + } + + if (bd_count) + bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN; + + BUG_ON(byte_count != scsi_bufflen(sc)); + return bd_count; +} + +/** + * bnx2i_iscsi_map_sg_list - maps SG list + * @cmd: iscsi cmd struct pointer + * + * creates BD list table for the command + */ +static void bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd) +{ + int bd_count; + + bd_count = bnx2i_map_scsi_sg(cmd->conn->hba, cmd); + if (!bd_count) { + struct iscsi_bd *bd = cmd->io_tbl.bd_tbl; + + bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0; + bd[0].buffer_length = bd[0].flags = 0; + } + cmd->io_tbl.bd_valid = bd_count; +} + + +/** + * bnx2i_iscsi_unmap_sg_list - unmaps SG list + * @cmd: iscsi cmd struct pointer + * + * unmap IO buffers and invalidate the BD table + */ +void bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd) +{ + struct scsi_cmnd *sc = cmd->scsi_cmd; + + if (cmd->io_tbl.bd_valid && sc) { + scsi_dma_unmap(sc); + cmd->io_tbl.bd_valid = 0; + } +} + +static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd) +{ + memset(&cmd->req, 0x00, sizeof(cmd->req)); + cmd->req.op_code = 0xFF; + cmd->req.bd_list_addr_lo = (u32) cmd->io_tbl.bd_tbl_dma; + cmd->req.bd_list_addr_hi = + (u32) ((u64) cmd->io_tbl.bd_tbl_dma >> 32); + +} + + +/** + * bnx2i_bind_conn_to_iscsi_cid - bind conn structure to 'iscsi_cid' + * @hba: pointer to adapter instance + * @conn: pointer to iscsi connection + * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1) + * + * update iscsi cid table entry with connection pointer. This enables + * driver to quickly get hold of connection structure pointer in + * completion/interrupt thread using iscsi context ID + */ +static int bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_hba *hba, + struct bnx2i_conn *bnx2i_conn, + u32 iscsi_cid) +{ + if (hba && hba->cid_que.conn_cid_tbl[iscsi_cid]) { + iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data, + "conn bind - entry #%d not free\n", iscsi_cid); + return -EBUSY; + } + + hba->cid_que.conn_cid_tbl[iscsi_cid] = bnx2i_conn; + return 0; +} + + +/** + * bnx2i_get_conn_from_id - maps an iscsi cid to corresponding conn ptr + * @hba: pointer to adapter instance + * @iscsi_cid: iscsi context ID, range 0 - (MAX_CONN - 1) + */ +struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba, + u16 iscsi_cid) +{ + if (!hba->cid_que.conn_cid_tbl) { + printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n"); + return NULL; + + } else if (iscsi_cid >= hba->max_active_conns) { + printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid); + return NULL; + } + return hba->cid_que.conn_cid_tbl[iscsi_cid]; +} + + +/** + * bnx2i_alloc_iscsi_cid - allocates a iscsi_cid from free pool + * @hba: pointer to adapter instance + */ +static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba) +{ + int idx; + + if (!hba->cid_que.cid_free_cnt) + return -1; + + idx = hba->cid_que.cid_q_cons_idx; + hba->cid_que.cid_q_cons_idx++; + if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx) + hba->cid_que.cid_q_cons_idx = 0; + + hba->cid_que.cid_free_cnt--; + return hba->cid_que.cid_que[idx]; +} + + +/** + * bnx2i_free_iscsi_cid - returns tcp port to free list + * @hba: pointer to adapter instance + * @iscsi_cid: iscsi context ID to free + */ +static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid) +{ + int idx; + + if (iscsi_cid == (u16) -1) + return; + + hba->cid_que.cid_free_cnt++; + + idx = hba->cid_que.cid_q_prod_idx; + hba->cid_que.cid_que[idx] = iscsi_cid; + hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL; + hba->cid_que.cid_q_prod_idx++; + if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx) + hba->cid_que.cid_q_prod_idx = 0; +} + + +/** + * bnx2i_setup_free_cid_que - sets up free iscsi cid queue + * @hba: pointer to adapter instance + * + * allocates memory for iscsi cid queue & 'cid - conn ptr' mapping table, + * and initialize table attributes + */ +static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba) +{ + int mem_size; + int i; + + mem_size = hba->max_active_conns * sizeof(u32); + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + + hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL); + if (!hba->cid_que.cid_que_base) + return -ENOMEM; + + mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *); + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + hba->cid_que.conn_cid_tbl = kmalloc(mem_size, GFP_KERNEL); + if (!hba->cid_que.conn_cid_tbl) { + kfree(hba->cid_que.cid_que_base); + hba->cid_que.cid_que_base = NULL; + return -ENOMEM; + } + + hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base; + hba->cid_que.cid_q_prod_idx = 0; + hba->cid_que.cid_q_cons_idx = 0; + hba->cid_que.cid_q_max_idx = hba->max_active_conns; + hba->cid_que.cid_free_cnt = hba->max_active_conns; + + for (i = 0; i < hba->max_active_conns; i++) { + hba->cid_que.cid_que[i] = i; + hba->cid_que.conn_cid_tbl[i] = NULL; + } + return 0; +} + + +/** + * bnx2i_release_free_cid_que - releases 'iscsi_cid' queue resources + * @hba: pointer to adapter instance + */ +static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba) +{ + kfree(hba->cid_que.cid_que_base); + hba->cid_que.cid_que_base = NULL; + + kfree(hba->cid_que.conn_cid_tbl); + hba->cid_que.conn_cid_tbl = NULL; +} + + +/** + * bnx2i_alloc_ep - allocates ep structure from global pool + * @hba: pointer to adapter instance + * + * routine allocates a free endpoint structure from global pool and + * a tcp port to be used for this connection. Global resource lock, + * 'bnx2i_resc_lock' is held while accessing shared global data structures + */ +static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba) +{ + struct iscsi_endpoint *ep; + struct bnx2i_endpoint *bnx2i_ep; + u32 ec_div; + + ep = iscsi_create_endpoint(sizeof(*bnx2i_ep)); + if (!ep) { + printk(KERN_ERR "bnx2i: Could not allocate ep\n"); + return NULL; + } + + bnx2i_ep = ep->dd_data; + bnx2i_ep->cls_ep = ep; + INIT_LIST_HEAD(&bnx2i_ep->link); + bnx2i_ep->state = EP_STATE_IDLE; + bnx2i_ep->ep_iscsi_cid = (u16) -1; + bnx2i_ep->hba = hba; + bnx2i_ep->hba_age = hba->age; + + ec_div = event_coal_div; + while (ec_div >>= 1) + bnx2i_ep->ec_shift += 1; + + hba->ofld_conns_active++; + init_waitqueue_head(&bnx2i_ep->ofld_wait); + return ep; +} + + +/** + * bnx2i_free_ep - free endpoint + * @ep: pointer to iscsi endpoint structure + */ +static void bnx2i_free_ep(struct iscsi_endpoint *ep) +{ + struct bnx2i_endpoint *bnx2i_ep = ep->dd_data; + unsigned long flags; + + spin_lock_irqsave(&bnx2i_resc_lock, flags); + bnx2i_ep->state = EP_STATE_IDLE; + bnx2i_ep->hba->ofld_conns_active--; + + if (bnx2i_ep->ep_iscsi_cid != (u16) -1) + bnx2i_free_iscsi_cid(bnx2i_ep->hba, bnx2i_ep->ep_iscsi_cid); + + if (bnx2i_ep->conn) { + bnx2i_ep->conn->ep = NULL; + bnx2i_ep->conn = NULL; + } + + bnx2i_ep->hba = NULL; + spin_unlock_irqrestore(&bnx2i_resc_lock, flags); + iscsi_destroy_endpoint(ep); +} + + +/** + * bnx2i_alloc_bdt - allocates buffer descriptor (BD) table for the command + * @hba: adapter instance pointer + * @session: iscsi session pointer + * @cmd: iscsi command structure + */ +static int bnx2i_alloc_bdt(struct bnx2i_hba *hba, struct iscsi_session *session, + struct bnx2i_cmd *cmd) +{ + struct io_bdt *io = &cmd->io_tbl; + struct iscsi_bd *bd; + + io->bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, + ISCSI_MAX_BDS_PER_CMD * sizeof(*bd), + &io->bd_tbl_dma, GFP_KERNEL); + if (!io->bd_tbl) { + iscsi_session_printk(KERN_ERR, session, "Could not " + "allocate bdt.\n"); + return -ENOMEM; + } + io->bd_valid = 0; + return 0; +} + +/** + * bnx2i_destroy_cmd_pool - destroys iscsi command pool and release BD table + * @hba: adapter instance pointer + * @session: iscsi session pointer + * @cmd: iscsi command structure + */ +static void bnx2i_destroy_cmd_pool(struct bnx2i_hba *hba, + struct iscsi_session *session) +{ + int i; + + for (i = 0; i < session->cmds_max; i++) { + struct iscsi_task *task = session->cmds[i]; + struct bnx2i_cmd *cmd = task->dd_data; + + if (cmd->io_tbl.bd_tbl) + dma_free_coherent(&hba->pcidev->dev, + ISCSI_MAX_BDS_PER_CMD * + sizeof(struct iscsi_bd), + cmd->io_tbl.bd_tbl, + cmd->io_tbl.bd_tbl_dma); + } + +} + + +/** + * bnx2i_setup_cmd_pool - sets up iscsi command pool for the session + * @hba: adapter instance pointer + * @session: iscsi session pointer + */ +static int bnx2i_setup_cmd_pool(struct bnx2i_hba *hba, + struct iscsi_session *session) +{ + int i; + + for (i = 0; i < session->cmds_max; i++) { + struct iscsi_task *task = session->cmds[i]; + struct bnx2i_cmd *cmd = task->dd_data; + + task->hdr = &cmd->hdr; + task->hdr_max = sizeof(struct iscsi_hdr); + + if (bnx2i_alloc_bdt(hba, session, cmd)) + goto free_bdts; + } + + return 0; + +free_bdts: + bnx2i_destroy_cmd_pool(hba, session); + return -ENOMEM; +} + + +/** + * bnx2i_setup_mp_bdt - allocate BD table resources + * @hba: pointer to adapter structure + * + * Allocate memory for dummy buffer and associated BD + * table to be used by middle path (MP) requests + */ +static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba) +{ + int rc = 0; + struct iscsi_bd *mp_bdt; + u64 addr; + + hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + &hba->mp_bd_dma, GFP_KERNEL); + if (!hba->mp_bd_tbl) { + printk(KERN_ERR "unable to allocate Middle Path BDT\n"); + rc = -1; + goto out; + } + + hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, + CNIC_PAGE_SIZE, + &hba->dummy_buf_dma, GFP_KERNEL); + if (!hba->dummy_buffer) { + printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n"); + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + hba->mp_bd_tbl, hba->mp_bd_dma); + hba->mp_bd_tbl = NULL; + rc = -1; + goto out; + } + + mp_bdt = (struct iscsi_bd *) hba->mp_bd_tbl; + addr = (unsigned long) hba->dummy_buf_dma; + mp_bdt->buffer_addr_lo = addr & 0xffffffff; + mp_bdt->buffer_addr_hi = addr >> 32; + mp_bdt->buffer_length = CNIC_PAGE_SIZE; + mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN | + ISCSI_BD_FIRST_IN_BD_CHAIN; +out: + return rc; +} + + +/** + * bnx2i_free_mp_bdt - releases ITT back to free pool + * @hba: pointer to adapter instance + * + * free MP dummy buffer and associated BD table + */ +static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba) +{ + if (hba->mp_bd_tbl) { + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + hba->mp_bd_tbl, hba->mp_bd_dma); + hba->mp_bd_tbl = NULL; + } + if (hba->dummy_buffer) { + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + hba->dummy_buffer, hba->dummy_buf_dma); + hba->dummy_buffer = NULL; + } + return; +} + +/** + * bnx2i_drop_session - notifies iscsid of connection error. + * @hba: adapter instance pointer + * @session: iscsi session pointer + * + * This notifies iscsid that there is a error, so it can initiate + * recovery. + * + * This relies on caller using the iscsi class iterator so the object + * is refcounted and does not disapper from under us. + */ +void bnx2i_drop_session(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED); +} + +/** + * bnx2i_ep_destroy_list_add - add an entry to EP destroy list + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * EP destroy queue manager + */ +static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_add_tail(&ep->link, &hba->ep_destroy_list); + write_unlock_bh(&hba->ep_rdwr_lock); + return 0; +} + +/** + * bnx2i_ep_destroy_list_del - add an entry to EP destroy list + * + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * EP destroy queue manager + */ +static int bnx2i_ep_destroy_list_del(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_del_init(&ep->link); + write_unlock_bh(&hba->ep_rdwr_lock); + + return 0; +} + +/** + * bnx2i_ep_ofld_list_add - add an entry to ep offload pending list + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * pending conn offload completion queue manager + */ +static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_add_tail(&ep->link, &hba->ep_ofld_list); + write_unlock_bh(&hba->ep_rdwr_lock); + return 0; +} + +/** + * bnx2i_ep_ofld_list_del - add an entry to ep offload pending list + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * pending conn offload completion queue manager + */ +static int bnx2i_ep_ofld_list_del(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_del_init(&ep->link); + write_unlock_bh(&hba->ep_rdwr_lock); + return 0; +} + + +/** + * bnx2i_find_ep_in_ofld_list - find iscsi_cid in pending list of endpoints + * + * @hba: pointer to adapter instance + * @iscsi_cid: iscsi context ID to find + * + */ +struct bnx2i_endpoint * +bnx2i_find_ep_in_ofld_list(struct bnx2i_hba *hba, u32 iscsi_cid) +{ + struct list_head *list; + struct list_head *tmp; + struct bnx2i_endpoint *ep; + + read_lock_bh(&hba->ep_rdwr_lock); + list_for_each_safe(list, tmp, &hba->ep_ofld_list) { + ep = (struct bnx2i_endpoint *)list; + + if (ep->ep_iscsi_cid == iscsi_cid) + break; + ep = NULL; + } + read_unlock_bh(&hba->ep_rdwr_lock); + + if (!ep) + printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid); + return ep; +} + +/** + * bnx2i_find_ep_in_destroy_list - find iscsi_cid in destroy list + * @hba: pointer to adapter instance + * @iscsi_cid: iscsi context ID to find + * + */ +struct bnx2i_endpoint * +bnx2i_find_ep_in_destroy_list(struct bnx2i_hba *hba, u32 iscsi_cid) +{ + struct list_head *list; + struct list_head *tmp; + struct bnx2i_endpoint *ep; + + read_lock_bh(&hba->ep_rdwr_lock); + list_for_each_safe(list, tmp, &hba->ep_destroy_list) { + ep = (struct bnx2i_endpoint *)list; + + if (ep->ep_iscsi_cid == iscsi_cid) + break; + ep = NULL; + } + read_unlock_bh(&hba->ep_rdwr_lock); + + if (!ep) + printk(KERN_ERR "l5 cid %d not found\n", iscsi_cid); + + return ep; +} + +/** + * bnx2i_ep_active_list_add - add an entry to ep active list + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * current active conn queue manager + */ +static void bnx2i_ep_active_list_add(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_add_tail(&ep->link, &hba->ep_active_list); + write_unlock_bh(&hba->ep_rdwr_lock); +} + + +/** + * bnx2i_ep_active_list_del - deletes an entry to ep active list + * @hba: pointer to adapter instance + * @ep: pointer to endpoint (transport identifier) structure + * + * current active conn queue manager + */ +static void bnx2i_ep_active_list_del(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + write_lock_bh(&hba->ep_rdwr_lock); + list_del_init(&ep->link); + write_unlock_bh(&hba->ep_rdwr_lock); +} + + +/** + * bnx2i_setup_host_queue_size - assigns shost->can_queue param + * @hba: pointer to adapter instance + * @shost: scsi host pointer + * + * Initializes 'can_queue' parameter based on how many outstanding commands + * the device can handle. Each device 5708/5709/57710 has different + * capabilities + */ +static void bnx2i_setup_host_queue_size(struct bnx2i_hba *hba, + struct Scsi_Host *shost) +{ + if (test_bit(BNX2I_NX2_DEV_5708, &hba->cnic_dev_type)) + shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708; + else if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) + shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5709; + else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) + shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_57710; + else + shost->can_queue = ISCSI_MAX_CMDS_PER_HBA_5708; +} + + +/** + * bnx2i_alloc_hba - allocate and init adapter instance + * @cnic: cnic device pointer + * + * allocate & initialize adapter structure and call other + * support routines to do per adapter initialization + */ +struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) +{ + struct Scsi_Host *shost; + struct bnx2i_hba *hba; + + shost = iscsi_host_alloc(&bnx2i_host_template, sizeof(*hba), 0); + if (!shost) + return NULL; + shost->dma_boundary = cnic->pcidev->dma_mask; + shost->transportt = bnx2i_scsi_xport_template; + shost->max_id = ISCSI_MAX_CONNS_PER_HBA; + shost->max_channel = 0; + shost->max_lun = 512; + shost->max_cmd_len = 16; + + hba = iscsi_host_priv(shost); + hba->shost = shost; + hba->netdev = cnic->netdev; + /* Get PCI related information and update hba struct members */ + hba->pcidev = cnic->pcidev; + pci_dev_get(hba->pcidev); + hba->pci_did = hba->pcidev->device; + hba->pci_vid = hba->pcidev->vendor; + hba->pci_sdid = hba->pcidev->subsystem_device; + hba->pci_svid = hba->pcidev->subsystem_vendor; + hba->pci_func = PCI_FUNC(hba->pcidev->devfn); + hba->pci_devno = PCI_SLOT(hba->pcidev->devfn); + + bnx2i_identify_device(hba, cnic); + bnx2i_setup_host_queue_size(hba, shost); + + hba->reg_base = pci_resource_start(hba->pcidev, 0); + if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) { + hba->regview = pci_iomap(hba->pcidev, 0, BNX2_MQ_CONFIG2); + if (!hba->regview) + goto ioreg_map_err; + } else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { + hba->regview = pci_iomap(hba->pcidev, 0, 4096); + if (!hba->regview) + goto ioreg_map_err; + } + + if (bnx2i_setup_mp_bdt(hba)) + goto mp_bdt_mem_err; + + INIT_LIST_HEAD(&hba->ep_ofld_list); + INIT_LIST_HEAD(&hba->ep_active_list); + INIT_LIST_HEAD(&hba->ep_destroy_list); + rwlock_init(&hba->ep_rdwr_lock); + + hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED; + + /* different values for 5708/5709/57710 */ + hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA; + + if (bnx2i_setup_free_cid_que(hba)) + goto cid_que_err; + + /* SQ/RQ/CQ size can be changed via sysfx interface */ + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { + if (sq_size && sq_size <= BNX2I_5770X_SQ_WQES_MAX) + hba->max_sqes = sq_size; + else + hba->max_sqes = BNX2I_5770X_SQ_WQES_DEFAULT; + } else { /* 5706/5708/5709 */ + if (sq_size && sq_size <= BNX2I_570X_SQ_WQES_MAX) + hba->max_sqes = sq_size; + else + hba->max_sqes = BNX2I_570X_SQ_WQES_DEFAULT; + } + + hba->max_rqes = rq_size; + hba->max_cqes = hba->max_sqes + rq_size; + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { + if (hba->max_cqes > BNX2I_5770X_CQ_WQES_MAX) + hba->max_cqes = BNX2I_5770X_CQ_WQES_MAX; + } else if (hba->max_cqes > BNX2I_570X_CQ_WQES_MAX) + hba->max_cqes = BNX2I_570X_CQ_WQES_MAX; + + hba->num_ccell = hba->max_sqes / 2; + + spin_lock_init(&hba->lock); + mutex_init(&hba->net_dev_lock); + init_waitqueue_head(&hba->eh_wait); + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { + hba->hba_shutdown_tmo = 30 * HZ; + hba->conn_teardown_tmo = 20 * HZ; + hba->conn_ctx_destroy_tmo = 6 * HZ; + } else { /* 5706/5708/5709 */ + hba->hba_shutdown_tmo = 20 * HZ; + hba->conn_teardown_tmo = 10 * HZ; + hba->conn_ctx_destroy_tmo = 2 * HZ; + } + +#ifdef CONFIG_32BIT + spin_lock_init(&hba->stat_lock); +#endif + memset(&hba->stats, 0, sizeof(struct iscsi_stats_info)); + + if (iscsi_host_add(shost, &hba->pcidev->dev)) + goto free_dump_mem; + return hba; + +free_dump_mem: + bnx2i_release_free_cid_que(hba); +cid_que_err: + bnx2i_free_mp_bdt(hba); +mp_bdt_mem_err: + if (hba->regview) { + pci_iounmap(hba->pcidev, hba->regview); + hba->regview = NULL; + } +ioreg_map_err: + pci_dev_put(hba->pcidev); + scsi_host_put(shost); + return NULL; +} + +/** + * bnx2i_free_hba- releases hba structure and resources held by the adapter + * @hba: pointer to adapter instance + * + * free adapter structure and call various cleanup routines. + */ +void bnx2i_free_hba(struct bnx2i_hba *hba) +{ + struct Scsi_Host *shost = hba->shost; + + iscsi_host_remove(shost); + INIT_LIST_HEAD(&hba->ep_ofld_list); + INIT_LIST_HEAD(&hba->ep_active_list); + INIT_LIST_HEAD(&hba->ep_destroy_list); + pci_dev_put(hba->pcidev); + + if (hba->regview) { + pci_iounmap(hba->pcidev, hba->regview); + hba->regview = NULL; + } + bnx2i_free_mp_bdt(hba); + bnx2i_release_free_cid_que(hba); + iscsi_host_free(shost); +} + +/** + * bnx2i_conn_free_login_resources - free DMA resources used for login process + * @hba: pointer to adapter instance + * @bnx2i_conn: iscsi connection pointer + * + * Login related resources, mostly BDT & payload DMA memory is freed + */ +static void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba, + struct bnx2i_conn *bnx2i_conn) +{ + if (bnx2i_conn->gen_pdu.resp_bd_tbl) { + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + bnx2i_conn->gen_pdu.resp_bd_tbl, + bnx2i_conn->gen_pdu.resp_bd_dma); + bnx2i_conn->gen_pdu.resp_bd_tbl = NULL; + } + + if (bnx2i_conn->gen_pdu.req_bd_tbl) { + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + bnx2i_conn->gen_pdu.req_bd_tbl, + bnx2i_conn->gen_pdu.req_bd_dma); + bnx2i_conn->gen_pdu.req_bd_tbl = NULL; + } + + if (bnx2i_conn->gen_pdu.resp_buf) { + dma_free_coherent(&hba->pcidev->dev, + ISCSI_DEF_MAX_RECV_SEG_LEN, + bnx2i_conn->gen_pdu.resp_buf, + bnx2i_conn->gen_pdu.resp_dma_addr); + bnx2i_conn->gen_pdu.resp_buf = NULL; + } + + if (bnx2i_conn->gen_pdu.req_buf) { + dma_free_coherent(&hba->pcidev->dev, + ISCSI_DEF_MAX_RECV_SEG_LEN, + bnx2i_conn->gen_pdu.req_buf, + bnx2i_conn->gen_pdu.req_dma_addr); + bnx2i_conn->gen_pdu.req_buf = NULL; + } +} + +/** + * bnx2i_conn_alloc_login_resources - alloc DMA resources for login/nop. + * @hba: pointer to adapter instance + * @bnx2i_conn: iscsi connection pointer + * + * Mgmt task DNA resources are allocated in this routine. + */ +static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba, + struct bnx2i_conn *bnx2i_conn) +{ + /* Allocate memory for login request/response buffers */ + bnx2i_conn->gen_pdu.req_buf = + dma_alloc_coherent(&hba->pcidev->dev, + ISCSI_DEF_MAX_RECV_SEG_LEN, + &bnx2i_conn->gen_pdu.req_dma_addr, + GFP_KERNEL); + if (bnx2i_conn->gen_pdu.req_buf == NULL) + goto login_req_buf_failure; + + bnx2i_conn->gen_pdu.req_buf_size = 0; + bnx2i_conn->gen_pdu.req_wr_ptr = bnx2i_conn->gen_pdu.req_buf; + + bnx2i_conn->gen_pdu.resp_buf = + dma_alloc_coherent(&hba->pcidev->dev, + ISCSI_DEF_MAX_RECV_SEG_LEN, + &bnx2i_conn->gen_pdu.resp_dma_addr, + GFP_KERNEL); + if (bnx2i_conn->gen_pdu.resp_buf == NULL) + goto login_resp_buf_failure; + + bnx2i_conn->gen_pdu.resp_buf_size = ISCSI_DEF_MAX_RECV_SEG_LEN; + bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf; + + bnx2i_conn->gen_pdu.req_bd_tbl = + dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + &bnx2i_conn->gen_pdu.req_bd_dma, GFP_KERNEL); + if (bnx2i_conn->gen_pdu.req_bd_tbl == NULL) + goto login_req_bd_tbl_failure; + + bnx2i_conn->gen_pdu.resp_bd_tbl = + dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + &bnx2i_conn->gen_pdu.resp_bd_dma, + GFP_KERNEL); + if (bnx2i_conn->gen_pdu.resp_bd_tbl == NULL) + goto login_resp_bd_tbl_failure; + + return 0; + +login_resp_bd_tbl_failure: + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, + bnx2i_conn->gen_pdu.req_bd_tbl, + bnx2i_conn->gen_pdu.req_bd_dma); + bnx2i_conn->gen_pdu.req_bd_tbl = NULL; + +login_req_bd_tbl_failure: + dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN, + bnx2i_conn->gen_pdu.resp_buf, + bnx2i_conn->gen_pdu.resp_dma_addr); + bnx2i_conn->gen_pdu.resp_buf = NULL; +login_resp_buf_failure: + dma_free_coherent(&hba->pcidev->dev, ISCSI_DEF_MAX_RECV_SEG_LEN, + bnx2i_conn->gen_pdu.req_buf, + bnx2i_conn->gen_pdu.req_dma_addr); + bnx2i_conn->gen_pdu.req_buf = NULL; +login_req_buf_failure: + iscsi_conn_printk(KERN_ERR, bnx2i_conn->cls_conn->dd_data, + "login resource alloc failed!!\n"); + return -ENOMEM; + +} + + +/** + * bnx2i_iscsi_prep_generic_pdu_bd - prepares BD table. + * @bnx2i_conn: iscsi connection pointer + * + * Allocates buffers and BD tables before shipping requests to cnic + * for PDUs prepared by 'iscsid' daemon + */ +static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *bnx2i_conn) +{ + struct iscsi_bd *bd_tbl; + + bd_tbl = (struct iscsi_bd *) bnx2i_conn->gen_pdu.req_bd_tbl; + + bd_tbl->buffer_addr_hi = + (u32) ((u64) bnx2i_conn->gen_pdu.req_dma_addr >> 32); + bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.req_dma_addr; + bd_tbl->buffer_length = bnx2i_conn->gen_pdu.req_wr_ptr - + bnx2i_conn->gen_pdu.req_buf; + bd_tbl->reserved0 = 0; + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN | + ISCSI_BD_FIRST_IN_BD_CHAIN; + + bd_tbl = (struct iscsi_bd *) bnx2i_conn->gen_pdu.resp_bd_tbl; + bd_tbl->buffer_addr_hi = (u64) bnx2i_conn->gen_pdu.resp_dma_addr >> 32; + bd_tbl->buffer_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_dma_addr; + bd_tbl->buffer_length = ISCSI_DEF_MAX_RECV_SEG_LEN; + bd_tbl->reserved0 = 0; + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN | + ISCSI_BD_FIRST_IN_BD_CHAIN; +} + + +/** + * bnx2i_iscsi_send_generic_request - called to send mgmt tasks. + * @task: transport layer task pointer + * + * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login, + * Nop-out and Logout requests flow through this path. + */ +static int bnx2i_iscsi_send_generic_request(struct iscsi_task *task) +{ + struct bnx2i_cmd *cmd = task->dd_data; + struct bnx2i_conn *bnx2i_conn = cmd->conn; + int rc = 0; + char *buf; + int data_len; + + bnx2i_iscsi_prep_generic_pdu_bd(bnx2i_conn); + switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { + case ISCSI_OP_LOGIN: + bnx2i_send_iscsi_login(bnx2i_conn, task); + break; + case ISCSI_OP_NOOP_OUT: + data_len = bnx2i_conn->gen_pdu.req_buf_size; + buf = bnx2i_conn->gen_pdu.req_buf; + if (data_len) + rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task, + buf, data_len, 1); + else + rc = bnx2i_send_iscsi_nopout(bnx2i_conn, task, + NULL, 0, 1); + break; + case ISCSI_OP_LOGOUT: + rc = bnx2i_send_iscsi_logout(bnx2i_conn, task); + break; + case ISCSI_OP_SCSI_TMFUNC: + rc = bnx2i_send_iscsi_tmf(bnx2i_conn, task); + break; + case ISCSI_OP_TEXT: + rc = bnx2i_send_iscsi_text(bnx2i_conn, task); + break; + default: + iscsi_conn_printk(KERN_ALERT, bnx2i_conn->cls_conn->dd_data, + "send_gen: unsupported op 0x%x\n", + task->hdr->opcode); + } + return rc; +} + + +/********************************************************************** + * SCSI-ML Interface + **********************************************************************/ + +/** + * bnx2i_cpy_scsi_cdb - copies LUN & CDB fields in required format to sq wqe + * @sc: SCSI-ML command pointer + * @cmd: iscsi cmd pointer + */ +static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc, struct bnx2i_cmd *cmd) +{ + u32 dword; + int lpcnt; + u8 *srcp; + u32 *dstp; + u32 scsi_lun[2]; + + int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun); + cmd->req.lun[0] = be32_to_cpu(scsi_lun[0]); + cmd->req.lun[1] = be32_to_cpu(scsi_lun[1]); + + lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword); + srcp = (u8 *) sc->cmnd; + dstp = (u32 *) cmd->req.cdb; + while (lpcnt--) { + memcpy(&dword, (const void *) srcp, 4); + *dstp = cpu_to_be32(dword); + srcp += 4; + dstp++; + } + if (sc->cmd_len & 0x3) { + dword = (u32) srcp[0] | ((u32) srcp[1] << 8); + *dstp = cpu_to_be32(dword); + } +} + +static void bnx2i_cleanup_task(struct iscsi_task *task) +{ + struct iscsi_conn *conn = task->conn; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; + + /* + * mgmt task or cmd was never sent to us to transmit. + */ + if (!task->sc || task->state == ISCSI_TASK_PENDING) + return; + /* + * need to clean-up task context to claim dma buffers + */ + if (task->state == ISCSI_TASK_ABRT_TMF) { + bnx2i_send_cmd_cleanup_req(hba, task->dd_data); + + spin_unlock_bh(&conn->session->back_lock); + wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl, + msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT)); + spin_lock_bh(&conn->session->back_lock); + } + bnx2i_iscsi_unmap_sg_list(task->dd_data); +} + +/** + * bnx2i_mtask_xmit - transmit mtask to chip for further processing + * @conn: transport layer conn structure pointer + * @task: transport layer command structure pointer + */ +static int +bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) +{ + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; + struct bnx2i_cmd *cmd = task->dd_data; + + memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN); + + bnx2i_setup_cmd_wqe_template(cmd); + bnx2i_conn->gen_pdu.req_buf_size = task->data_count; + + /* Tx PDU/data length count */ + ADD_STATS_64(hba, tx_pdus, 1); + ADD_STATS_64(hba, tx_bytes, task->data_count); + + if (task->data_count) { + memcpy(bnx2i_conn->gen_pdu.req_buf, task->data, + task->data_count); + bnx2i_conn->gen_pdu.req_wr_ptr = + bnx2i_conn->gen_pdu.req_buf + task->data_count; + } + cmd->conn = conn->dd_data; + cmd->scsi_cmd = NULL; + return bnx2i_iscsi_send_generic_request(task); +} + +/** + * bnx2i_task_xmit - transmit iscsi command to chip for further processing + * @task: transport layer command structure pointer + * + * maps SG buffers and send request to chip/firmware in the form of SQ WQE + */ +static int bnx2i_task_xmit(struct iscsi_task *task) +{ + struct iscsi_conn *conn = task->conn; + struct iscsi_session *session = conn->session; + struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session); + struct bnx2i_hba *hba = iscsi_host_priv(shost); + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct scsi_cmnd *sc = task->sc; + struct bnx2i_cmd *cmd = task->dd_data; + struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr; + + if (atomic_read(&bnx2i_conn->ep->num_active_cmds) + 1 > + hba->max_sqes) + return -ENOMEM; + + /* + * If there is no scsi_cmnd this must be a mgmt task + */ + if (!sc) + return bnx2i_mtask_xmit(conn, task); + + bnx2i_setup_cmd_wqe_template(cmd); + cmd->req.op_code = ISCSI_OP_SCSI_CMD; + cmd->conn = bnx2i_conn; + cmd->scsi_cmd = sc; + cmd->req.total_data_transfer_length = scsi_bufflen(sc); + cmd->req.cmd_sn = be32_to_cpu(hdr->cmdsn); + + bnx2i_iscsi_map_sg_list(cmd); + bnx2i_cpy_scsi_cdb(sc, cmd); + + cmd->req.op_attr = ISCSI_ATTR_SIMPLE; + if (sc->sc_data_direction == DMA_TO_DEVICE) { + cmd->req.op_attr |= ISCSI_CMD_REQUEST_WRITE; + cmd->req.itt = task->itt | + (ISCSI_TASK_TYPE_WRITE << ISCSI_CMD_REQUEST_TYPE_SHIFT); + bnx2i_setup_write_cmd_bd_info(task); + } else { + if (scsi_bufflen(sc)) + cmd->req.op_attr |= ISCSI_CMD_REQUEST_READ; + cmd->req.itt = task->itt | + (ISCSI_TASK_TYPE_READ << ISCSI_CMD_REQUEST_TYPE_SHIFT); + } + + cmd->req.num_bds = cmd->io_tbl.bd_valid; + if (!cmd->io_tbl.bd_valid) { + cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma; + cmd->req.bd_list_addr_hi = (u32) ((u64) hba->mp_bd_dma >> 32); + cmd->req.num_bds = 1; + } + + bnx2i_send_iscsi_scsicmd(bnx2i_conn, cmd); + return 0; +} + +/** + * bnx2i_session_create - create a new iscsi session + * @cmds_max: max commands supported + * @qdepth: scsi queue depth to support + * @initial_cmdsn: initial iscsi CMDSN to be used for this session + * + * Creates a new iSCSI session instance on given device. + */ +static struct iscsi_cls_session * +bnx2i_session_create(struct iscsi_endpoint *ep, + uint16_t cmds_max, uint16_t qdepth, + uint32_t initial_cmdsn) +{ + struct Scsi_Host *shost; + struct iscsi_cls_session *cls_session; + struct bnx2i_hba *hba; + struct bnx2i_endpoint *bnx2i_ep; + + if (!ep) { + printk(KERN_ERR "bnx2i: missing ep.\n"); + return NULL; + } + + bnx2i_ep = ep->dd_data; + shost = bnx2i_ep->hba->shost; + hba = iscsi_host_priv(shost); + if (bnx2i_adapter_ready(hba)) + return NULL; + + /* + * user can override hw limit as long as it is within + * the min/max. + */ + if (cmds_max > hba->max_sqes) + cmds_max = hba->max_sqes; + else if (cmds_max < BNX2I_SQ_WQES_MIN) + cmds_max = BNX2I_SQ_WQES_MIN; + + cls_session = iscsi_session_setup(&bnx2i_iscsi_transport, shost, + cmds_max, 0, sizeof(struct bnx2i_cmd), + initial_cmdsn, ISCSI_MAX_TARGET); + if (!cls_session) + return NULL; + + if (bnx2i_setup_cmd_pool(hba, cls_session->dd_data)) + goto session_teardown; + return cls_session; + +session_teardown: + iscsi_session_teardown(cls_session); + return NULL; +} + + +/** + * bnx2i_session_destroy - destroys iscsi session + * @cls_session: pointer to iscsi cls session + * + * Destroys previously created iSCSI session instance and releases + * all resources held by it + */ +static void bnx2i_session_destroy(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct bnx2i_hba *hba = iscsi_host_priv(shost); + + bnx2i_destroy_cmd_pool(hba, session); + iscsi_session_teardown(cls_session); +} + + +/** + * bnx2i_conn_create - create iscsi connection instance + * @cls_session: pointer to iscsi cls session + * @cid: iscsi cid as per rfc (not NX2's CID terminology) + * + * Creates a new iSCSI connection instance for a given session + */ +static struct iscsi_cls_conn * +bnx2i_conn_create(struct iscsi_cls_session *cls_session, uint32_t cid) +{ + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct bnx2i_hba *hba = iscsi_host_priv(shost); + struct bnx2i_conn *bnx2i_conn; + struct iscsi_cls_conn *cls_conn; + struct iscsi_conn *conn; + + cls_conn = iscsi_conn_setup(cls_session, sizeof(*bnx2i_conn), + cid); + if (!cls_conn) + return NULL; + conn = cls_conn->dd_data; + + bnx2i_conn = conn->dd_data; + bnx2i_conn->cls_conn = cls_conn; + bnx2i_conn->hba = hba; + + atomic_set(&bnx2i_conn->work_cnt, 0); + + /* 'ep' ptr will be assigned in bind() call */ + bnx2i_conn->ep = NULL; + init_completion(&bnx2i_conn->cmd_cleanup_cmpl); + + if (bnx2i_conn_alloc_login_resources(hba, bnx2i_conn)) { + iscsi_conn_printk(KERN_ALERT, conn, + "conn_new: login resc alloc failed!!\n"); + goto free_conn; + } + + return cls_conn; + +free_conn: + iscsi_conn_teardown(cls_conn); + return NULL; +} + +/** + * bnx2i_conn_bind - binds iscsi sess, conn and ep objects together + * @cls_session: pointer to iscsi cls session + * @cls_conn: pointer to iscsi cls conn + * @transport_fd: 64-bit EP handle + * @is_leading: leading connection on this session? + * + * Binds together iSCSI session instance, iSCSI connection instance + * and the TCP connection. This routine returns error code if + * TCP connection does not belong on the device iSCSI sess/conn + * is bound + */ +static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session, + struct iscsi_cls_conn *cls_conn, + uint64_t transport_fd, int is_leading) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + struct bnx2i_hba *hba = iscsi_host_priv(shost); + struct bnx2i_endpoint *bnx2i_ep; + struct iscsi_endpoint *ep; + int ret_code; + + ep = iscsi_lookup_endpoint(transport_fd); + if (!ep) + return -EINVAL; + /* + * Forcefully terminate all in progress connection recovery at the + * earliest, either in bind(), send_pdu(LOGIN), or conn_start() + */ + if (bnx2i_adapter_ready(hba)) + return -EIO; + + bnx2i_ep = ep->dd_data; + if ((bnx2i_ep->state == EP_STATE_TCP_FIN_RCVD) || + (bnx2i_ep->state == EP_STATE_TCP_RST_RCVD)) + /* Peer disconnect via' FIN or RST */ + return -EINVAL; + + if (iscsi_conn_bind(cls_session, cls_conn, is_leading)) + return -EINVAL; + + if (bnx2i_ep->hba != hba) { + /* Error - TCP connection does not belong to this device + */ + iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data, + "conn bind, ep=0x%p (%s) does not", + bnx2i_ep, bnx2i_ep->hba->netdev->name); + iscsi_conn_printk(KERN_ALERT, cls_conn->dd_data, + "belong to hba (%s)\n", + hba->netdev->name); + return -EEXIST; + } + bnx2i_ep->conn = bnx2i_conn; + bnx2i_conn->ep = bnx2i_ep; + bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid; + bnx2i_conn->fw_cid = bnx2i_ep->ep_cid; + + ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn, + bnx2i_ep->ep_iscsi_cid); + + /* 5706/5708/5709 FW takes RQ as full when initiated, but for 57710 + * driver needs to explicitly replenish RQ index during setup. + */ + if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type)) + bnx2i_put_rq_buf(bnx2i_conn, 0); + + bnx2i_arm_cq_event_coalescing(bnx2i_conn->ep, CNIC_ARM_CQE); + return ret_code; +} + + +/** + * bnx2i_conn_destroy - destroy iscsi connection instance & release resources + * @cls_conn: pointer to iscsi cls conn + * + * Destroy an iSCSI connection instance and release memory resources held by + * this connection + */ +static void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct Scsi_Host *shost; + struct bnx2i_hba *hba; + struct bnx2i_work *work, *tmp; + unsigned cpu = 0; + struct bnx2i_percpu_s *p; + + shost = iscsi_session_to_shost(iscsi_conn_to_session(cls_conn)); + hba = iscsi_host_priv(shost); + + bnx2i_conn_free_login_resources(hba, bnx2i_conn); + + if (atomic_read(&bnx2i_conn->work_cnt)) { + for_each_online_cpu(cpu) { + p = &per_cpu(bnx2i_percpu, cpu); + spin_lock_bh(&p->p_work_lock); + list_for_each_entry_safe(work, tmp, + &p->work_list, list) { + if (work->session == conn->session && + work->bnx2i_conn == bnx2i_conn) { + list_del_init(&work->list); + kfree(work); + if (!atomic_dec_and_test( + &bnx2i_conn->work_cnt)) + break; + } + } + spin_unlock_bh(&p->p_work_lock); + } + } + + iscsi_conn_teardown(cls_conn); +} + + +/** + * bnx2i_ep_get_param - return iscsi ep parameter to caller + * @ep: pointer to iscsi endpoint + * @param: parameter type identifier + * @buf: buffer pointer + * + * returns iSCSI ep parameters + */ +static int bnx2i_ep_get_param(struct iscsi_endpoint *ep, + enum iscsi_param param, char *buf) +{ + struct bnx2i_endpoint *bnx2i_ep = ep->dd_data; + struct bnx2i_hba *hba = bnx2i_ep->hba; + int len = -ENOTCONN; + + if (!hba) + return -ENOTCONN; + + switch (param) { + case ISCSI_PARAM_CONN_PORT: + mutex_lock(&hba->net_dev_lock); + if (bnx2i_ep->cm_sk) + len = sprintf(buf, "%hu\n", bnx2i_ep->cm_sk->dst_port); + mutex_unlock(&hba->net_dev_lock); + break; + case ISCSI_PARAM_CONN_ADDRESS: + mutex_lock(&hba->net_dev_lock); + if (bnx2i_ep->cm_sk) + len = sprintf(buf, "%pI4\n", &bnx2i_ep->cm_sk->dst_ip); + mutex_unlock(&hba->net_dev_lock); + break; + default: + return -ENOSYS; + } + + return len; +} + +/** + * bnx2i_host_get_param - returns host (adapter) related parameters + * @shost: scsi host pointer + * @param: parameter type identifier + * @buf: buffer pointer + */ +static int bnx2i_host_get_param(struct Scsi_Host *shost, + enum iscsi_host_param param, char *buf) +{ + struct bnx2i_hba *hba = iscsi_host_priv(shost); + int len = 0; + + switch (param) { + case ISCSI_HOST_PARAM_HWADDRESS: + len = sysfs_format_mac(buf, hba->cnic->mac_addr, 6); + break; + case ISCSI_HOST_PARAM_NETDEV_NAME: + len = sprintf(buf, "%s\n", hba->netdev->name); + break; + case ISCSI_HOST_PARAM_IPADDRESS: { + struct list_head *active_list = &hba->ep_active_list; + + read_lock_bh(&hba->ep_rdwr_lock); + if (!list_empty(&hba->ep_active_list)) { + struct bnx2i_endpoint *bnx2i_ep; + struct cnic_sock *csk; + + bnx2i_ep = list_first_entry(active_list, + struct bnx2i_endpoint, + link); + csk = bnx2i_ep->cm_sk; + if (test_bit(SK_F_IPV6, &csk->flags)) + len = sprintf(buf, "%pI6\n", csk->src_ip); + else + len = sprintf(buf, "%pI4\n", csk->src_ip); + } + read_unlock_bh(&hba->ep_rdwr_lock); + break; + } + default: + return iscsi_host_get_param(shost, param, buf); + } + return len; +} + +/** + * bnx2i_conn_start - completes iscsi connection migration to FFP + * @cls_conn: pointer to iscsi cls conn + * + * last call in FFP migration to handover iscsi conn to the driver + */ +static int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + struct bnx2i_conn *bnx2i_conn = conn->dd_data; + + bnx2i_conn->ep->state = EP_STATE_ULP_UPDATE_START; + bnx2i_update_iscsi_conn(conn); + + /* + * this should normally not sleep for a long time so it should + * not disrupt the caller. + */ + bnx2i_conn->ep->ofld_timer.expires = 1 * HZ + jiffies; + bnx2i_conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer; + bnx2i_conn->ep->ofld_timer.data = (unsigned long) bnx2i_conn->ep; + add_timer(&bnx2i_conn->ep->ofld_timer); + /* update iSCSI context for this conn, wait for CNIC to complete */ + wait_event_interruptible(bnx2i_conn->ep->ofld_wait, + bnx2i_conn->ep->state != EP_STATE_ULP_UPDATE_START); + + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&bnx2i_conn->ep->ofld_timer); + + iscsi_conn_start(cls_conn); + return 0; +} + + +/** + * bnx2i_conn_get_stats - returns iSCSI stats + * @cls_conn: pointer to iscsi cls conn + * @stats: pointer to iscsi statistic struct + */ +static void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn, + struct iscsi_stats *stats) +{ + struct iscsi_conn *conn = cls_conn->dd_data; + + stats->txdata_octets = conn->txdata_octets; + stats->rxdata_octets = conn->rxdata_octets; + stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; + stats->dataout_pdus = conn->dataout_pdus_cnt; + stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; + stats->datain_pdus = conn->datain_pdus_cnt; + stats->r2t_pdus = conn->r2t_pdus_cnt; + stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; + stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; + stats->digest_err = 0; + stats->timeout_err = 0; + strcpy(stats->custom[0].desc, "eh_abort_cnt"); + stats->custom[0].value = conn->eh_abort_cnt; + stats->custom_length = 1; +} + + +/** + * bnx2i_check_route - checks if target IP route belongs to one of NX2 devices + * @dst_addr: target IP address + * + * check if route resolves to BNX2 device + */ +static struct bnx2i_hba *bnx2i_check_route(struct sockaddr *dst_addr) +{ + struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr; + struct bnx2i_hba *hba; + struct cnic_dev *cnic = NULL; + + hba = get_adapter_list_head(); + if (hba && hba->cnic) + cnic = hba->cnic->cm_select_dev(desti, CNIC_ULP_ISCSI); + if (!cnic) { + printk(KERN_ALERT "bnx2i: no route," + "can't connect using cnic\n"); + goto no_nx2_route; + } + hba = bnx2i_find_hba_for_cnic(cnic); + if (!hba) + goto no_nx2_route; + + if (bnx2i_adapter_ready(hba)) { + printk(KERN_ALERT "bnx2i: check route, hba not found\n"); + goto no_nx2_route; + } + if (hba->netdev->mtu > hba->mtu_supported) { + printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n", + hba->netdev->name, hba->netdev->mtu); + printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n", + hba->mtu_supported); + goto no_nx2_route; + } + return hba; +no_nx2_route: + return NULL; +} + + +/** + * bnx2i_tear_down_conn - tear down iscsi/tcp connection and free resources + * @hba: pointer to adapter instance + * @ep: endpoint (transport identifier) structure + * + * destroys cm_sock structure and on chip iscsi context + */ +static int bnx2i_tear_down_conn(struct bnx2i_hba *hba, + struct bnx2i_endpoint *ep) +{ + if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic) && ep->cm_sk) + hba->cnic->cm_destroy(ep->cm_sk); + + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type) && + ep->state == EP_STATE_DISCONN_TIMEDOUT) { + if (ep->conn && ep->conn->cls_conn && + ep->conn->cls_conn->dd_data) { + struct iscsi_conn *conn = ep->conn->cls_conn->dd_data; + + /* Must suspend all rx queue activity for this ep */ + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); + } + /* CONN_DISCONNECT timeout may or may not be an issue depending + * on what transcribed in TCP layer, different targets behave + * differently + */ + printk(KERN_ALERT "bnx2i (%s): - WARN - CONN_DISCON timed out, " + "please submit GRC Dump, NW/PCIe trace, " + "driver msgs to developers for analysis\n", + hba->netdev->name); + } + + ep->state = EP_STATE_CLEANUP_START; + init_timer(&ep->ofld_timer); + ep->ofld_timer.expires = hba->conn_ctx_destroy_tmo + jiffies; + ep->ofld_timer.function = bnx2i_ep_ofld_timer; + ep->ofld_timer.data = (unsigned long) ep; + add_timer(&ep->ofld_timer); + + bnx2i_ep_destroy_list_add(hba, ep); + + /* destroy iSCSI context, wait for it to complete */ + if (bnx2i_send_conn_destroy(hba, ep)) + ep->state = EP_STATE_CLEANUP_CMPL; + + wait_event_interruptible(ep->ofld_wait, + (ep->state != EP_STATE_CLEANUP_START)); + + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&ep->ofld_timer); + + bnx2i_ep_destroy_list_del(hba, ep); + + if (ep->state != EP_STATE_CLEANUP_CMPL) + /* should never happen */ + printk(KERN_ALERT "bnx2i - conn destroy failed\n"); + + return 0; +} + + +/** + * bnx2i_ep_connect - establish TCP connection to target portal + * @shost: scsi host + * @dst_addr: target IP address + * @non_blocking: blocking or non-blocking call + * + * this routine initiates the TCP/IP connection by invoking Option-2 i/f + * with l5_core and the CNIC. This is a multi-step process of resolving + * route to target, create a iscsi connection context, handshaking with + * CNIC module to create/initialize the socket struct and finally + * sending down option-2 request to complete TCP 3-way handshake + */ +static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost, + struct sockaddr *dst_addr, + int non_blocking) +{ + u32 iscsi_cid = BNX2I_CID_RESERVED; + struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr; + struct sockaddr_in6 *desti6; + struct bnx2i_endpoint *bnx2i_ep; + struct bnx2i_hba *hba; + struct cnic_dev *cnic; + struct cnic_sockaddr saddr; + struct iscsi_endpoint *ep; + int rc = 0; + + if (shost) { + /* driver is given scsi host to work with */ + hba = iscsi_host_priv(shost); + } else + /* + * check if the given destination can be reached through + * a iscsi capable NetXtreme2 device + */ + hba = bnx2i_check_route(dst_addr); + + if (!hba) { + rc = -EINVAL; + goto nohba; + } + mutex_lock(&hba->net_dev_lock); + + if (bnx2i_adapter_ready(hba) || !hba->cid_que.cid_free_cnt) { + rc = -EPERM; + goto check_busy; + } + cnic = hba->cnic; + ep = bnx2i_alloc_ep(hba); + if (!ep) { + rc = -ENOMEM; + goto check_busy; + } + bnx2i_ep = ep->dd_data; + + atomic_set(&bnx2i_ep->num_active_cmds, 0); + iscsi_cid = bnx2i_alloc_iscsi_cid(hba); + if (iscsi_cid == -1) { + printk(KERN_ALERT "bnx2i (%s): alloc_ep - unable to allocate " + "iscsi cid\n", hba->netdev->name); + rc = -ENOMEM; + bnx2i_free_ep(ep); + goto check_busy; + } + bnx2i_ep->hba_age = hba->age; + + rc = bnx2i_alloc_qp_resc(hba, bnx2i_ep); + if (rc != 0) { + printk(KERN_ALERT "bnx2i (%s): ep_conn - alloc QP resc error" + "\n", hba->netdev->name); + rc = -ENOMEM; + goto qp_resc_err; + } + + bnx2i_ep->ep_iscsi_cid = (u16)iscsi_cid; + bnx2i_ep->state = EP_STATE_OFLD_START; + bnx2i_ep_ofld_list_add(hba, bnx2i_ep); + + init_timer(&bnx2i_ep->ofld_timer); + bnx2i_ep->ofld_timer.expires = 2 * HZ + jiffies; + bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer; + bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep; + add_timer(&bnx2i_ep->ofld_timer); + + if (bnx2i_send_conn_ofld_req(hba, bnx2i_ep)) { + if (bnx2i_ep->state == EP_STATE_OFLD_FAILED_CID_BUSY) { + printk(KERN_ALERT "bnx2i (%s): iscsi cid %d is busy\n", + hba->netdev->name, bnx2i_ep->ep_iscsi_cid); + rc = -EBUSY; + } else + rc = -ENOSPC; + printk(KERN_ALERT "bnx2i (%s): unable to send conn offld kwqe" + "\n", hba->netdev->name); + bnx2i_ep_ofld_list_del(hba, bnx2i_ep); + goto conn_failed; + } + + /* Wait for CNIC hardware to setup conn context and return 'cid' */ + wait_event_interruptible(bnx2i_ep->ofld_wait, + bnx2i_ep->state != EP_STATE_OFLD_START); + + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&bnx2i_ep->ofld_timer); + + bnx2i_ep_ofld_list_del(hba, bnx2i_ep); + + if (bnx2i_ep->state != EP_STATE_OFLD_COMPL) { + if (bnx2i_ep->state == EP_STATE_OFLD_FAILED_CID_BUSY) { + printk(KERN_ALERT "bnx2i (%s): iscsi cid %d is busy\n", + hba->netdev->name, bnx2i_ep->ep_iscsi_cid); + rc = -EBUSY; + } else + rc = -ENOSPC; + goto conn_failed; + } + + rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, bnx2i_ep->ep_cid, + iscsi_cid, &bnx2i_ep->cm_sk, bnx2i_ep); + if (rc) { + rc = -EINVAL; + /* Need to terminate and cleanup the connection */ + goto release_ep; + } + + bnx2i_ep->cm_sk->rcv_buf = 256 * 1024; + bnx2i_ep->cm_sk->snd_buf = 256 * 1024; + clear_bit(SK_TCP_TIMESTAMP, &bnx2i_ep->cm_sk->tcp_flags); + + memset(&saddr, 0, sizeof(saddr)); + if (dst_addr->sa_family == AF_INET) { + desti = (struct sockaddr_in *) dst_addr; + saddr.remote.v4 = *desti; + saddr.local.v4.sin_family = desti->sin_family; + } else if (dst_addr->sa_family == AF_INET6) { + desti6 = (struct sockaddr_in6 *) dst_addr; + saddr.remote.v6 = *desti6; + saddr.local.v6.sin6_family = desti6->sin6_family; + } + + bnx2i_ep->timestamp = jiffies; + bnx2i_ep->state = EP_STATE_CONNECT_START; + if (!test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) { + rc = -EINVAL; + goto conn_failed; + } else + rc = cnic->cm_connect(bnx2i_ep->cm_sk, &saddr); + if (rc) + goto release_ep; + + bnx2i_ep_active_list_add(hba, bnx2i_ep); + + if (bnx2i_map_ep_dbell_regs(bnx2i_ep)) + goto del_active_ep; + + mutex_unlock(&hba->net_dev_lock); + return ep; + +del_active_ep: + bnx2i_ep_active_list_del(hba, bnx2i_ep); +release_ep: + if (bnx2i_tear_down_conn(hba, bnx2i_ep)) { + mutex_unlock(&hba->net_dev_lock); + return ERR_PTR(rc); + } +conn_failed: + bnx2i_free_qp_resc(hba, bnx2i_ep); +qp_resc_err: + bnx2i_free_ep(ep); +check_busy: + mutex_unlock(&hba->net_dev_lock); +nohba: + return ERR_PTR(rc); +} + + +/** + * bnx2i_ep_poll - polls for TCP connection establishement + * @ep: TCP connection (endpoint) handle + * @timeout_ms: timeout value in milli secs + * + * polls for TCP connect request to complete + */ +static int bnx2i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms) +{ + struct bnx2i_endpoint *bnx2i_ep; + int rc = 0; + + bnx2i_ep = ep->dd_data; + if ((bnx2i_ep->state == EP_STATE_IDLE) || + (bnx2i_ep->state == EP_STATE_CONNECT_FAILED) || + (bnx2i_ep->state == EP_STATE_OFLD_FAILED)) + return -1; + if (bnx2i_ep->state == EP_STATE_CONNECT_COMPL) + return 1; + + rc = wait_event_interruptible_timeout(bnx2i_ep->ofld_wait, + ((bnx2i_ep->state == + EP_STATE_OFLD_FAILED) || + (bnx2i_ep->state == + EP_STATE_CONNECT_FAILED) || + (bnx2i_ep->state == + EP_STATE_CONNECT_COMPL)), + msecs_to_jiffies(timeout_ms)); + if (bnx2i_ep->state == EP_STATE_OFLD_FAILED) + rc = -1; + + if (rc > 0) + return 1; + else if (!rc) + return 0; /* timeout */ + else + return rc; +} + + +/** + * bnx2i_ep_tcp_conn_active - check EP state transition + * @ep: endpoint pointer + * + * check if underlying TCP connection is active + */ +static int bnx2i_ep_tcp_conn_active(struct bnx2i_endpoint *bnx2i_ep) +{ + int ret; + int cnic_dev_10g = 0; + + if (test_bit(BNX2I_NX2_DEV_57710, &bnx2i_ep->hba->cnic_dev_type)) + cnic_dev_10g = 1; + + switch (bnx2i_ep->state) { + case EP_STATE_CLEANUP_FAILED: + case EP_STATE_OFLD_FAILED: + case EP_STATE_DISCONN_TIMEDOUT: + ret = 0; + break; + case EP_STATE_CONNECT_START: + case EP_STATE_CONNECT_FAILED: + case EP_STATE_CONNECT_COMPL: + case EP_STATE_ULP_UPDATE_START: + case EP_STATE_ULP_UPDATE_COMPL: + case EP_STATE_TCP_FIN_RCVD: + case EP_STATE_LOGOUT_SENT: + case EP_STATE_LOGOUT_RESP_RCVD: + case EP_STATE_ULP_UPDATE_FAILED: + ret = 1; + break; + case EP_STATE_TCP_RST_RCVD: + if (cnic_dev_10g) + ret = 0; + else + ret = 1; + break; + default: + ret = 0; + } + + return ret; +} + + +/* + * bnx2i_hw_ep_disconnect - executes TCP connection teardown process in the hw + * @ep: TCP connection (bnx2i endpoint) handle + * + * executes TCP connection teardown process + */ +int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep) +{ + struct bnx2i_hba *hba = bnx2i_ep->hba; + struct cnic_dev *cnic; + struct iscsi_session *session = NULL; + struct iscsi_conn *conn = NULL; + int ret = 0; + int close = 0; + int close_ret = 0; + + if (!hba) + return 0; + + cnic = hba->cnic; + if (!cnic) + return 0; + + if (bnx2i_ep->state == EP_STATE_IDLE || + bnx2i_ep->state == EP_STATE_DISCONN_TIMEDOUT) + return 0; + + if (!bnx2i_ep_tcp_conn_active(bnx2i_ep)) + goto destroy_conn; + + if (bnx2i_ep->conn) { + conn = bnx2i_ep->conn->cls_conn->dd_data; + session = conn->session; + } + + init_timer(&bnx2i_ep->ofld_timer); + bnx2i_ep->ofld_timer.expires = hba->conn_teardown_tmo + jiffies; + bnx2i_ep->ofld_timer.function = bnx2i_ep_ofld_timer; + bnx2i_ep->ofld_timer.data = (unsigned long) bnx2i_ep; + add_timer(&bnx2i_ep->ofld_timer); + + if (!test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) + goto out; + + if (session) { + spin_lock_bh(&session->frwd_lock); + if (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD) { + if (session->state == ISCSI_STATE_LOGGING_OUT) { + if (bnx2i_ep->state == EP_STATE_LOGOUT_SENT) { + /* Logout sent, but no resp */ + printk(KERN_ALERT "bnx2i (%s): WARNING" + " logout response was not " + "received!\n", + bnx2i_ep->hba->netdev->name); + } else if (bnx2i_ep->state == + EP_STATE_LOGOUT_RESP_RCVD) + close = 1; + } + } else + close = 1; + + spin_unlock_bh(&session->frwd_lock); + } + + bnx2i_ep->state = EP_STATE_DISCONN_START; + + if (close) + close_ret = cnic->cm_close(bnx2i_ep->cm_sk); + else + close_ret = cnic->cm_abort(bnx2i_ep->cm_sk); + + if (close_ret) + printk(KERN_ALERT "bnx2i (%s): close/abort(%d) returned %d\n", + bnx2i_ep->hba->netdev->name, close, close_ret); + else + /* wait for option-2 conn teardown */ + wait_event_interruptible(bnx2i_ep->ofld_wait, + bnx2i_ep->state != EP_STATE_DISCONN_START); + + if (signal_pending(current)) + flush_signals(current); + del_timer_sync(&bnx2i_ep->ofld_timer); + +destroy_conn: + bnx2i_ep_active_list_del(hba, bnx2i_ep); + if (bnx2i_tear_down_conn(hba, bnx2i_ep)) + return -EINVAL; +out: + bnx2i_ep->state = EP_STATE_IDLE; + return ret; +} + + +/** + * bnx2i_ep_disconnect - executes TCP connection teardown process + * @ep: TCP connection (iscsi endpoint) handle + * + * executes TCP connection teardown process + */ +static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep) +{ + struct bnx2i_endpoint *bnx2i_ep; + struct bnx2i_conn *bnx2i_conn = NULL; + struct iscsi_conn *conn = NULL; + struct bnx2i_hba *hba; + + bnx2i_ep = ep->dd_data; + + /* driver should not attempt connection cleanup until TCP_CONNECT + * completes either successfully or fails. Timeout is 9-secs, so + * wait for it to complete + */ + while ((bnx2i_ep->state == EP_STATE_CONNECT_START) && + !time_after(jiffies, bnx2i_ep->timestamp + (12 * HZ))) + msleep(250); + + if (bnx2i_ep->conn) { + bnx2i_conn = bnx2i_ep->conn; + conn = bnx2i_conn->cls_conn->dd_data; + iscsi_suspend_queue(conn); + } + hba = bnx2i_ep->hba; + + mutex_lock(&hba->net_dev_lock); + + if (bnx2i_ep->state == EP_STATE_DISCONN_TIMEDOUT) + goto out; + + if (bnx2i_ep->state == EP_STATE_IDLE) + goto free_resc; + + if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) || + (bnx2i_ep->hba_age != hba->age)) { + bnx2i_ep_active_list_del(hba, bnx2i_ep); + goto free_resc; + } + + /* Do all chip cleanup here */ + if (bnx2i_hw_ep_disconnect(bnx2i_ep)) { + mutex_unlock(&hba->net_dev_lock); + return; + } +free_resc: + bnx2i_free_qp_resc(hba, bnx2i_ep); + + if (bnx2i_conn) + bnx2i_conn->ep = NULL; + + bnx2i_free_ep(ep); +out: + mutex_unlock(&hba->net_dev_lock); + + wake_up_interruptible(&hba->eh_wait); +} + + +/** + * bnx2i_nl_set_path - ISCSI_UEVENT_PATH_UPDATE user message handler + * @buf: pointer to buffer containing iscsi path message + * + */ +static int bnx2i_nl_set_path(struct Scsi_Host *shost, struct iscsi_path *params) +{ + struct bnx2i_hba *hba = iscsi_host_priv(shost); + char *buf = (char *) params; + u16 len = sizeof(*params); + + /* handled by cnic driver */ + hba->cnic->iscsi_nl_msg_recv(hba->cnic, ISCSI_UEVENT_PATH_UPDATE, buf, + len); + + return 0; +} + +static umode_t bnx2i_attr_is_visible(int param_type, int param) +{ + switch (param_type) { + case ISCSI_HOST_PARAM: + switch (param) { + case ISCSI_HOST_PARAM_NETDEV_NAME: + case ISCSI_HOST_PARAM_HWADDRESS: + case ISCSI_HOST_PARAM_IPADDRESS: + return S_IRUGO; + default: + return 0; + } + case ISCSI_PARAM: + switch (param) { + case ISCSI_PARAM_MAX_RECV_DLENGTH: + case ISCSI_PARAM_MAX_XMIT_DLENGTH: + case ISCSI_PARAM_HDRDGST_EN: + case ISCSI_PARAM_DATADGST_EN: + case ISCSI_PARAM_CONN_ADDRESS: + case ISCSI_PARAM_CONN_PORT: + case ISCSI_PARAM_EXP_STATSN: + case ISCSI_PARAM_PERSISTENT_ADDRESS: + case ISCSI_PARAM_PERSISTENT_PORT: + case ISCSI_PARAM_PING_TMO: + case ISCSI_PARAM_RECV_TMO: + case ISCSI_PARAM_INITIAL_R2T_EN: + case ISCSI_PARAM_MAX_R2T: + case ISCSI_PARAM_IMM_DATA_EN: + case ISCSI_PARAM_FIRST_BURST: + case ISCSI_PARAM_MAX_BURST: + case ISCSI_PARAM_PDU_INORDER_EN: + case ISCSI_PARAM_DATASEQ_INORDER_EN: + case ISCSI_PARAM_ERL: + case ISCSI_PARAM_TARGET_NAME: + case ISCSI_PARAM_TPGT: + case ISCSI_PARAM_USERNAME: + case ISCSI_PARAM_PASSWORD: + case ISCSI_PARAM_USERNAME_IN: + case ISCSI_PARAM_PASSWORD_IN: + case ISCSI_PARAM_FAST_ABORT: + case ISCSI_PARAM_ABORT_TMO: + case ISCSI_PARAM_LU_RESET_TMO: + case ISCSI_PARAM_TGT_RESET_TMO: + case ISCSI_PARAM_IFACE_NAME: + case ISCSI_PARAM_INITIATOR_NAME: + case ISCSI_PARAM_BOOT_ROOT: + case ISCSI_PARAM_BOOT_NIC: + case ISCSI_PARAM_BOOT_TARGET: + return S_IRUGO; + default: + return 0; + } + } + + return 0; +} + +/* + * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template + * used while registering with the scsi host and iSCSI transport module. + */ +static struct scsi_host_template bnx2i_host_template = { + .module = THIS_MODULE, + .name = "QLogic Offload iSCSI Initiator", + .proc_name = "bnx2i", + .queuecommand = iscsi_queuecommand, + .eh_abort_handler = iscsi_eh_abort, + .eh_device_reset_handler = iscsi_eh_device_reset, + .eh_target_reset_handler = iscsi_eh_recover_target, + .change_queue_depth = scsi_change_queue_depth, + .target_alloc = iscsi_target_alloc, + .can_queue = 2048, + .max_sectors = 127, + .cmd_per_lun = 128, + .this_id = -1, + .use_clustering = ENABLE_CLUSTERING, + .sg_tablesize = ISCSI_MAX_BDS_PER_CMD, + .shost_attrs = bnx2i_dev_attributes, + .track_queue_depth = 1, +}; + +struct iscsi_transport bnx2i_iscsi_transport = { + .owner = THIS_MODULE, + .name = "bnx2i", + .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | + CAP_MULTI_R2T | CAP_DATADGST | + CAP_DATA_PATH_OFFLOAD | + CAP_TEXT_NEGO, + .create_session = bnx2i_session_create, + .destroy_session = bnx2i_session_destroy, + .create_conn = bnx2i_conn_create, + .bind_conn = bnx2i_conn_bind, + .destroy_conn = bnx2i_conn_destroy, + .attr_is_visible = bnx2i_attr_is_visible, + .set_param = iscsi_set_param, + .get_conn_param = iscsi_conn_get_param, + .get_session_param = iscsi_session_get_param, + .get_host_param = bnx2i_host_get_param, + .start_conn = bnx2i_conn_start, + .stop_conn = iscsi_conn_stop, + .send_pdu = iscsi_conn_send_pdu, + .xmit_task = bnx2i_task_xmit, + .get_stats = bnx2i_conn_get_stats, + /* TCP connect - disconnect - option-2 interface calls */ + .get_ep_param = bnx2i_ep_get_param, + .ep_connect = bnx2i_ep_connect, + .ep_poll = bnx2i_ep_poll, + .ep_disconnect = bnx2i_ep_disconnect, + .set_path = bnx2i_nl_set_path, + /* Error recovery timeout call */ + .session_recovery_timedout = iscsi_session_recovery_timedout, + .cleanup_task = bnx2i_cleanup_task, +}; diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c new file mode 100644 index 000000000..6d56fd60c --- /dev/null +++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c @@ -0,0 +1,145 @@ +/* bnx2i_sysfs.c: QLogic NetXtreme II iSCSI driver. + * + * Copyright (c) 2004 - 2013 Broadcom Corporation + * Copyright (c) 2014, QLogic 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: Anil Veerabhadrappa (anilgv@broadcom.com) + * Previously Maintained by: Eddie Wai (eddie.wai@broadcom.com) + * Maintained by: QLogic-Storage-Upstream@qlogic.com + */ + +#include "bnx2i.h" + +/** + * bnx2i_dev_to_hba - maps dev pointer to adapter struct + * @dev: device pointer + * + * Map device to hba structure + */ +static inline struct bnx2i_hba *bnx2i_dev_to_hba(struct device *dev) +{ + struct Scsi_Host *shost = class_to_shost(dev); + return iscsi_host_priv(shost); +} + + +/** + * bnx2i_show_sq_info - return(s currently configured send queue (SQ) size + * @dev: device pointer + * @buf: buffer to return current SQ size parameter + * + * Returns current SQ size parameter, this paramater determines the number + * outstanding iSCSI commands supported on a connection + */ +static ssize_t bnx2i_show_sq_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev); + + return sprintf(buf, "0x%x\n", hba->max_sqes); +} + + +/** + * bnx2i_set_sq_info - update send queue (SQ) size parameter + * @dev: device pointer + * @buf: buffer to return current SQ size parameter + * @count: parameter buffer size + * + * Interface for user to change shared queue size allocated for each conn + * Must be within SQ limits and a power of 2. For the latter this is needed + * because of how libiscsi preallocates tasks. + */ +static ssize_t bnx2i_set_sq_info(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev); + u32 val; + int max_sq_size; + + if (hba->ofld_conns_active) + goto skip_config; + + if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) + max_sq_size = BNX2I_5770X_SQ_WQES_MAX; + else + max_sq_size = BNX2I_570X_SQ_WQES_MAX; + + if (sscanf(buf, " 0x%x ", &val) > 0) { + if ((val >= BNX2I_SQ_WQES_MIN) && (val <= max_sq_size) && + (is_power_of_2(val))) + hba->max_sqes = val; + } + + return count; + +skip_config: + printk(KERN_ERR "bnx2i: device busy, cannot change SQ size\n"); + return 0; +} + + +/** + * bnx2i_show_ccell_info - returns command cell (HQ) size + * @dev: device pointer + * @buf: buffer to return current SQ size parameter + * + * returns per-connection TCP history queue size parameter + */ +static ssize_t bnx2i_show_ccell_info(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev); + + return sprintf(buf, "0x%x\n", hba->num_ccell); +} + + +/** + * bnx2i_get_link_state - set command cell (HQ) size + * @dev: device pointer + * @buf: buffer to return current SQ size parameter + * @count: parameter buffer size + * + * updates per-connection TCP history queue size parameter + */ +static ssize_t bnx2i_set_ccell_info(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 val; + struct bnx2i_hba *hba = bnx2i_dev_to_hba(dev); + + if (hba->ofld_conns_active) + goto skip_config; + + if (sscanf(buf, " 0x%x ", &val) > 0) { + if ((val >= BNX2I_CCELLS_MIN) && + (val <= BNX2I_CCELLS_MAX)) { + hba->num_ccell = val; + } + } + + return count; + +skip_config: + printk(KERN_ERR "bnx2i: device busy, cannot change CCELL size\n"); + return 0; +} + + +static DEVICE_ATTR(sq_size, S_IRUGO | S_IWUSR, + bnx2i_show_sq_info, bnx2i_set_sq_info); +static DEVICE_ATTR(num_ccell, S_IRUGO | S_IWUSR, + bnx2i_show_ccell_info, bnx2i_set_ccell_info); + +struct device_attribute *bnx2i_dev_attributes[] = { + &dev_attr_sq_size, + &dev_attr_num_ccell, + NULL +}; |