From: Ravi Anand Subject: Update qla4xxx driver for SLES11 SP1 References: bnc#556572,FATE#307128 Patch-mainline: not yet Change log from v5.01.00-k9 to v5.01.00.00.11.01-k10: - Wait for device online in reset_lun for 10s, for device to come online otherwise indicating an error condition. - Updated IPv6 support - Link Down -> Mark all devices missing This change will cut 20 seconds of failover times. Previously, the driver took no action on a Link Down, and waited for the I/O on a dead connection to timeout in the firmware before marking the DDB missing. - Code Clean up - remove "marker_needed" - Updated firmware ready timeout algorithm to prevent long delays and use jiffies to time out instead of counter. Also use msleep_interruptible instead of msleep. - Added srb reference count support Serialization between the error handler and recovery code. - Avoid relogin on device marked missing causing mailbox command (0x63) failure - Check for command completion while device and host reset Created variables to reference h, b, t, l, because if the IOCTL scsi passthru command completes within eh_device_reset, the cmd structure may no longer be valid. Fix for ER67742: hang while sg_reset. Also wait for hba online in device reset path - Do not retry login to CHAP auth failed targets Per RFC 3720, Login Response Status Code 0x02 should not be retried. Condensed connection error checking code to a single routine, and added check for status class 0x02. - Added support for abort task management command Handles SCSI aborts. - Add Async PDU support Added support for Asynchronous PDU IOCB - handle DDB removal via DPC mailbox command free device ddb (0x31) does not generate AEN, so we need to process it separately where we defer the removal of ddb to DPC. - Update data structure to use single ioctl module update scsi_qla_host to match with qisioctl struct. - ioctl initialization to interact with the application - Add support for ACB firmware features in the driver to notify the firmware that the driver supports ifcb size greater than 512B. - added active srb array implementation for effective srb processing within the io path. - v5.01.00.00.11.01-k10 Changed driver version for SLES11 SP1. Signed-off-by: Ravi Anand Acked-by: Hannes Reinecke --- drivers/scsi/qla4xxx/ql4_def.h | 120 +++++++- drivers/scsi/qla4xxx/ql4_fw.h | 91 ++++-- drivers/scsi/qla4xxx/ql4_glbl.h | 18 + drivers/scsi/qla4xxx/ql4_init.c | 349 +++++++++++++++++------ drivers/scsi/qla4xxx/ql4_inline.h | 71 ++++ drivers/scsi/qla4xxx/ql4_iocb.c | 21 + drivers/scsi/qla4xxx/ql4_isr.c | 53 +++ drivers/scsi/qla4xxx/ql4_mbx.c | 426 ++++++++++++++++++++++------ drivers/scsi/qla4xxx/ql4_os.c | 553 ++++++++++++++++++++++++++++++++++--- drivers/scsi/qla4xxx/ql4_version.h | 2 10 files changed, 1432 insertions(+), 272 deletions(-) --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -90,16 +90,17 @@ ***********************************/ #define MAX_HBAS 16 #define MAX_BUSES 1 -#define MAX_TARGETS (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) +#define MAX_TARGETS MAX_DEV_DB_ENTRIES #define MAX_LUNS 0xffff #define MAX_AEN_ENTRIES 256 /* should be > EXT_DEF_MAX_AEN_QUEUE */ -#define MAX_DDB_ENTRIES (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) +#define MAX_DDB_ENTRIES MAX_DEV_DB_ENTRIES #define MAX_PDU_ENTRIES 32 #define INVALID_ENTRY 0xFFFF #define MAX_CMDS_TO_RISC 1024 #define MAX_SRBS MAX_CMDS_TO_RISC #define MBOX_AEN_REG_COUNT 5 #define MAX_INIT_RETRIES 5 +#define LEGACY_IFCB_SIZE 0x200 /* * Buffer sizes @@ -114,6 +115,7 @@ */ #define MAC_ADDR_LEN 6 /* in bytes */ #define IP_ADDR_LEN 4 /* in bytes */ +#define IPv6_ADDR_LEN 16 /* IPv6 address size */ #define DRIVER_NAME "qla4xxx" #define MAX_LINKED_CMDS_PER_LUN 3 @@ -146,6 +148,7 @@ #define ISNS_DEREG_TOV 5 #define MAX_RESET_HA_RETRIES 2 +#define DEVICE_ONLINE_TOV 10 /* * SCSI Request Block structure (srb) that is placed @@ -220,7 +223,7 @@ struct ddb_entry { uint16_t os_target_id; /* Target ID */ uint16_t fw_ddb_index; /* DDB firmware index */ - uint8_t reserved[2]; + uint8_t options; uint32_t fw_ddb_device_state; /* F/W Device State -- see ql4_fw.h */ uint32_t CmdSn; @@ -245,10 +248,13 @@ struct ddb_entry { uint16_t port; uint32_t tpgt; - uint8_t ip_addr[ISCSI_IPADDR_SIZE]; + uint8_t ip_addr[IP_ADDR_LEN]; uint8_t iscsi_name[ISCSI_NAME_SIZE]; /* 72 x48 */ uint8_t iscsi_alias[0x20]; uint8_t isid[6]; + + struct in6_addr remote_ipv6_addr; + struct in6_addr link_local_ipv6_addr; }; /* @@ -260,6 +266,8 @@ struct ddb_entry { * commands */ #define DDB_STATE_MISSING 2 /* Device logged off, trying * to re-login */ +#define DDB_STATE_REMOVED 3 /* The fw ddb_entry is freed + * the session can be destroyed */ /* * DDB flags. @@ -269,16 +277,38 @@ struct ddb_entry { * logged it out */ #define DF_ISNS_DISCOVERED 2 /* Device was discovered via iSNS */ #define DF_FO_MASKED 3 +#define DF_REMOVE 4 /* FW DDB is destroyed */ #include "ql4_fw.h" #include "ql4_nvram.h" +/* shortcut to print ISID */ +#define ISID(addr) \ + ((unsigned char *)&addr)[5], \ + ((unsigned char *)&addr)[4], \ + ((unsigned char *)&addr)[3], \ + ((unsigned char *)&addr)[2], \ + ((unsigned char *)&addr)[1], \ + ((unsigned char *)&addr)[0] +#define ISID_FMT "0x%02x%02x%02x%02x%02x%02x" + /* * Linux Host Adapter structure */ struct scsi_qla_host { + struct klist_node node; + uint16_t instance; + uint16_t rsvd0; + + /* exported functions */ + int (*ql4cmd)(struct scsi_qla_host *ha, struct srb * srb); + int (*ql4mbx)(struct scsi_qla_host *ha, uint8_t inCount, + uint8_t outCount, uint32_t *mbx_cmd, uint32_t *mbx_sts); + /* Linux adapter configuration data */ + struct Scsi_Host *host; /* pointer to host data */ + uint32_t tot_ddbs; unsigned long flags; #define AF_ONLINE 0 /* 0x00000001 */ @@ -290,6 +320,7 @@ struct scsi_qla_host { #define AF_LINK_UP 8 /* 0x00000100 */ #define AF_IRQ_ATTACHED 10 /* 0x00000400 */ #define AF_DISABLE_ACB_COMPLETE 11 /* 0x00000800 */ +#define AF_OS_INDEX_VALID 12 /* 0x00001000 */ unsigned long dpc_flags; @@ -301,9 +332,9 @@ struct scsi_qla_host { #define DPC_ISNS_RESTART 7 /* 0x00000080 */ #define DPC_AEN 9 /* 0x00000200 */ #define DPC_GET_DHCP_IP_ADDR 15 /* 0x00008000 */ - - struct Scsi_Host *host; /* pointer to host data */ - uint32_t tot_ddbs; +#define DPC_REMOVE_DEVICE 17 /* 0x00020000 */ +#define DPC_LINK_CHANGED 18 /* 0x00040000 */ +#define DPC_ASYNC_MSG_PDU 19 /* 0x00080000 */ uint16_t iocb_cnt; @@ -320,14 +351,14 @@ struct scsi_qla_host { #define MIN_IOBASE_LEN 0x100 uint16_t req_q_count; - uint8_t marker_needed; - uint8_t rsvd1; + uint8_t rsvd1[2]; unsigned long host_no; /* NVRAM registers */ struct eeprom_data *nvram; spinlock_t hardware_lock ____cacheline_aligned; + spinlock_t list_lock; uint32_t eeprom_cmd_data; /* Counters for general statistics */ @@ -352,7 +383,6 @@ struct scsi_qla_host { uint32_t firmware_version[2]; uint32_t patch_number; uint32_t build_number; - uint32_t board_id; /* --- From Init_FW --- */ /* init_cb_t *init_cb; */ @@ -372,6 +402,7 @@ struct scsi_qla_host { /* --- From GetFwState --- */ uint32_t firmware_state; + uint32_t board_id; uint32_t addl_fw_state; /* Linux kernel thread */ @@ -394,6 +425,10 @@ struct scsi_qla_host { uint16_t free_srb_q_count; uint16_t num_srbs_allocated; + /* Active array */ + struct srb *active_srb_array[MAX_SRBS]; + uint16_t current_active_index; + /* DMA Memory Block */ void *queues; dma_addr_t queues_dma; @@ -422,12 +457,20 @@ struct scsi_qla_host { uint16_t aen_out; struct aen aen_q[MAX_AEN_ENTRIES]; - struct ql4_aen_log aen_log;/* tracks all aens */ + /* pdu variables */ + uint16_t pdu_count; /* Number of available aen_q entries */ + uint16_t pdu_in; /* Current indexes */ + uint16_t pdu_out; + uint16_t pdu_active; + struct pdu_entry *free_pdu_top; + struct pdu_entry *free_pdu_bottom; + struct pdu_entry pdu_queue[MAX_PDU_ENTRIES]; /* This mutex protects several threads to do mailbox commands * concurrently. */ struct mutex mbox_sem; + wait_queue_head_t mailbox_wait_queue; /* temporary mailbox status registers */ volatile uint8_t mbox_status_count; @@ -439,10 +482,63 @@ struct scsi_qla_host { /* Map ddb_list entry by FW ddb index */ struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES]; + struct ql4_aen_log aen_log;/* tracks all aens */ + void (*ql4getaenlog)(struct scsi_qla_host *ha, struct ql4_aen_log *aenl); + +#define QL_INDICES_PER_ENTRY 32 +#define QL_OSINDEX_ENTRIES (MAX_DDB_ENTRIES/QL_INDICES_PER_ENTRY) + volatile unsigned long os_map[QL_OSINDEX_ENTRIES]; + /* Saved srb for status continuation entry processing */ struct srb *status_srb; + + struct list_head async_iocb_list; + dma_addr_t gen_req_rsp_iocb_dma; + void *gen_req_rsp_iocb; + + /* IPv6 support info from InitFW */ + uint8_t acb_version; + uint8_t ipv4_addr_state; + uint16_t ipv4_options; + + uint32_t resvd2; + uint32_t ipv6_options; + uint32_t ipv6_addl_options; + uint8_t ipv6_link_local_state; + uint8_t ipv6_addr0_state; + uint8_t ipv6_addr1_state; + uint8_t ipv6_default_router_state; + struct in6_addr ipv6_link_local_addr; + struct in6_addr ipv6_addr0; + struct in6_addr ipv6_addr1; + struct in6_addr ipv6_default_router_addr; + + uint16_t ifcb_size; +}; + +static inline int is_ipv4_enabled(struct scsi_qla_host *ha) +{ + return ((ha->ipv4_options & IPOPT_IPv4_PROTOCOL_ENABLE) != 0); +} + +static inline int is_ipv6_enabled(struct scsi_qla_host *ha) +{ + return ((ha->ipv6_options & IPV6_OPT_IPV6_PROTOCOL_ENABLE) != 0); +} + +/* + * structure to buffer Async PDUs + */ +struct async_msg_pdu_iocb { + struct list_head list; + uint8_t iocb[0x40]; }; +typedef struct _ASYNC_PDU_SENSE { + uint16_t sense_len; /* 00-01 */ + uint8_t sense_data[0]; +} ASYNC_PDU_SENSE; + static inline int is_qla4010(struct scsi_qla_host *ha) { return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4010; @@ -511,7 +607,7 @@ static inline void __iomem* isp_port_err &ha->reg->u2.isp4022.p0.port_err_status); } -static inline void __iomem * isp_gp_out(struct scsi_qla_host *ha) +static inline void __iomem *isp_gp_out(struct scsi_qla_host *ha) { return (is_qla4010(ha) ? &ha->reg->u2.isp4010.gp_out : --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -215,6 +215,7 @@ union external_hw_config_reg { /* Mailbox command definitions */ #define MBOX_CMD_ABOUT_FW 0x0009 #define MBOX_CMD_PING 0x000B +#define MBOX_CMD_ABORT_TASK 0x0015 #define MBOX_CMD_LUN_RESET 0x0016 #define MBOX_CMD_TARGET_WARM_RESET 0x0017 #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E @@ -227,8 +228,8 @@ union external_hw_config_reg { #define MBOX_CMD_READ_FLASH 0x0026 #define MBOX_CMD_CLEAR_DATABASE_ENTRY 0x0031 #define MBOX_CMD_CONN_CLOSE_SESS_LOGOUT 0x0056 -#define LOGOUT_OPTION_CLOSE_SESSION 0x01 -#define LOGOUT_OPTION_RELOGIN 0x02 +#define LOGOUT_OPTION_CLOSE_SESSION 0x02 +#define LOGOUT_OPTION_RESET 0x04 #define MBOX_CMD_EXECUTE_IOCB_A64 0x005A #define MBOX_CMD_INITIALIZE_FIRMWARE 0x0060 #define MBOX_CMD_GET_INIT_FW_CTRL_BLOCK 0x0061 @@ -258,13 +259,15 @@ union external_hw_config_reg { /* Mailbox 1 */ #define FW_STATE_READY 0x0000 #define FW_STATE_CONFIG_WAIT 0x0001 -#define FW_STATE_WAIT_LOGIN 0x0002 +#define FW_STATE_WAIT_AUTOCONNECT 0x0002 #define FW_STATE_ERROR 0x0004 -#define FW_STATE_DHCP_IN_PROGRESS 0x0008 +#define FW_STATE_CONFIGURING_IP 0x0008 /* Mailbox 3 */ #define FW_ADDSTATE_OPTICAL_MEDIA 0x0001 -#define FW_ADDSTATE_DHCP_ENABLED 0x0002 +#define FW_ADDSTATE_DHCPv4_ENABLED 0x0002 +#define FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED 0x0004 +#define FW_ADDSTATE_DHCPv4_LEASE_EXPIRED 0x0008 #define FW_ADDSTATE_LINK_UP 0x0010 #define FW_ADDSTATE_ISNS_SVC_ENABLED 0x0020 #define MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS 0x006B @@ -320,6 +323,8 @@ union external_hw_config_reg { /* Host Adapter Initialization Control Block (from host) */ struct addr_ctrl_blk { uint8_t version; /* 00 */ +#define IFCB_VER_MIN 0x01 +#define IFCB_VER_MAX 0x02 uint8_t control; /* 01 */ uint16_t fw_options; /* 02-03 */ @@ -351,11 +356,15 @@ struct addr_ctrl_blk { uint16_t iscsi_opts; /* 30-31 */ uint16_t ipv4_tcp_opts; /* 32-33 */ uint16_t ipv4_ip_opts; /* 34-35 */ +#define IPOPT_IPv4_PROTOCOL_ENABLE 0x8000 uint16_t iscsi_max_pdu_size; /* 36-37 */ uint8_t ipv4_tos; /* 38 */ uint8_t ipv4_ttl; /* 39 */ uint8_t acb_version; /* 3A */ +#define ACB_NOT_SUPPORTED 0x00 +#define ACB_SUPPORTED 0x02 /* Capable of ACB Version 2 Features */ + uint8_t res2; /* 3B */ uint16_t def_timeout; /* 3C-3D */ uint16_t iscsi_fburst_len; /* 3E-3F */ @@ -397,16 +406,34 @@ struct addr_ctrl_blk { uint32_t cookie; /* 200-203 */ uint16_t ipv6_port; /* 204-205 */ uint16_t ipv6_opts; /* 206-207 */ +#define IPV6_OPT_IPV6_PROTOCOL_ENABLE 0x8000 + uint16_t ipv6_addtl_opts; /* 208-209 */ +#define IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE 0x0002 /* Pri ACB Only */ +#define IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR 0x0001 + uint16_t ipv6_tcp_opts; /* 20A-20B */ uint8_t ipv6_tcp_wsf; /* 20C */ uint16_t ipv6_flow_lbl; /* 20D-20F */ - uint8_t ipv6_gw_addr[16]; /* 210-21F */ + uint8_t ipv6_dflt_rtr_addr[16]; /* 210-21F */ uint16_t ipv6_vlan_tag; /* 220-221 */ uint8_t ipv6_lnk_lcl_addr_state;/* 222 */ uint8_t ipv6_addr0_state; /* 223 */ uint8_t ipv6_addr1_state; /* 224 */ - uint8_t ipv6_gw_state; /* 225 */ +#define IP_ADDRSTATE_UNCONFIGURED 0 +#define IP_ADDRSTATE_INVALID 1 +#define IP_ADDRSTATE_ACQUIRING 2 +#define IP_ADDRSTATE_TENTATIVE 3 +#define IP_ADDRSTATE_DEPRICATED 4 +#define IP_ADDRSTATE_PREFERRED 5 +#define IP_ADDRSTATE_DISABLING 6 + + uint8_t ipv6_dflt_rtr_state; /* 225 */ +#define IPV6_RTRSTATE_UNKNOWN 0 +#define IPV6_RTRSTATE_MANUAL 1 +#define IPV6_RTRSTATE_ADVERTISED 3 +#define IPV6_RTRSTATE_STALE 4 + uint8_t ipv6_traffic_class; /* 226 */ uint8_t ipv6_hop_limit; /* 227 */ uint8_t ipv6_if_id[8]; /* 228-22F */ @@ -424,7 +451,7 @@ struct addr_ctrl_blk { struct init_fw_ctrl_blk { struct addr_ctrl_blk pri; - struct addr_ctrl_blk sec; +/* struct addr_ctrl_blk sec;*/ }; /*************************************************************************/ @@ -433,6 +460,9 @@ struct dev_db_entry { uint16_t options; /* 00-01 */ #define DDB_OPT_DISC_SESSION 0x10 #define DDB_OPT_TARGET 0x02 /* device is a target */ +#define DDB_OPT_IPV6_DEVICE 0x100 +#define DDB_OPT_IPV6_NULL_LINK_LOCAL 0x800 /* post connection */ +#define DDB_OPT_IPV6_FW_DEFINED_LINK_LOCAL 0x800 /* pre connection */ uint16_t exec_throttle; /* 02-03 */ uint16_t exec_count; /* 04-05 */ @@ -468,7 +498,7 @@ struct dev_db_entry { * pointer to a string so we * don't have to reserve soooo * much RAM */ - uint8_t ipv6_addr[0x10];/* 1A0-1AF */ + uint8_t link_local_ipv6_addr[0x10]; /* 1A0-1AF */ uint8_t res5[0x10]; /* 1B0-1BF */ uint16_t ddb_link; /* 1C0-1C1 */ uint16_t chap_tbl_idx; /* 1C2-1C3 */ @@ -577,13 +607,14 @@ struct conn_event_log_entry { /* IOCB header structure */ struct qla4_header { uint8_t entryType; -#define ET_STATUS 0x03 -#define ET_MARKER 0x04 -#define ET_CONT_T1 0x0A -#define ET_STATUS_CONTINUATION 0x10 -#define ET_CMND_T3 0x19 -#define ET_PASSTHRU0 0x3A -#define ET_PASSTHRU_STATUS 0x3C +#define ET_STATUS 0x03 +#define ET_MARKER 0x04 +#define ET_CONT_T1 0x0A +#define ET_STATUS_CONTINUATION 0x10 +#define ET_CMND_T3 0x19 +#define ET_ASYNC_PDU 0x37 +#define ET_PASSTHRU0 0x3A +#define ET_PASSTHRU_STATUS 0x3C uint8_t entryStatus; uint8_t systemDefined; @@ -692,6 +723,18 @@ struct qla4_marker_entry { uint64_t reserved6; /* 38-3F */ }; +/* Asynchronous PDU IOCB structure */ +struct async_pdu_iocb { + struct qla4_header hdr; /* 00-02 */ + uint32_t async_pdu_handle; /* 03-06 */ + uint16_t target_id; /* 07-08 */ + uint16_t status; /* 09-0A */ +#define ASYNC_PDU_IOCB_STS_OK 0x01 + + uint32_t rsrvd; /* 0B-0F */ + uint8_t iscsi_pdu_hdr[48]; /* 10-3F */ +}; + /* Status entry structure*/ struct status_entry { struct qla4_header hdr; /* 00-03 */ @@ -734,6 +777,15 @@ struct status_entry { }; +struct pdu_entry { + uint8_t *Buff; + uint32_t BuffLen; + uint32_t SendBuffLen; + uint32_t RecvBuffLen; + struct pdu_entry *Next; + dma_addr_t DmaBuff; +}; + /* Status Continuation entry */ struct status_cont_entry { struct qla4_header hdr; /* 00-03 */ @@ -745,11 +797,9 @@ struct passthru0 { uint32_t handle; /* 04-07 */ uint16_t target; /* 08-09 */ uint16_t connectionID; /* 0A-0B */ -#define ISNS_DEFAULT_SERVER_CONN_ID ((uint16_t)0x8000) uint16_t controlFlags; /* 0C-0D */ -#define PT_FLAG_ETHERNET_FRAME 0x8000 -#define PT_FLAG_ISNS_PDU 0x8000 +#define PT_FLAG_ISCSI_PDU 0x1000 #define PT_FLAG_SEND_BUFFER 0x0200 #define PT_FLAG_WAIT_4_RESPONSE 0x0100 @@ -759,7 +809,8 @@ struct passthru0 { struct data_seg_a64 outDataSeg64; /* 10-1B */ uint32_t res1; /* 1C-1F */ struct data_seg_a64 inDataSeg64; /* 20-2B */ - uint8_t res2[20]; /* 2C-3F */ + uint8_t res2[16]; /* 2C-3F */ + uint32_t async_pdu_handle; }; struct passthru_status { --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -20,17 +20,21 @@ int qla4xxx_soft_reset(struct scsi_qla_h irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id); void qla4xxx_free_ddb_list(struct scsi_qla_host * ha); +void qla4xxx_free_ddb(struct scsi_qla_host *, struct ddb_entry *); void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); int qla4xxx_relogin_device(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry); +int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb); int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, int lun); int qla4xxx_reset_target(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry); int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, uint32_t offset, uint32_t len); +int qla4xxx_issue_iocb(struct scsi_qla_host *ha, uint32_t comp_offset, + dma_addr_t phys_addr); int qla4xxx_get_firmware_status(struct scsi_qla_host * ha); int qla4xxx_get_firmware_state(struct scsi_qla_host * ha); int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha); @@ -58,6 +62,10 @@ void qla4xxx_get_crash_record(struct scs struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha); int qla4xxx_add_sess(struct ddb_entry *); void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry); +int qla4xxx_conn_close_sess_logout(struct scsi_qla_host *ha, + uint16_t fw_ddb_index, + uint16_t connection_id, + uint16_t option); int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha); int qla4xxx_get_fw_version(struct scsi_qla_host * ha); void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, @@ -67,12 +75,18 @@ struct srb * qla4xxx_del_from_active_arr uint32_t index); void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); -int qla4xxx_process_ddb_changed(struct scsi_qla_host * ha, - uint32_t fw_ddb_index, uint32_t state); +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, + uint32_t state, uint32_t conn_error); void qla4xxx_dump_buffer(void *b, uint32_t size); int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod); +void sp_put(struct scsi_qla_host *ha, struct srb *sp); +int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err); +int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, + uint8_t outCount, uint32_t *mbx_cmd, + uint32_t *mbx_sts); + extern int ql4xextended_error_logging; extern int ql4xdiscoverywait; extern int ql4xdontresethba; --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -51,7 +51,7 @@ static void ql4xxx_set_mac_number(struct * This routine deallocates and unlinks the specified ddb_entry from the * adapter's **/ -static void qla4xxx_free_ddb(struct scsi_qla_host *ha, +void qla4xxx_free_ddb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry) { /* Remove device entry from list */ @@ -95,6 +95,7 @@ void qla4xxx_free_ddb_list(struct scsi_q **/ int qla4xxx_init_rings(struct scsi_qla_host *ha) { + uint16_t i; unsigned long flags = 0; /* Initialize request queue. */ @@ -123,6 +124,10 @@ int qla4xxx_init_rings(struct scsi_qla_h writel(0, &ha->reg->rsp_q_out); readl(&ha->reg->rsp_q_out); + /* Initialize active array */ + for (i = 0; i < MAX_SRBS; i++) + ha->active_srb_array[i] = NULL; + spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_SUCCESS; @@ -189,6 +194,71 @@ static int qla4xxx_init_local_data(struc return qla4xxx_get_firmware_status(ha); } +static uint8_t +qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha) +{ + uint8_t ipv4_wait = 0; + uint8_t ipv6_wait = 0; + int8_t ip_address[IPv6_ADDR_LEN] = {0} ; + + /* If both IPv4 & IPv6 are enabled, possibly only one + * IP address may be acquired, so check to see if we + * need to wait for another */ + if (is_ipv4_enabled(ha) && is_ipv6_enabled(ha)) { + if (((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_ENABLED) != 0) && + ((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED) == 0)) { + ipv4_wait = 1; + } + if (((ha->ipv6_addl_options & IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE) != 0) && + ((ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING))) { + + ipv6_wait = 1; + + if ((ha->ipv6_link_local_state == IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_PREFERRED)) { + DEBUG2(printk("scsi%ld: %s: " + "Preferred IP configured. Don't wait! \n", + ha->host_no, __func__)); + ipv6_wait = 0; + } + if (memcmp(&ha->ipv6_default_router_addr, ip_address, + IPv6_ADDR_LEN) == 0) { + DEBUG2(printk("scsi%ld: %s: " + "No Router configured. Don't wait! \n", + ha->host_no, __func__)); + ipv6_wait = 0; + } + if ((ha->ipv6_default_router_state == IPV6_RTRSTATE_MANUAL) && + (ha->ipv6_link_local_state == IP_ADDRSTATE_TENTATIVE) && + (memcmp(&ha->ipv6_link_local_addr, + &ha->ipv6_default_router_addr, 4) == 0)) { + DEBUG2(printk("scsi%ld: %s: LinkLocal Router & " + "IP configured. Don't wait! \n", + ha->host_no, __func__)); + ipv6_wait = 0; + } + } + if (ipv4_wait || ipv6_wait) { + DEBUG2(printk("scsi%ld: %s: Wait for additional IP(s) \"", + ha->host_no, __func__)); + if (ipv4_wait) + DEBUG2(printk("IPv4 ")); + if (ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6LinkLocal ")); + if (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr0 ")); + if (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr1 ")); + DEBUG2(printk("\"\n")); + } + } + + return (ipv4_wait|ipv6_wait); +} + static int qla4xxx_fw_ready(struct scsi_qla_host *ha) { uint32_t timeout_count; @@ -226,38 +296,75 @@ static int qla4xxx_fw_ready(struct scsi_ continue; } + if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk("scsi%ld: %s: fwstate:" + "AUTOCONNECT in progress\n", + ha->host_no, __func__)); + } + + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk("scsi%ld: %s: fwstate: CONFIGURING IP\n", + ha->host_no, __func__)); + /* + * Check for link state after 15 secs and if link is still DOWN then, + * cable is unplugged. Ignore "DHCP in Progress/CONFIGURING IP" bit + * to check if firmware is in ready state or not after 15 secs. + * This is applicable for both 2.x & 3.x firmware + */ + if (timeout_count <= (ADAPTER_INIT_TOV - 15)) { + if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) { + DEBUG2(printk("scsi%ld: %s: LINK UP " + "(Cable plugged)\n", + ha->host_no, __func__)); + } + else if (ha->firmware_state & + (FW_STATE_CONFIGURING_IP | FW_STATE_READY)) { + DEBUG2(printk("scsi%ld: %s: LINK DOWN " + "(Cable unplugged)\n", + ha->host_no, __func__)); + ha->firmware_state = FW_STATE_READY; + } + } + } + if (ha->firmware_state == FW_STATE_READY) { - DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); - /* The firmware is ready to process SCSI commands. */ - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: MEDIA TYPE - %s\n", - ha->host_no, - __func__, (ha->addl_fw_state & - FW_ADDSTATE_OPTICAL_MEDIA) - != 0 ? "OPTICAL" : "COPPER")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: DHCP STATE Enabled " - "%s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_DHCP_ENABLED) != 0 ? - "YES" : "NO")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: LINK %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_LINK_UP) != 0 ? - "UP" : "DOWN")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: iSNS Service " - "Started %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? - "YES" : "NO")); + /* If DHCP IP Addr is available, retrieve it now. */ + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + + if (!qla4xxx_wait_for_ip_config(ha) || timeout_count == 1) { + DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); + /* The firmware is ready to process SCSI commands. */ + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: MEDIA TYPE - %s\n", + ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_OPTICAL_MEDIA) + != 0 ? "OPTICAL" : "COPPER")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: DHCPv4 STATE Enabled " + "%s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_DHCPv4_ENABLED) != 0 ? + "YES" : "NO")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: LINK %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_LINK_UP) != 0 ? + "UP" : "DOWN")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: iSNS Service " + "Started %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? + "YES" : "NO")); - ready = 1; - break; + ready = 1; + break; + } } DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - " "seconds expired= %d\n", ha->host_no, __func__, @@ -272,15 +379,19 @@ static int qla4xxx_fw_ready(struct scsi_ msleep(1000); } /* end of for */ - if (timeout_count == 0) + if (timeout_count <= 0) DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", ha->host_no, __func__)); - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) { - DEBUG2(printk("scsi%ld: %s: FW is reporting its waiting to" - " grab an IP address from DHCP server\n", - ha->host_no, __func__)); + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but is reporting " + "it's waiting to configure an IP address\n", + ha->host_no, __func__)); ready = 1; + } else if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but " + "auto-discovery still in process\n", + ha->host_no, __func__)); } return ready; @@ -343,11 +454,11 @@ static struct ddb_entry* qla4xxx_get_ddb __func__, fw_ddb_index)); list_for_each_entry(ddb_entry, &ha->ddb_list, list) { if ((memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsi_name, - ISCSI_NAME_SIZE) == 0) && - (ddb_entry->tpgt == - le32_to_cpu(fw_ddb_entry->tgt_portal_grp)) && - (memcmp(ddb_entry->isid, fw_ddb_entry->isid, - sizeof(ddb_entry->isid)) == 0)) { + ISCSI_NAME_SIZE) == 0) && + (ddb_entry->tpgt == + le32_to_cpu(fw_ddb_entry->tgt_portal_grp)) && + (memcmp(ddb_entry->isid, fw_ddb_entry->isid, + sizeof(ddb_entry->isid)) == 0)) { found++; break; } @@ -387,6 +498,7 @@ static int qla4xxx_update_ddb_entry(stru struct dev_db_entry *fw_ddb_entry = NULL; dma_addr_t fw_ddb_entry_dma; int status = QLA_ERROR; + uint32_t conn_err; if (ddb_entry == NULL) { DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no, @@ -405,12 +517,13 @@ static int qla4xxx_update_ddb_entry(stru goto exit_update_ddb; } - if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + if ((qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, fw_ddb_entry_dma, NULL, NULL, - &ddb_entry->fw_ddb_device_state, NULL, + &ddb_entry->fw_ddb_device_state, &conn_err, &ddb_entry->tcp_source_port_num, - &ddb_entry->connection_id) == - QLA_ERROR) { + &ddb_entry->connection_id) == QLA_SUCCESS)) + status = QLA_SUCCESS; + else { DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for " "fw_ddb_index %d\n", ha->host_no, __func__, fw_ddb_index)); @@ -418,7 +531,6 @@ static int qla4xxx_update_ddb_entry(stru goto exit_update_ddb; } - status = QLA_SUCCESS; ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->tsid); ddb_entry->task_mgmt_timeout = le16_to_cpu(fw_ddb_entry->def_timeout); @@ -442,9 +554,26 @@ static int qla4xxx_update_ddb_entry(stru memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ip_addr[0], min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ip_addr))); - DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n", - ha->host_no, __func__, fw_ddb_index, - ddb_entry->fw_ddb_device_state, status)); + if (ddb_entry->options & DDB_OPT_IPV6_DEVICE) { + memcpy(&ddb_entry->remote_ipv6_addr, + fw_ddb_entry->ip_addr, + min(sizeof(ddb_entry->remote_ipv6_addr), + sizeof(fw_ddb_entry->ip_addr))); + memcpy(&ddb_entry->link_local_ipv6_addr, + fw_ddb_entry->link_local_ipv6_addr, + min(sizeof(ddb_entry->link_local_ipv6_addr), + sizeof(fw_ddb_entry->link_local_ipv6_addr))); + } + + DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " + "State %04x ConnErr %08x " + NIPQUAD_FMT ":%04d \"%s\"\n", + __func__, fw_ddb_index, + ddb_entry->os_target_id, + ddb_entry->fw_ddb_device_state, conn_err, + NIPQUAD(fw_ddb_entry->ip_addr), + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name)); exit_update_ddb: if (fw_ddb_entry) @@ -492,6 +621,39 @@ static struct ddb_entry * qla4xxx_alloc_ } /** + * qla4_is_relogin_allowed - Are we allowed to login? + * @ha: Pointer to host adapter structure. + * @conn_err: Last connection error associated with the ddb + * + * This routine tests the given connection error to determine if + * we are allowed to login. + **/ +int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err) +{ + uint32_t err_code, login_rsp_sts_class; + int relogin = 1; + + err_code = ((conn_err & 0x00ff0000) >> 16); + login_rsp_sts_class = ((conn_err & 0x0000ff00) >> 8); + if (err_code == 0x1c || err_code == 0x06) { + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, send target completed or access" + " denied failure\n", conn_err)); + relogin = 0; + } + if ((err_code == 0x08) && (login_rsp_sts_class == 0x02)) { + /* Login Response PDU returned an error. + Login Response Status in Error Code Detail + indicates login should not be retried.*/ + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, do not retry relogin\n", conn_err)); + relogin = 0; + } + + return relogin; +} + +/** * qla4xxx_configure_ddbs - builds driver ddb list * @ha: Pointer to host adapter structure. * @@ -505,15 +667,25 @@ static int qla4xxx_build_ddb_list(struct uint32_t fw_ddb_index = 0; uint32_t next_fw_ddb_index = 0; uint32_t ddb_state; - uint32_t conn_err, err_code; + uint32_t conn_err; struct ddb_entry *ddb_entry; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; uint32_t new_tgt; + uint32_t ipv6_device; + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (fw_ddb_entry == NULL) { + DEBUG2(dev_info(&ha->pdev->dev, "%s: DMA alloc failed\n", __func__)); + return QLA_ERROR; + } dev_info(&ha->pdev->dev, "Initializing DDBs ...\n"); for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index = next_fw_ddb_index) { /* First, let's see if a device exists here */ - if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, 0, NULL, &next_fw_ddb_index, &ddb_state, &conn_err, NULL, NULL) == QLA_ERROR) { @@ -533,13 +705,11 @@ static int qla4xxx_build_ddb_list(struct /* Try and login to device */ DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n", ha->host_no, __func__, fw_ddb_index)); - err_code = ((conn_err & 0x00ff0000) >> 16); - if (err_code == 0x1c || err_code == 0x06) { - DEBUG2(printk("scsi%ld: %s send target " - "completed " - "or access denied failure\n", - ha->host_no, __func__)); - } else { + ipv6_device = le16_to_cpu(fw_ddb_entry->options) & + DDB_OPT_IPV6_DEVICE; + if (qla4_is_relogin_allowed(ha, conn_err) && + ((!ipv6_device && *((uint32_t *)fw_ddb_entry->ip_addr)) + || ipv6_device)) { qla4xxx_set_ddb_entry(ha, fw_ddb_index, 0); if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, &next_fw_ddb_index, @@ -599,7 +769,6 @@ next_one: struct qla4_relog_scan { int halt_wait; uint32_t conn_err; - uint32_t err_code; uint32_t fw_ddb_index; uint32_t next_fw_ddb_index; uint32_t fw_ddb_device_state; @@ -609,18 +778,7 @@ static int qla4_test_rdy(struct scsi_qla { struct ddb_entry *ddb_entry; - /* - * Don't want to do a relogin if connection - * error is 0x1c. - */ - rs->err_code = ((rs->conn_err & 0x00ff0000) >> 16); - if (rs->err_code == 0x1c || rs->err_code == 0x06) { - DEBUG2(printk( - "scsi%ld: %s send target" - " completed or " - "access denied failure\n", - ha->host_no, __func__)); - } else { + if (qla4_is_relogin_allowed(ha, rs->conn_err)) { /* We either have a device that is in * the process of relogging in or a * device that is waiting to be @@ -908,7 +1066,7 @@ static void qla4x00_pci_config(struct sc static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) { int status = QLA_ERROR; - uint32_t max_wait_time; + unsigned long max_wait_time; unsigned long flags; uint32_t mbox_status; @@ -927,6 +1085,13 @@ static int qla4xxx_start_firmware_from_f ha->host_no, __func__)); spin_lock_irqsave(&ha->hardware_lock, flags); + /* + * Firmware must be informed that the driver supports + * ACB firmware features while starting firmware. + * If the firmware also supports these features it will + * be indicated in the IFCB offset 0x3A (acb_version). + */ + writel(ACB_SUPPORTED, &ha->reg->mailbox[6]); writel(jiffies, &ha->reg->mailbox[7]); if (is_qla4022(ha) | is_qla4032(ha)) writel(set_rmask(NVR_WRITE_ENABLE), @@ -940,7 +1105,10 @@ static int qla4xxx_start_firmware_from_f spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Wait for firmware to come UP. */ - max_wait_time = FIRMWARE_UP_TOV * 4; + DEBUG2(printk("scsi%ld: %s: Wait up to %d seconds for " + "boot firmware to complete... \n", + ha->host_no, __func__, FIRMWARE_UP_TOV)); + max_wait_time = jiffies + (FIRMWARE_UP_TOV * HZ); do { uint32_t ctrl_status; @@ -955,12 +1123,11 @@ static int qla4xxx_start_firmware_from_f break; DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to " - "complete... ctrl_sts=0x%x, remaining=%d\n", - ha->host_no, __func__, ctrl_status, - max_wait_time)); + "complete... ctrl_sts=0x%x\n", + ha->host_no, __func__, ctrl_status)); - msleep(250); - } while ((max_wait_time--)); + msleep_interruptible(250); + } while (!time_after_eq(jiffies, max_wait_time)); if (mbox_status == MBOX_STS_COMMAND_COMPLETE) { DEBUG(printk("scsi%ld: %s: Firmware has started\n", @@ -1141,6 +1308,7 @@ int qla4xxx_initialize_adapter(struct sc int status = QLA_ERROR; int8_t ip_address[IP_ADDR_LEN] = {0} ; + clear_bit(AF_ONLINE, &ha->flags); ha->eeprom_cmd_data = 0; qla4x00_pci_config(ha); @@ -1166,7 +1334,7 @@ int qla4xxx_initialize_adapter(struct sc * the ddb_list and wait for DHCP lease acquired aen to come in * followed by 0x8014 aen" to trigger the tgt discovery process. */ - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) goto exit_init_online; /* Skip device discovery if ip and subnet is zero */ @@ -1270,8 +1438,8 @@ static void qla4xxx_add_device_dynamical * * This routine processes a Decive Database Changed AEN Event. **/ -int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, - uint32_t fw_ddb_index, uint32_t state) +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, + uint32_t state, uint32_t conn_err) { struct ddb_entry * ddb_entry; uint32_t old_fw_ddb_device_state; @@ -1318,19 +1486,24 @@ int qla4xxx_process_ddb_changed(struct s * the device came back. */ } else { - /* Device went away, try to relogin. */ - /* Mark device missing */ - if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + /* Device went away, mark device missing */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) { + DEBUG2(dev_info(&ha->pdev->dev, "%s mark missing " + "ddb_entry 0x%p sess 0x%p conn 0x%p\n", + __func__, ddb_entry, + ddb_entry->sess, ddb_entry->conn)); qla4xxx_mark_device_missing(ha, ddb_entry); + } + /* * Relogin if device state changed to a not active state. - * However, do not relogin if this aen is a result of an IOCTL - * logout (DF_NO_RELOGIN) or if this is a discovered device. + * However, do not relogin if a RELOGIN is in process, or + * we are not allowed to relogin to this DDB. */ if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED && !test_bit(DF_RELOGIN, &ddb_entry->flags) && !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) && - !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) { + qla4_is_relogin_allowed(ha, conn_err)) { /* * This triggers a relogin. After the relogin_timer * expires, the relogin gets scheduled. We must wait a @@ -1338,7 +1511,7 @@ int qla4xxx_process_ddb_changed(struct s * with failed device_state or a logout response before * we can issue another relogin. */ - /* Firmware padds this timeout: (time2wait +1). + /* Firmware pads this timeout: (time2wait +1). * Driver retry to login should be longer than F/W. * Otherwise F/W will fail * set_ddb() mbx cmd with 0x4005 since it still --- a/drivers/scsi/qla4xxx/ql4_inline.h +++ b/drivers/scsi/qla4xxx/ql4_inline.h @@ -35,6 +35,34 @@ qla4xxx_lookup_ddb_by_fw_index(struct sc return ddb_entry; } +/* + * The MBOX_CMD_CLEAR_DATABASE_ENTRY (0x31) mailbox command does not + * result in an AEN, so we need to process it seperately. + */ +static inline void qla4xxx_check_for_clear_ddb(struct scsi_qla_host *ha, + uint32_t *mbox_cmd) +{ + uint32_t fw_ddb_index; + struct ddb_entry *ddb_entry = NULL; + + if (mbox_cmd[0] == MBOX_CMD_CLEAR_DATABASE_ENTRY) { + + fw_ddb_index = mbox_cmd[1]; + + if (fw_ddb_index < MAX_DDB_ENTRIES) + ddb_entry = ha->fw_ddb_index_map[fw_ddb_index]; + + if (ddb_entry) { + dev_info(&ha->pdev->dev, "%s: ddb[%d] os[%d] freed\n", + __func__, ddb_entry->fw_ddb_index, + ddb_entry->os_target_id); + set_bit(DF_REMOVE, &ddb_entry->flags); + set_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags); + queue_work(ha->dpc_thread, &ha->dpc_work); + } + } +} + static inline void __qla4xxx_enable_intrs(struct scsi_qla_host *ha) { @@ -82,3 +110,46 @@ qla4xxx_disable_intrs(struct scsi_qla_ho __qla4xxx_disable_intrs(ha); spin_unlock_irqrestore(&ha->hardware_lock, flags); } + +static inline void +qla4xxx_remove_device(struct scsi_qla_host *ha) +{ + struct ddb_entry *ddb_entry, *dtemp; + + if (test_and_clear_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags)) { + list_for_each_entry_safe(ddb_entry, dtemp, + &ha->ddb_list, list) { + if (test_and_clear_bit(DF_REMOVE, &ddb_entry->flags)) { + dev_info(&ha->pdev->dev, + "%s: ddb[%d] os[%d] - removed\n", + __func__, ddb_entry->fw_ddb_index, + ddb_entry->os_target_id); + qla4xxx_free_ddb(ha, ddb_entry); + } + } + } +} + +static void +ql4_get_aen_log(struct scsi_qla_host *ha, struct ql4_aen_log *aenl) +{ + if (aenl) { + memcpy(aenl, &ha->aen_log, sizeof (ha->aen_log)); + ha->aen_log.count = 0; + } +} + +static inline int +qla4xxx_ioctl_init(struct scsi_qla_host *ha) +{ + ha->ql4mbx = qla4xxx_mailbox_command; + ha->ql4cmd = qla4xxx_send_command_to_isp; + ha->ql4getaenlog = ql4_get_aen_log; + return 0; +} + +static inline void +qla4xxx_ioctl_exit(struct scsi_qla_host *ha) +{ + return; +} --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -209,6 +209,7 @@ int qla4xxx_send_command_to_isp(struct s int nseg; uint16_t tot_dsds; uint16_t req_cnt; + uint16_t i; unsigned long flags; uint32_t index; char tag[2]; @@ -221,7 +222,24 @@ int qla4xxx_send_command_to_isp(struct s /* Acquire hardware specific lock */ spin_lock_irqsave(&ha->hardware_lock, flags); - index = (uint32_t)cmd->request->tag; + //index = (uint32_t)cmd->request->tag; + index = ha->current_active_index; + for (i = 0; i < MAX_SRBS; i++) { + index++; + if (index == MAX_SRBS) + index = 1; + if (ha->active_srb_array[index] == 0) { + ha->current_active_index = index; + break; + } + } + + if (i >= MAX_SRBS) { + printk(KERN_INFO "scsi%ld: %s: NO more SRB entries used " + "iocbs=%d, \n reqs remaining=%d\n", ha->host_no, + __func__, ha->iocb_cnt, ha->req_q_count); + goto queuing_error; + } /* * Check to see if adapter is online before placing request on @@ -300,6 +318,7 @@ int qla4xxx_send_command_to_isp(struct s wmb(); srb->cmd->host_scribble = (unsigned char *)srb; + ha->active_srb_array[index] = srb; /* update counters */ srb->state = SRB_ACTIVE_STATE; --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -9,6 +9,7 @@ #include "ql4_glbl.h" #include "ql4_dbg.h" #include "ql4_inline.h" +#include /** * qla4xxx_copy_sense - copy sense data into cmd sense buffer @@ -97,7 +98,7 @@ qla4xxx_status_cont_entry(struct scsi_ql /* Place command on done queue. */ if (srb->req_sense_len == 0) { - qla4xxx_srb_compl(ha, srb); + sp_put(ha, srb); ha->status_srb = NULL; } } @@ -329,7 +330,7 @@ status_entry_exit: /* complete the request, if not waiting for status_continuation pkt */ srb->cc_stat = sts_entry->completionStatus; if (ha->status_srb == NULL) - qla4xxx_srb_compl(ha, srb); + sp_put(ha, srb); } /** @@ -344,6 +345,9 @@ static void qla4xxx_process_response_que uint32_t count = 0; struct srb *srb = NULL; struct status_entry *sts_entry; + struct async_pdu_iocb *apdu; + struct iscsi_hdr *pdu_hdr; + struct async_msg_pdu_iocb *apdu_iocb; /* Process all responses from response queue */ while ((ha->response_in = @@ -371,6 +375,34 @@ static void qla4xxx_process_response_que case ET_PASSTHRU_STATUS: break; + case ET_ASYNC_PDU: + apdu = (struct async_pdu_iocb *)sts_entry; + if (apdu->status != ASYNC_PDU_IOCB_STS_OK) + break; + + pdu_hdr = (struct iscsi_hdr *)apdu->iscsi_pdu_hdr; + if (pdu_hdr->hlength || pdu_hdr->dlength[0] || + pdu_hdr->dlength[1] || pdu_hdr->dlength[2]){ + apdu_iocb = kmalloc(sizeof(struct async_msg_pdu_iocb), + GFP_ATOMIC); + if (apdu_iocb) { + memcpy(apdu_iocb->iocb, apdu, + sizeof(struct async_pdu_iocb)); + list_add_tail(&apdu_iocb->list, + &ha->async_iocb_list); + DEBUG2(printk("scsi%ld:" + "%s: schedule async msg pdu\n", + ha->host_no, __func__)); + set_bit(DPC_ASYNC_MSG_PDU, + &ha->dpc_flags); + } else { + DEBUG2(printk("scsi%ld:" + "%s: unable to alloc ASYNC PDU\n", + ha->host_no, __func__)); + } + } + break; + case ET_STATUS_CONTINUATION: qla4xxx_status_cont_entry(ha, (struct status_cont_entry *) sts_entry); @@ -393,7 +425,7 @@ static void qla4xxx_process_response_que /* ETRY normally by sending it back with * DID_BUS_BUSY */ srb->cmd->result = DID_BUS_BUSY << 16; - qla4xxx_srb_compl(ha, srb); + sp_put(ha, srb); break; case ET_CONTINUE: @@ -498,15 +530,20 @@ static void qla4xxx_isr_decode_mailbox(s break; case MBOX_ASTS_LINK_UP: - DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", - ha->host_no, mbox_status)); set_bit(AF_LINK_UP, &ha->flags); + if (test_bit(AF_INIT_DONE, &ha->flags)) + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + + DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", + ha->host_no, mbox_status)); break; case MBOX_ASTS_LINK_DOWN: - DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", - ha->host_no, mbox_status)); clear_bit(AF_LINK_UP, &ha->flags); + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + + DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", + ha->host_no, mbox_status)); break; case MBOX_ASTS_HEARTBEAT: @@ -831,7 +868,7 @@ void qla4xxx_process_aen(struct scsi_qla qla4xxx_reinitialize_ddb_list(ha); } else if (mbox_sts[1] == 1) { /* Specific device. */ qla4xxx_process_ddb_changed(ha, mbox_sts[2], - mbox_sts[3]); + mbox_sts[3], mbox_sts[4]); } break; } --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -23,7 +23,7 @@ * If outCount is 0, this routine completes successfully WITHOUT waiting * for the mailbox command to complete. **/ -static int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, +int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, uint8_t outCount, uint32_t *mbx_cmd, uint32_t *mbx_sts) { @@ -164,6 +164,8 @@ static int qla4xxx_mailbox_command(struc spin_unlock_irqrestore(&ha->hardware_lock, flags); mbox_exit: + if (status == QLA_SUCCESS) + qla4xxx_check_for_clear_ddb(ha, mbx_cmd); mutex_lock(&ha->mbox_sem); clear_bit(AF_MBOX_COMMAND, &ha->flags); mutex_unlock(&ha->mbox_sem); @@ -173,107 +175,284 @@ mbox_exit: } /** + * qla4xxx_issue_iocb - issue mailbox iocb command + * @ha: adapter state pointer. + * @buffer: buffer pointer. + * @phys_addr: physical address of buffer. + * @size: size of buffer. + * + * Issues iocbs via mailbox commands. + * TARGET_QUEUE_LOCK must be released. + * ADAPTER_STATE_LOCK must be released. + **/ +int +qla4xxx_issue_iocb(struct scsi_qla_host *ha, uint32_t comp_offset, + dma_addr_t phys_addr) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_EXECUTE_IOCB_A64; + mbox_cmd[1] = comp_offset; + mbox_cmd[2] = LSDW(phys_addr); + mbox_cmd[3] = MSDW(phys_addr); + + status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]); + return status; +} + +int qla4xxx_conn_close_sess_logout(struct scsi_qla_host *ha, + uint16_t fw_ddb_index, + uint16_t connection_id, + uint16_t option) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_CONN_CLOSE_SESS_LOGOUT; + mbox_cmd[1] = fw_ddb_index; + mbox_cmd[2] = connection_id; + mbox_cmd[3] = LOGOUT_OPTION_RESET; + + if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 2, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_CONN_CLOSE_SESS_LOGOUT " + "option %04x failed sts %04X %04X", + ha->host_no, __func__, + option, mbox_sts[0], mbox_sts[1])); + if (mbox_sts[0] == 0x4005) + DEBUG2(printk("%s reason %04X\n", __func__, + mbox_sts[1])); + } + return QLA_SUCCESS; +} + +uint8_t +qla4xxx_set_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, uint32_t *mbox_sts, + dma_addr_t init_fw_cb_dma) +{ + memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); + memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); + mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; + mbox_cmd[1] = 0; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + if (ha->ifcb_size > LEGACY_IFCB_SIZE) { + mbox_cmd[4] = ha->ifcb_size; + mbox_cmd[5] = (IFCB_VER_MAX << 8) | IFCB_VER_MIN; + } + + if (qla4xxx_mailbox_command(ha, 6, 6, mbox_cmd, mbox_sts) + != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: " + "MBOX_CMD_INITIALIZE_FIRMWARE failed w/ status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + return (QLA_ERROR); + } + return (QLA_SUCCESS); +} + +uint8_t +qla4xxx_get_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, uint32_t *mbox_sts, + dma_addr_t init_fw_cb_dma) +{ + int i; + + memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); + memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); + mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + if (init_fw_cb_dma != 0 && ha->ifcb_size > LEGACY_IFCB_SIZE) + mbox_cmd[4] = ha->ifcb_size; + + if (qla4xxx_mailbox_command(ha, 5, 5, mbox_cmd, mbox_sts) + != QLA_SUCCESS) { + if (init_fw_cb_dma == 0 && + mbox_sts[0] == MBOX_STS_COMMAND_ERROR) + return (QLA_SUCCESS); + + DEBUG2(printk("scsi%ld: %s: " + "MBOX_CMD_GET_INIT_FW_CTRL_BLOCK failed w/ status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + + for (i = 0; i < MBOX_REG_COUNT; i++) { + DEBUG2(printk("mbox_cmd[%d] = %08x, " + "mbox_sts[%d] = %08x\n", + i, mbox_cmd[i], i, mbox_sts[i])); + } + + return (QLA_ERROR); + } + return (QLA_SUCCESS); +} + +void +qla4xxx_update_local_ip(struct scsi_qla_host *ha, + struct addr_ctrl_blk *init_fw_cb) +{ + /* Save IPv4 Address Info */ + memcpy(ha->ip_address, init_fw_cb->ipv4_addr, + min(sizeof(ha->ip_address), sizeof(init_fw_cb->ipv4_addr))); + memcpy(ha->subnet_mask, init_fw_cb->ipv4_subnet, + min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->ipv4_subnet))); + memcpy(ha->gateway, init_fw_cb->ipv4_gw_addr, + min(sizeof(ha->gateway), sizeof(init_fw_cb->ipv4_gw_addr))); + + if (is_ipv6_enabled(ha)) { + /* Save IPv6 Address */ + ha->ipv6_link_local_state = init_fw_cb->ipv6_lnk_lcl_addr_state; + ha->ipv6_addr0_state = init_fw_cb->ipv6_addr0_state; + ha->ipv6_addr1_state = init_fw_cb->ipv6_addr1_state; + ha->ipv6_default_router_state = init_fw_cb->ipv6_dflt_rtr_state; + ha->ipv6_link_local_addr.in6_u.u6_addr8[0] = 0xFE; + ha->ipv6_link_local_addr.in6_u.u6_addr8[1] = 0x80; + + memcpy(&ha->ipv6_link_local_addr.in6_u.u6_addr8[8], init_fw_cb->ipv6_if_id, + min(sizeof(ha->ipv6_link_local_addr)/2, sizeof(init_fw_cb->ipv6_if_id))); + memcpy(&ha->ipv6_addr0, init_fw_cb->ipv6_addr0, + min(sizeof(ha->ipv6_addr0), sizeof(init_fw_cb->ipv6_addr0))); + memcpy(&ha->ipv6_addr1, init_fw_cb->ipv6_addr1, + min(sizeof(ha->ipv6_addr1), sizeof(init_fw_cb->ipv6_addr1))); + memcpy(&ha->ipv6_default_router_addr, init_fw_cb->ipv6_dflt_rtr_addr, + min(sizeof(ha->ipv6_default_router_addr), sizeof(init_fw_cb->ipv6_dflt_rtr_addr))); + } +} + +uint8_t +qla4xxx_update_local_ifcb(struct scsi_qla_host *ha, + uint32_t *mbox_cmd, + uint32_t *mbox_sts, + struct addr_ctrl_blk *init_fw_cb, + dma_addr_t init_fw_cb_dma) +{ + if (qla4xxx_get_ifcb(ha, mbox_cmd, mbox_sts, init_fw_cb_dma) + != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", + ha->host_no, __func__)); + return (QLA_ERROR); + } + + DEBUG2(qla4xxx_dump_buffer(init_fw_cb, sizeof(struct addr_ctrl_blk))); + + /* Save some info in adapter structure. */ + ha->acb_version = init_fw_cb->acb_version; + ha->firmware_options = le16_to_cpu(init_fw_cb->fw_options); + ha->tcp_options = le16_to_cpu(init_fw_cb->ipv4_tcp_opts); + ha->ipv4_options = le16_to_cpu(init_fw_cb->ipv4_ip_opts); + ha->ipv4_addr_state = le16_to_cpu(init_fw_cb->ipv4_addr_state); + ha->heartbeat_interval = init_fw_cb->hb_interval; + memcpy(ha->name_string, init_fw_cb->iscsi_name, + min(sizeof(ha->name_string), + sizeof(init_fw_cb->iscsi_name))); + /*memcpy(ha->alias, init_fw_cb->Alias, + min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ + + /* Save Command Line Paramater info */ + ha->port_down_retry_count = le16_to_cpu(init_fw_cb->conn_ka_timeout); + ha->discovery_wait = ql4xdiscoverywait; + + if (ha->acb_version == ACB_SUPPORTED) { + ha->ipv6_options = init_fw_cb->ipv6_opts; + ha->ipv6_addl_options = init_fw_cb->ipv6_addtl_opts; + } + qla4xxx_update_local_ip(ha, init_fw_cb); + + return (QLA_SUCCESS); +} + +/** * qla4xxx_initialize_fw_cb - initializes firmware control block. * @ha: Pointer to host adapter structure. **/ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) { - struct init_fw_ctrl_blk *init_fw_cb; + struct addr_ctrl_blk *init_fw_cb; dma_addr_t init_fw_cb_dma; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; int status = QLA_ERROR; + /* Default to Legacy IFCB Size */ + ha->ifcb_size = LEGACY_IFCB_SIZE; + init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), &init_fw_cb_dma, GFP_KERNEL); if (init_fw_cb == NULL) { DEBUG2(printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, __func__)); return 10; } - memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); + memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); /* Get Initialize Firmware Control Block. */ memset(&mbox_cmd, 0, sizeof(mbox_cmd)); memset(&mbox_sts, 0, sizeof(mbox_sts)); - mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); - - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != - QLA_SUCCESS) { + /* + * Determine if larger IFCB is supported + */ + if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) + != QLA_SUCCESS) { dma_free_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), init_fw_cb, init_fw_cb_dma); - return status; + goto exit_init_fw_cb; + } + + if (mbox_sts[0] == MBOX_STS_COMMAND_ERROR && + mbox_sts[4] > LEGACY_IFCB_SIZE) { + /* Supports larger ifcb size */ + ha->ifcb_size = mbox_sts[4]; } /* Initialize request and response queues. */ qla4xxx_init_rings(ha); /* Fill in the request and response queue information. */ - init_fw_cb->pri.rqq_consumer_idx = cpu_to_le16(ha->request_out); - init_fw_cb->pri.compq_producer_idx = cpu_to_le16(ha->response_in); - init_fw_cb->pri.rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); - init_fw_cb->pri.compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); - init_fw_cb->pri.rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); - init_fw_cb->pri.rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); - init_fw_cb->pri.compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); - init_fw_cb->pri.compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); - init_fw_cb->pri.shdwreg_addr_lo = - cpu_to_le32(LSDW(ha->shadow_regs_dma)); - init_fw_cb->pri.shdwreg_addr_hi = - cpu_to_le32(MSDW(ha->shadow_regs_dma)); + init_fw_cb->rqq_consumer_idx = cpu_to_le16(ha->request_out); + init_fw_cb->compq_producer_idx = cpu_to_le16(ha->response_in); + init_fw_cb->rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); + init_fw_cb->compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); + init_fw_cb->rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); + init_fw_cb->rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); + init_fw_cb->compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); + init_fw_cb->compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); + init_fw_cb->shdwreg_addr_lo = cpu_to_le32(LSDW(ha->shadow_regs_dma)); + init_fw_cb->shdwreg_addr_hi = cpu_to_le32(MSDW(ha->shadow_regs_dma)); /* Set up required options. */ - init_fw_cb->pri.fw_options |= + init_fw_cb->fw_options |= __constant_cpu_to_le16(FWOPT_SESSION_MODE | FWOPT_INITIATOR_MODE); - init_fw_cb->pri.fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); - - /* Save some info in adapter structure. */ - ha->firmware_options = le16_to_cpu(init_fw_cb->pri.fw_options); - ha->tcp_options = le16_to_cpu(init_fw_cb->pri.ipv4_tcp_opts); - ha->heartbeat_interval = init_fw_cb->pri.hb_interval; - memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, - min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); - memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, - min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); - memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, - min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); - memcpy(ha->name_string, init_fw_cb->pri.iscsi_name, - min(sizeof(ha->name_string), - sizeof(init_fw_cb->pri.iscsi_name))); - /*memcpy(ha->alias, init_fw_cb->Alias, - min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ - - /* Save Command Line Paramater info */ - ha->port_down_retry_count = le16_to_cpu(init_fw_cb->pri.conn_ka_timeout); - ha->discovery_wait = ql4xdiscoverywait; - - /* Send Initialize Firmware Control Block. */ - memset(&mbox_cmd, 0, sizeof(mbox_cmd)); - memset(&mbox_sts, 0, sizeof(mbox_sts)); + init_fw_cb->fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); - mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; - mbox_cmd[1] = 0; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); + if (qla4xxx_set_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) + != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to set init_fw_ctrl_blk\n", + ha->host_no, __func__)); + goto exit_init_fw_cb; + } - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) == - QLA_SUCCESS) - status = QLA_SUCCESS; - else { - DEBUG2(printk("scsi%ld: %s: MBOX_CMD_INITIALIZE_FIRMWARE " - "failed w/ status %04X\n", ha->host_no, __func__, - mbox_sts[0])); + if (qla4xxx_update_local_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], + init_fw_cb, init_fw_cb_dma) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to update local ifcb\n", + ha->host_no, __func__)); + goto exit_init_fw_cb; } - dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), - init_fw_cb, init_fw_cb_dma); + status = QLA_SUCCESS; + +exit_init_fw_cb: + dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); return status; } @@ -284,13 +463,13 @@ int qla4xxx_initialize_fw_cb(struct scsi **/ int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha) { - struct init_fw_ctrl_blk *init_fw_cb; + struct addr_ctrl_blk *init_fw_cb; dma_addr_t init_fw_cb_dma; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), &init_fw_cb_dma, GFP_KERNEL); if (init_fw_cb == NULL) { printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, @@ -299,35 +478,21 @@ int qla4xxx_get_dhcp_ip_address(struct s } /* Get Initialize Firmware Control Block. */ - memset(&mbox_cmd, 0, sizeof(mbox_cmd)); - memset(&mbox_sts, 0, sizeof(mbox_sts)); - - memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); - mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); - - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != - QLA_SUCCESS) { + memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); + if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) + != QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", ha->host_no, __func__)); dma_free_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), init_fw_cb, init_fw_cb_dma); return QLA_ERROR; } /* Save IP Address. */ - memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, - min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); - memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, - min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); - memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, - min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); - - dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), - init_fw_cb, init_fw_cb_dma); + qla4xxx_update_local_ip(ha, init_fw_cb); + dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); return QLA_SUCCESS; } @@ -441,14 +606,14 @@ int qla4xxx_get_fwddb_entry(struct scsi_ goto exit_get_fwddb; } if (fw_ddb_entry) { - dev_info(&ha->pdev->dev, "DDB[%d] MB0 %04x Tot %d Next %d " - "State %04x ConnErr %08x %d.%d.%d.%d:%04d \"%s\"\n", - fw_ddb_index, mbox_sts[0], mbox_sts[2], mbox_sts[3], - mbox_sts[4], mbox_sts[5], fw_ddb_entry->ip_addr[0], - fw_ddb_entry->ip_addr[1], fw_ddb_entry->ip_addr[2], - fw_ddb_entry->ip_addr[3], - le16_to_cpu(fw_ddb_entry->port), - fw_ddb_entry->iscsi_name); + dev_info(&ha->pdev->dev, "%s: DDB[%d] MB0 %04x Tot %d " + "Next %d State %04x ConnErr %08x " NIPQUAD_FMT + ":%04d \"%s\"\n", __func__, fw_ddb_index, + mbox_sts[0], mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5], + NIPQUAD(fw_ddb_entry->ip_addr), + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name); } if (num_valid_ddb_entries) *num_valid_ddb_entries = mbox_sts[2]; @@ -664,6 +829,53 @@ exit_get_event_log: } /** + * qla4xxx_abort_task - issues Abort Task + * @ha: Pointer to host adapter structure. + * @srb: Pointer to srb entry + * + * This routine performs a LUN RESET on the specified target/lun. + * The caller must ensure that the ddb_entry and lun_entry pointers + * are valid before calling this routine. + **/ +int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + struct scsi_cmnd *cmd = srb->cmd; + int status = QLA_SUCCESS; + + DEBUG2(printk("scsi%ld:%d:%d:%d: abort task issued\n", ha->host_no, + cmd->device->channel, cmd->device->id, cmd->device->lun)); + + /* + * Send abort task command to ISP, so that the ISP will return + * request with ABORT status + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_ABORT_TASK; + mbox_cmd[1] = srb->fw_ddb_index; + mbox_cmd[2] = (unsigned long)(unsigned char *)cmd->host_scribble; + mbox_cmd[5] = 0x01; /* Immediate Command Enable */ + + qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0], &mbox_sts[0]); + if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) { + status = QLA_ERROR; + + DEBUG2(printk("scsi%ld:%d:%d:%d: abort task FAILED: ", ha->host_no, + cmd->device->channel, cmd->device->id, cmd->device->lun)); + DEBUG2(printk("mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n", + mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3], + mbox_sts[4])); + } + + return status; +} + + + +/** * qla4xxx_reset_lun - issues LUN Reset * @ha: Pointer to host adapter structure. * @db_entry: Pointer to device database entry @@ -679,11 +891,33 @@ int qla4xxx_reset_lun(struct scsi_qla_ho uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; int status = QLA_SUCCESS; + unsigned long wait_online; DEBUG2(printk("scsi%ld:%d:%d: lun reset issued\n", ha->host_no, ddb_entry->os_target_id, lun)); /* + * If device is not online wait for 10 sec for device to come online. + * else return error + */ + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + wait_online = jiffies + (DEVICE_ONLINE_TOV * HZ); + while (time_before(jiffies, wait_online)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + break; + } + + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + DEBUG2(printk("scsi%ld: %s: Unable to reset lun." + "Device is not online.\n", ha->host_no + , __func__)); + return QLA_ERROR; + } + } + + /* * Send lun reset command to ISP, so that the ISP will return all * outstanding requests with RESET status */ --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include "ql4_def.h" #include "ql4_version.h" @@ -18,7 +20,18 @@ /* * Driver version */ -static char qla4xxx_version_str[40]; +char qla4xxx_version_str[40]; +EXPORT_SYMBOL_GPL(qla4xxx_version_str); + +/* + * List of host adapters + */ +struct klist qla4xxx_hostlist; + +struct klist *qla4xxx_hostlist_ptr = &qla4xxx_hostlist; +EXPORT_SYMBOL_GPL(qla4xxx_hostlist_ptr); + +static atomic_t qla4xxx_hba_count; /* * SRB allocation cache @@ -73,6 +86,7 @@ static enum blk_eh_timer_return qla4xxx_ */ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)); +static int qla4xxx_eh_abort(struct scsi_cmnd *cmd); static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); @@ -87,6 +101,7 @@ static struct scsi_host_template qla4xxx .proc_name = DRIVER_NAME, .queuecommand = qla4xxx_queuecommand, + .eh_abort_handler = qla4xxx_eh_abort, .eh_device_reset_handler = qla4xxx_eh_device_reset, .eh_target_reset_handler = qla4xxx_eh_target_reset, .eh_host_reset_handler = qla4xxx_eh_host_reset, @@ -95,6 +110,7 @@ static struct scsi_host_template qla4xxx .slave_configure = qla4xxx_slave_configure, .slave_alloc = qla4xxx_slave_alloc, .slave_destroy = qla4xxx_slave_destroy, + .target_destroy = NULL, .scan_finished = iscsi_scan_finished, .scan_start = qla4xxx_scan_start, @@ -272,10 +288,8 @@ void qla4xxx_destroy_sess(struct ddb_ent if (!ddb_entry->sess) return; - if (ddb_entry->conn) { - atomic_set(&ddb_entry->state, DDB_STATE_DEAD); + if (ddb_entry->conn) iscsi_remove_session(ddb_entry->sess); - } iscsi_free_session(ddb_entry->sess); } @@ -370,8 +384,19 @@ void qla4xxx_mark_device_missing(struct ddb_entry->fw_ddb_index)); iscsi_block_session(ddb_entry->sess); iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); + set_bit(DF_NO_RELOGIN, &ddb_entry->flags); } +/*** + * qla4xxx_get_new_srb - Allocate memory for a local srb. + * @ha: Pointer to host adapter structure. + * @ddb_entry: Pointer to device database entry + * @cmd: Pointer to Linux's SCSI command structure + * @done: Pointer to Linux's SCSI mid-layer done function + * + * NOTE: Sets te ref_count for non-NULL srb to one, + * and initializes some fields. + **/ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry, struct scsi_cmnd *cmd, @@ -417,6 +442,33 @@ void qla4xxx_srb_compl(struct scsi_qla_h } /** + * sp_put - Decrement reference count and call callback. + * @ha: Pointer to host adapter structure. + * @sp: Pointer to srb structure + **/ +void sp_put(struct scsi_qla_host *ha, struct srb *sp) +{ + if (atomic_read(&sp->ref_count) == 0) { + DEBUG2(printk("%s: SP->ref_count ZERO\n", __func__)); + DEBUG2(BUG()); + return; + } + if (!atomic_dec_and_test(&sp->ref_count)) { + return; + } + qla4xxx_srb_compl(ha, sp); +} + +/** + * sp_get - Increment reference count of the specified sp. + * @sp: Pointer to srb structure + **/ +void sp_get(struct srb *sp) +{ + atomic_inc(&sp->ref_count); +} + +/** * qla4xxx_queuecommand - scsi layer issues scsi command to driver. * @cmd: Pointer to Linux's SCSI command structure * @done_fn: Function that the driver calls to notify the SCSI mid-layer @@ -451,7 +503,7 @@ static int qla4xxx_queuecommand(struct s } if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { - if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) { + if ((atomic_read(&ddb_entry->state) == DDB_STATE_DEAD)) { cmd->result = DID_NO_CONNECT << 16; goto qc_fail_command; } @@ -498,10 +550,24 @@ qc_fail_command: **/ static void qla4xxx_mem_free(struct scsi_qla_host *ha) { + struct list_head *ptr; + struct async_msg_pdu_iocb *apdu_iocb; + if (ha->queues) dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues, ha->queues_dma); + if (ha->gen_req_rsp_iocb) + dma_free_coherent(&ha->pdev->dev, PAGE_SIZE, + ha->gen_req_rsp_iocb, ha->gen_req_rsp_iocb_dma); + + while (!list_empty(&ha->async_iocb_list)) { + ptr = ha->async_iocb_list.next; + apdu_iocb = list_entry(ptr, struct async_msg_pdu_iocb, list); + list_del_init(&apdu_iocb->list); + kfree(apdu_iocb); + } + ha->queues_len = 0; ha->queues = NULL; ha->queues_dma = 0; @@ -587,6 +653,15 @@ static int qla4xxx_mem_alloc(struct scsi goto mem_alloc_error_exit; } + ha->gen_req_rsp_iocb = dma_alloc_coherent(&ha->pdev->dev, PAGE_SIZE, + &ha->gen_req_rsp_iocb_dma, GFP_KERNEL); + if (ha->gen_req_rsp_iocb == NULL) { + dev_warn(&ha->pdev->dev, + "Memory Allocation failed - gen_req_rsp_iocb.\n"); + + goto mem_alloc_error_exit; + } + return QLA_SUCCESS; mem_alloc_error_exit: @@ -605,6 +680,24 @@ static void qla4xxx_timer(struct scsi_ql /* Search for relogin's to time-out and port down retry. */ list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { + /* First check to see if the device has exhausted the + * port down retry count */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_MISSING) { + if (atomic_read(&ddb_entry->port_down_timer) == 0) + continue; + + if (atomic_dec_and_test(&ddb_entry->port_down_timer)) { + DEBUG2(printk("scsi%ld: %s: index [%d] " + "port down retry count of (%d) secs " + "exhausted.\n", + ha->host_no, __func__, + ddb_entry->fw_ddb_index, + ha->port_down_retry_count);) + + atomic_set(&ddb_entry->state, DDB_STATE_DEAD); + start_dpc++; + } + } /* Count down time between sending relogins */ if (adapter_up(ha) && !test_bit(DF_RELOGIN, &ddb_entry->flags) && @@ -639,7 +732,8 @@ static void qla4xxx_timer(struct scsi_ql if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE && ddb_entry->fw_ddb_device_state == - DDB_DS_SESSION_FAILED) { + DDB_DS_SESSION_FAILED && + !test_bit(DF_NO_RELOGIN, &ddb_entry->flags)) { /* Reset retry relogin timer */ atomic_inc(&ddb_entry->relogin_retry_count); DEBUG2(printk("scsi%ld: index[%d] relogin" @@ -684,6 +778,9 @@ static void qla4xxx_timer(struct scsi_ql test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) || + test_bit(DPC_REMOVE_DEVICE, &ha->dpc_flags) || + test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) || + test_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags) || test_bit(DPC_AEN, &ha->dpc_flags)) && ha->dpc_thread) { DEBUG2(printk("scsi%ld: %s: scheduling dpc routine" @@ -710,7 +807,6 @@ static int qla4xxx_cmd_wait(struct scsi_ uint32_t index = 0; int stat = QLA_SUCCESS; unsigned long flags; - struct scsi_cmnd *cmd; int wait_cnt = WAIT_CMD_TOV; /* * Initialized for 30 seconds as we * expect all commands to retuned @@ -720,9 +816,8 @@ static int qla4xxx_cmd_wait(struct scsi_ while (wait_cnt) { spin_lock_irqsave(&ha->hardware_lock, flags); /* Find a command that hasn't completed. */ - for (index = 0; index < ha->host->can_queue; index++) { - cmd = scsi_host_find_tag(ha->host, index); - if (cmd != NULL) + for (index = 1; index < MAX_SRBS; index++) { + if (ha->active_srb_array[index] != NULL) break; } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -881,11 +976,11 @@ static void qla4xxx_flush_active_srbs(st unsigned long flags; spin_lock_irqsave(&ha->hardware_lock, flags); - for (i = 0; i < ha->host->can_queue; i++) { - srb = qla4xxx_del_from_active_array(ha, i); - if (srb != NULL) { + for (i = 1; i < MAX_SRBS; i++) { + if ((srb = ha->active_srb_array[i]) != NULL) { + qla4xxx_del_from_active_array(ha, i); srb->cmd->result = DID_RESET << 16; - qla4xxx_srb_compl(ha, srb); + sp_put(ha, srb); } } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -1000,6 +1095,134 @@ static int qla4xxx_recover_adapter(struc return status; } +/* + * qla4xxx_async_iocbs - processes ASYNC PDU IOCBS, if they are greater in + * length than 48 bytes (i.e., more than just the iscsi header). Used for + * unsolicited pdus received from target. + */ +static void qla4xxx_async_iocbs(struct scsi_qla_host *ha, + struct async_msg_pdu_iocb *amsg_pdu_iocb) +{ + struct iscsi_hdr *hdr; + struct async_pdu_iocb *apdu; + uint32_t len; + void *buf_addr; + dma_addr_t buf_addr_dma; + uint32_t offset; + struct passthru0 *pthru0_iocb; + struct ddb_entry *ddb_entry = NULL; + ASYNC_PDU_SENSE *pdu_sense; + + uint8_t using_prealloc = 1; + uint8_t async_event_type; + + apdu = (struct async_pdu_iocb *)amsg_pdu_iocb->iocb; + hdr = (struct iscsi_hdr *)apdu->iscsi_pdu_hdr; + len = hdr->hlength + hdr->dlength[2] + + (hdr->dlength[1]<<8) + (hdr->dlength[0]<<16); + + offset = sizeof(struct passthru0) + sizeof(struct passthru_status); + if (len <= (PAGE_SIZE - offset)) { + buf_addr_dma = ha->gen_req_rsp_iocb_dma + offset; + buf_addr = (uint8_t *)ha->gen_req_rsp_iocb + offset; + } else { + using_prealloc = 0; + buf_addr = dma_alloc_coherent(&ha->pdev->dev, len, + &buf_addr_dma, GFP_KERNEL); + if (!buf_addr) { + dev_info(&ha->pdev->dev, + "%s: dma_alloc_coherent failed\n", __func__); + return; + } + } + /* Create the pass-thru0 iocb */ + pthru0_iocb = ha->gen_req_rsp_iocb; + memset(pthru0_iocb, 0, offset); + pthru0_iocb->hdr.entryType = ET_PASSTHRU0; + pthru0_iocb->hdr.entryCount = 1; + pthru0_iocb->target = cpu_to_le16(apdu->target_id); + pthru0_iocb->controlFlags = + cpu_to_le16(PT_FLAG_ISCSI_PDU | PT_FLAG_WAIT_4_RESPONSE); + pthru0_iocb->timeout = cpu_to_le16(PT_DEFAULT_TIMEOUT); + pthru0_iocb->inDataSeg64.base.addrHigh = + cpu_to_le32(MSDW(buf_addr_dma)); + pthru0_iocb->inDataSeg64.base.addrLow = + cpu_to_le32(LSDW(buf_addr_dma)); + pthru0_iocb->inDataSeg64.count = cpu_to_le32(len); + pthru0_iocb->async_pdu_handle = cpu_to_le32(apdu->async_pdu_handle); + + dev_info(&ha->pdev->dev, + "%s: qla4xxx_issue_iocb\n", __func__); + + if (qla4xxx_issue_iocb(ha, sizeof(struct passthru0), + ha->gen_req_rsp_iocb_dma) != QLA_SUCCESS) { + dev_info(&ha->pdev->dev, + "%s: qla4xxx_issue_iocb failed\n", __func__); + goto exit_async_pdu_iocb; + } + + async_event_type = ((struct iscsi_async *)hdr)->async_event; + pdu_sense = (ASYNC_PDU_SENSE *)buf_addr; + + switch (async_event_type) { + case ISCSI_ASYNC_MSG_SCSI_EVENT: + dev_info(&ha->pdev->dev, + "%s: async msg event 0x%x processed\n" + , __func__, async_event_type); + + qla4xxx_dump_buffer(buf_addr, len); + + if (pdu_sense->sense_data[12] == 0x3F) { + if (pdu_sense->sense_data[13] == 0x0E) { + /* reported luns data has changed */ + uint16_t fw_index = apdu->target_id; + + ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_index); + if (ddb_entry == NULL) { + dev_info(&ha->pdev->dev, + "%s: No DDB entry for index [%d]\n" + , __func__, fw_index); + goto exit_async_pdu_iocb; + } + if (ddb_entry->fw_ddb_device_state != DDB_DS_SESSION_ACTIVE) { + dev_info(&ha->pdev->dev, + "scsi%ld: %s: No Active Session for index [%d]\n", + ha->host_no, __func__, fw_index); + goto exit_async_pdu_iocb; + } + + /* report new lun to kernel */ + scsi_scan_target(&ddb_entry->sess->dev, 0, + ddb_entry->sess->target_id, + SCAN_WILD_CARD, 0); + } + } + + break; + case ISCSI_ASYNC_MSG_REQUEST_LOGOUT: + case ISCSI_ASYNC_MSG_DROPPING_CONNECTION: + case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS: + case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION: + dev_info(&ha->pdev->dev, + "%s: async msg event 0x%x processed\n" + , __func__, async_event_type); + qla4xxx_conn_close_sess_logout(ha, apdu->target_id, 0, 0); + break; + default: + dev_info(&ha->pdev->dev, + "%s: async msg event 0x%x not processed\n", + __func__, async_event_type); + break; + }; + + exit_async_pdu_iocb: + if (!using_prealloc) + dma_free_coherent(&ha->pdev->dev, len, + buf_addr, buf_addr_dma); + + return; +} + /** * qla4xxx_do_dpc - dpc routine * @data: in our case pointer to adapter structure @@ -1016,6 +1239,7 @@ static void qla4xxx_do_dpc(struct work_s struct scsi_qla_host *ha = container_of(work, struct scsi_qla_host, dpc_work); struct ddb_entry *ddb_entry, *dtemp; + struct async_msg_pdu_iocb *apdu_iocb, *apdu_iocb_tmp; int status = QLA_ERROR; DEBUG2(printk("scsi%ld: %s: DPC handler waking up." @@ -1068,13 +1292,16 @@ static void qla4xxx_do_dpc(struct work_s if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) qla4xxx_get_dhcp_ip_address(ha); + qla4xxx_remove_device(ha); + /* ---- relogin device? --- */ if (adapter_up(ha) && test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { - if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) && - atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) + if ((test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags)) && + (!test_bit(DF_NO_RELOGIN, &ddb_entry->flags)) && + (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)) qla4xxx_relogin_device(ha, ddb_entry); /* @@ -1091,6 +1318,29 @@ static void qla4xxx_do_dpc(struct work_s } } } + + if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) { + if (!test_bit(AF_LINK_UP, &ha->flags)) { + /* ---- link down? --- */ + list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, ddb_entry); + } + } + } + + /* Check for ASYNC PDU IOCBs */ + if (adapter_up(ha) && + test_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags)) { + + list_for_each_entry_safe(apdu_iocb, apdu_iocb_tmp, + &ha->async_iocb_list, list) { + qla4xxx_async_iocbs(ha, apdu_iocb); + list_del_init(&apdu_iocb->list); + kfree(apdu_iocb); + } + clear_bit(DPC_ASYNC_MSG_PDU, &ha->dpc_flags); + } } /** @@ -1244,6 +1494,7 @@ static int __devinit qla4xxx_probe_adapt /* Initialize lists and spinlocks. */ INIT_LIST_HEAD(&ha->ddb_list); INIT_LIST_HEAD(&ha->free_srb_q); + INIT_LIST_HEAD(&ha->async_iocb_list); mutex_init(&ha->mbox_sem); @@ -1319,8 +1570,6 @@ static int __devinit qla4xxx_probe_adapt /* Start timer thread. */ qla4xxx_start_timer(ha, qla4xxx_timer, 1); - set_bit(AF_INIT_DONE, &ha->flags); - pci_set_drvdata(pdev, ha); ret = scsi_add_host(host, &pdev->dev); @@ -1333,9 +1582,27 @@ static int __devinit qla4xxx_probe_adapt qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev), ha->host_no, ha->firmware_version[0], ha->firmware_version[1], ha->patch_number, ha->build_number); + scsi_scan_host(host); + + /* Insert new entry into the list of adapters. */ + klist_add_tail(&ha->node, &qla4xxx_hostlist); + ha->instance = atomic_inc_return(&qla4xxx_hba_count) - 1; + + if (qla4xxx_ioctl_init(ha)) { + dev_info(&ha->pdev->dev, "ioctl init failed\n"); + goto remove_host; + } + + set_bit(AF_INIT_DONE, &ha->flags); + dev_info(&ha->pdev->dev, "%s: AF_INIT_DONE\n", __func__); + return 0; +remove_host: + qla4xxx_free_ddb_list(ha); + scsi_remove_host(host); + probe_failed: qla4xxx_free_adapter(ha); scsi_host_put(ha->host); @@ -1361,11 +1628,16 @@ static void __devexit qla4xxx_remove_ada while (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) ssleep(1); + klist_remove(&ha->node); + atomic_dec(&qla4xxx_hba_count); + /* remove devs from iscsi_sessions to scsi_devices */ qla4xxx_free_ddb_list(ha); scsi_remove_host(ha->host); + qla4xxx_ioctl_exit(ha); + qla4xxx_free_adapter(ha); scsi_host_put(ha->host); @@ -1429,12 +1701,14 @@ static void qla4xxx_slave_destroy(struct struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index) { struct srb *srb = NULL; - struct scsi_cmnd *cmd; - if (!(cmd = scsi_host_find_tag(ha->host, index))) + /* validate handle and remove from active array */ + if (index >= MAX_SRBS) return srb; - if (!(srb = (struct srb *)cmd->host_scribble)) + srb = ha->active_srb_array[index]; + ha->active_srb_array[index] = NULL; + if (!srb) return srb; /* update counters */ @@ -1451,16 +1725,20 @@ struct srb * qla4xxx_del_from_active_arr * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware * @ha: actual ha whose done queue will contain the comd returned by firmware. * @cmd: Scsi Command to wait on. + * @got_ref: Additional reference retrieved by caller. * * This routine waits for the command to be returned by the Firmware * for some max time. **/ static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha, - struct scsi_cmnd *cmd) + struct scsi_cmnd *cmd, int got_ref) { +#define ABORT_POLLING_PERIOD 1000 +#define ABORT_WAIT_ITER 1 + int done = 0; struct srb *rp; - uint32_t max_wait_time = EH_WAIT_CMD_TOV; + unsigned long wait_iter = ABORT_WAIT_ITER; do { /* Checking to see if its returned to OS */ @@ -1470,8 +1748,13 @@ static int qla4xxx_eh_wait_on_command(st break; } - msleep(2000); - } while (max_wait_time--); + if (got_ref && (atomic_read(&rp->ref_count) == 1)) { + done++; + break; + } + + msleep(ABORT_POLLING_PERIOD); + } while (!(--wait_iter)); return done; } @@ -1513,26 +1796,176 @@ static int qla4xxx_eh_wait_for_commands( { int cnt; int status = 0; + struct srb *sp; struct scsi_cmnd *cmd; + unsigned long flags; /* * Waiting for all commands for the designated target or dev * in the active array */ - for (cnt = 0; cnt < ha->host->can_queue; cnt++) { - cmd = scsi_host_find_tag(ha->host, cnt); - if (cmd && stgt == scsi_target(cmd->device) && - (!sdev || sdev == cmd->device)) { - if (!qla4xxx_eh_wait_on_command(ha, cmd)) { - status++; - break; + for (cnt = 1; cnt < MAX_SRBS; cnt++) { + spin_lock_irqsave(&ha->hardware_lock, flags); + sp = ha->active_srb_array[cnt]; + if (sp) { + cmd = sp->cmd; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (cmd && stgt == scsi_target(cmd->device) && + (!sdev || sdev == cmd->device)) { + if (!qla4xxx_eh_wait_on_command(ha, cmd, 0)) { + status++; + break; + } } + } else { + spin_unlock_irqrestore(&ha->hardware_lock, flags); } } return status; } /** + * qla4xxx_eh_abort - callback for abort task. + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is called by the Linux OS to abort the specified + * command. + **/ +static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) +{ + struct scsi_qla_host *ha; + struct srb *srb = NULL; + struct ddb_entry *ddb_entry; + int ret = SUCCESS; + unsigned int channel; + unsigned int id; + unsigned int lun; + unsigned long serial; + unsigned long flags = 0; + int i = 0; + int got_ref = 0; + unsigned long wait_online; + + if (cmd == NULL) { + DEBUG2(printk("ABORT - **** SCSI mid-layer passing in NULL cmd\n")); + return SUCCESS; + } + + ha = to_qla_host(cmd->device->host); + ddb_entry = cmd->device->hostdata; + channel = cmd->device->channel; + id = cmd->device->id; + lun = cmd->device->lun; + serial = cmd->serial_number; + + if (!ddb_entry) { + DEBUG2(printk("scsi%ld: ABORT - NULL ddb entry.\n", ha->host_no)); + return FAILED; + } + + if (!cmd->SCp.ptr) { + DEBUG2(printk("scsi%ld: ABORT - cmd already completed.\n", + ha->host_no)); + return ret; + } + + + + srb = (struct srb *) cmd->SCp.ptr; + + dev_info(&ha->pdev->dev, "scsi%ld:%d:%d:%d: ABORT ISSUED " + "cmd=%p, pid=%ld, ref=%d\n", ha->host_no, channel, id, lun, + cmd, serial, atomic_read(&srb->ref_count)); + + if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld:%d: %s: Unable to abort task. Adapter " + "DEAD.\n", ha->host_no, cmd->device->channel + , __func__)); + + return FAILED; + } + + /* Check active list for command */ + spin_lock_irqsave(&ha->hardware_lock, flags); + for (i = 1; i < MAX_SRBS; i++) { + srb = ha->active_srb_array[i]; + + if (srb == NULL) + continue; + + if (srb->cmd != cmd) + continue; + + DEBUG2(printk("scsi%ld:%d:%d:%d %s: aborting srb %p from RISC. " + "pid=%ld.\n", ha->host_no, channel, id, lun, + __func__, srb, serial)); + DEBUG3(qla4xxx_print_scsi_cmd(cmd)); + + /* Get a reference to the sp and drop the lock.*/ + sp_get(srb); + got_ref++; + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* + * If device is not online wait for 10 sec for device to come online, + * else return error and do not issue abort task. + */ + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + wait_online = jiffies + (DEVICE_ONLINE_TOV * HZ); + while (time_before(jiffies, wait_online)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + break; + } + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + DEBUG2(printk("scsi%ld:%d: %s: Unable to abort task." + "Device is not online.\n", ha->host_no + , cmd->device->channel, __func__)); + + return FAILED; + } + } + + if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) { + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d:%d: ABORT TASK - FAILED.\n", + ha->host_no, channel, id, lun); + } else { + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d:%d: ABORT TASK - mbx success.\n", + ha->host_no, channel, id, lun); + } + spin_lock_irqsave(&ha->hardware_lock, flags); + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait for command to complete */ + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (qla4xxx_eh_wait_on_command(ha, cmd, got_ref)) { + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d:%d: ABORT SUCCEEDED - " + "cmd returned back to OS.\n", + ha->host_no, channel, id, lun); + ret = SUCCESS; + } + + if (got_ref) + sp_put(ha, srb); + + DEBUG2(printk("scsi%ld:%d:%d:%d: ABORT cmd=%p, pid=%ld, ref=%d, " + "ret=%x\n", ha->host_no, channel, id, lun, cmd, + serial, atomic_read(&srb->ref_count), ret)); + + return ret; +} + + + + +/** * qla4xxx_eh_device_reset - callback for target reset. * @cmd: Pointer to Linux's SCSI command structure * @@ -1541,16 +1974,34 @@ static int qla4xxx_eh_wait_for_commands( **/ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) { - struct scsi_qla_host *ha = to_qla_host(cmd->device->host); - struct ddb_entry *ddb_entry = cmd->device->hostdata; + struct scsi_qla_host *ha; + struct ddb_entry *ddb_entry; int ret = FAILED, stat; + struct Scsi_Host *h; + unsigned int b, t, l; - if (!ddb_entry) + if (cmd == NULL) { + DEBUG2(printk("%s: **** SCSI mid-layer passing in NULL cmd" + "DEVICE RESET - cmd already completed.\n", + __func__)); + return SUCCESS; + } + + h = cmd->device->host; + b = cmd->device->channel; + t = cmd->device->id; + l = cmd->device->lun; + ha = to_qla_host(h); + ddb_entry = cmd->device->hostdata; + + if (!ddb_entry) { + DEBUG2(printk("scsi%ld: DEVICE RESET - NULL ddb entry.\n" + , ha->host_no)); return ret; + } - dev_info(&ha->pdev->dev, - "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no, - cmd->device->channel, cmd->device->id, cmd->device->lun); + dev_info(&ha->pdev->dev, "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n" + , ha->host_no, b, t, l); DEBUG2(printk(KERN_INFO "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x," @@ -1558,8 +2009,13 @@ static int qla4xxx_eh_device_reset(struc cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); - /* FIXME: wait for hba to go online */ - stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun); + /* wait for hba to go online */ + if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { + dev_info(&ha->pdev->dev, "%s: DEVICE RESET." + "Adapter Offline.\n", __func__); + return FAILED; + } + stat = qla4xxx_reset_lun(ha, ddb_entry, l); if (stat != QLA_SUCCESS) { dev_info(&ha->pdev->dev, "DEVICE RESET FAILED. %d\n", stat); goto eh_dev_reset_done; @@ -1574,14 +2030,13 @@ static int qla4xxx_eh_device_reset(struc } /* Send marker. */ - if (qla4xxx_send_marker_iocb(ha, ddb_entry, cmd->device->lun, - MM_LUN_RESET) != QLA_SUCCESS) + if (qla4xxx_send_marker_iocb(ha, ddb_entry, l, MM_LUN_RESET) + != QLA_SUCCESS) goto eh_dev_reset_done; dev_info(&ha->pdev->dev, "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", - ha->host_no, cmd->device->channel, cmd->device->id, - cmd->device->lun); + ha->host_no, b, t, l); ret = SUCCESS; @@ -1655,6 +2110,13 @@ static int qla4xxx_eh_host_reset(struct int return_status = FAILED; struct scsi_qla_host *ha; + if (cmd == NULL) { + DEBUG2(printk("%s: **** SCSI mid-layer passing in NULL cmd" + "HOST RESET - cmd already completed.\n", + __func__)); + return SUCCESS; + } + ha = (struct scsi_qla_host *) cmd->device->host->hostdata; dev_info(&ha->pdev->dev, @@ -1717,6 +2179,9 @@ static int __init qla4xxx_module_init(vo { int ret; + atomic_set(&qla4xxx_hba_count, 0); + klist_init(&qla4xxx_hostlist, NULL, NULL); + /* Allocate cache for SRBs. */ srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0, SLAB_HWCACHE_ALIGN, NULL); --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,5 +5,5 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.01.00-k9" +#define QLA4XXX_DRIVER_VERSION "5.01.00.00.11.01-k10"