2810 lines
90 KiB
Diff
2810 lines
90 KiB
Diff
|
From: Ravi Anand <ravi.anand@qlogic.com>
|
||
|
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 <ravi.anand@qlogic.com>
|
||
|
Acked-by: Hannes Reinecke <hare@suse.de>
|
||
|
|
||
|
---
|
||
|
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 <scsi/iscsi_proto.h>
|
||
|
|
||
|
/**
|
||
|
* 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 <scsi/scsi_tcq.h>
|
||
|
#include <scsi/scsicam.h>
|
||
|
+#include <scsi/iscsi_proto.h>
|
||
|
+#include <scsi/scsi_eh.h>
|
||
|
|
||
|
#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"
|
||
|
|